堆排序
堆排序:
要知道堆排序,首先要了解一下二叉树的模型。
下图就是一颗二叉树,具体的情况我后续会分享的。
那么堆排序中有两种情况(看上图理解):
大根堆: 就是说父节点要比左右孩子都要大。
小根堆: 就是说父节点要比左右孩子都要小。
那么要实现堆排序,必须要做两件事情:
第一:构建大根堆。
首先上图:
首先这是一个无序的堆,那么我们怎样才能构建大根堆呢?
第一步: 首先我们发现,这个堆中有2个父节点(2,,3);
第二步: 比较2这个父节点的两个孩子(4,5),发现5大。
第三步: 然后将较大的右孩子(5)跟父节点(2)进行交换,至此3的左孩子堆构建完毕,
如图:
第四步: 比较第二个父节点(3)下面的左右孩子(5,1),发现左孩子5大。
第五步: 然后父节点(3)与左孩子(5)进行交换,注意,交换后,堆可能会遭到破坏,
必须按照以上的步骤一,步骤二,步骤三进行重新构造堆。
最后构造的堆如下:
第二:输出大根堆。
至此,我们把大根堆构造出来了,那怎么输出呢?我们做大根堆的目的就是要找出最大值,
那么我们将堆顶(5)与堆尾(2)进行交换,然后将(5)剔除根堆,由于堆顶现在是(2),
所以破坏了根堆,必须重新构造,构造完之后又会出现最大值,再次交换和剔除,最后也就是我们
要的效果了。
堆的定义:n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
说明:它是不稳定的排序方法。(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前 和排序后他们的相对位置不发生变化)
1 #include<iostream> 2 #include <ctime> //使用了time() 函数 3 #include <cstdlib> //使用了srand()函数 4 5 const int N=3005; 6 int a[N]; 7 using namespace std; 8 //堆排序 9 //整理节点time:O(lgn) 10 11 void AdjustHeap(int *a,int parent,int length) 12 { 13 //temp保存当前父节点 14 int temp=a[parent]; 15 //得到左孩子(这可是二叉树的定义,大家看图也可知道) 16 int child=2*parent; 17 while(child<=length) 18 { 19 //如果parent有右孩子,则要判断左孩子是否小于右孩子 20 if (child + 1 < length && a[child] < a[child + 1]) child++;//此方法用了点小技巧,大家认真思考一下 21 if(temp>=a[child]) break; //父节点大于子节点,就不用了,建堆完成 22 //另一种情况则是小于 23 a[parent]=a[child]; //大的子节点赋值给父节点 24 parent=child; //子节点做为父节点 25 child=2*parent; //步长增,进入下一节点 26 } 27 a[parent]=temp; //完成后,将temp赋值给此节点即可完成建堆 28 } 29 30 //堆排序time:O(nlgn) 31 void HeapSort(int*a,int n) 32 { 33 int i; 34 for(i=n/2;i>=1;i--) //从最后一个父节点一至到第一个 35 { 36 AdjustHeap(a,i,n); //对每个父节点进行一次建堆 37 } 38 //最后输出堆元素 39 for(int i=n;i>1;i--) 40 { 41 //堆顶与当前的第i个元素进行对调 42 int temp=a[1]; 43 a[1]=a[i]; 44 a[i]=temp; 45 //对调后,再对1...i-1的元素进行建堆,此时只需要从第1个元素开始到最底层即可 46 AdjustHeap(a,1,i-1); 47 } 48 } 49 50 int main() 51 { 52 //随机产生3000个数存入数组a中 53 srand(int(time(0))); //利用时间函数time(),产生每次不同的随机数种子 54 for(int i=1;i<=3000;i++) a[i]=rand(); //随机产生3000个数存于数组a中 (从1开始) 55 clock_t start = clock(); // 计时开始 56 HeapSort(a, 3000); //对数组a进行排序(从1开始) 57 clock_t end = clock(); //计时结束 58 for(int i=1;i<=20;i++) cout<<a[i]<<' '; //输出前20个数据(已从小到大排序) 59 cout<<endl<<"堆排排序耗时为:"<<end-start<<"ms"<<endl; //输出排序所用的时间:结束时间-开始时间(单位为毫秒) 60 return 0; 61 }