堆排序是最重要的排序算法之一,在平时的开发以及面试中经常会用到。堆的特点是:
1,有一颗完全二叉树构成,如图1;
2,可分为最大堆和最小堆。最大堆的意思就是:任何根节点的数据不小于左右孩子节点的数据;反之,最小堆的意思就是任何节点的数据不大于左右孩子节点的数据;
3,堆排序的算法复杂度为O(NlgN),比冒泡和插入快,究其原因在于堆只维护局部最大或最小。
4,堆的存储用数组实现,按层存储,如图1;
存储在数组里:
图 1
下面分四部分讲解堆,分别为:堆的插入,删除,以及堆排序,最后是堆化的优先队列。
一,堆的插入
堆的插入只能在最后一个位置插入,如图 2。只能在黑色圈的地方插入。插入以后,必须进行更新,以保持堆的性质,即根数据最大或是最小。更新的思路:从插入节点到根节点,依次比较更新,直到满足条件(本列子就是:根数据小于左右孩子)。
大致原理如图解:
关键代码如下:
注意的是:左孩子和右孩子的顺序分别为2i+1,2i+2。结合上面的流程图,插入的过程为:从下往上依次比较,重新调整堆的结构。
- void MinHeapFixup(int* a, int i)
- {
- int j, temp;
- temp = a[i];
- j = (i - 1) / 2; //父结点
- while (j >= 0 && i != 0)
- {
- if (a[j] <= temp)
- break;
- a[i] = a[j]; //把较大的子结点往下移动,替换它的子结点
- i = j;
- j = (i - 1) / 2;
- }
- a[i] = temp;
- }
- void MinHeapAddNumber(int* a, int n, int nNum)
- {
- a[n] = nNum;
- MinHeapFixup(a, n);
- }
记住只能删除最顶部的元素,再把最低端的元素放到最顶端,从上往下依次更新整个堆。示意图如下:
- void MinHeapFixdown(int a[], int i, int n)
- {
- int j, temp;
- temp = a[i];
- j = 2 * i + 1;
- while (j < n)
- {
- if (j + 1 < n && a[j + 1] < a[j]) //如果存在有孩子,比较左右孩子的大小
- j++;
- if (a[j] >= temp)
- break;
- a[i] = a[j];
- i = j;
- j = 2 * i + 1;
- }
- a[i] = temp;
- }
- void MinHeapDeleteNumber(int a[], int n)
- {
- std::swap(a[0], a[n - 1]);
- MinHeapFixdown(a, 0, n - 1);
- }
总结:删除,只能删除头结点,然后从上往下依次调整堆;插入,只能在末端插入,然后从下往上依次调整堆。
- void MakeMinHeap(int* a, int n)
- {
- for (int i = n / 2 - 1; i >= 0; i--)
- MinHeapFixdown(a, i, n);
- }
- int main()
- {
- int a[8]={9,7,5,6,8,4,10};
- cout<<"原始数组里的数据为:";
- for(int i=0;i<7;++i)
- cout<<a[i]<<" ";
- cout<<endl<<"堆化后的数组为:";
- MakeMinHeap(a, 7);
- for(int i=0;i<7;++i)
- cout<<a[i]<<" ";
- cout<<endl<<"增加一个数据3后的数组为:";
- MinHeapAddNumber(a,7,3);
- for(int i=0;i<8;++i)
- cout<<a[i]<<" ";
- MinHeapDeleteNumber(a,8);
- cout<<endl<<"删除顶点后的数据为:";
- for(int i=0;i<7;++i)
- cout<<a[i]<<" ";
- return 0;
- }
我们自然要问:维护一个堆结构的意义何在?
- int b[8];
- for(int i=0;i<8;++i)
- {
- b[i]=a[0];
- MinHeapDeleteNumber(a,8-i);
- }
- for(int i=0;i<8;++i)
- cout<<b[i]<<" ";