排序---堆排序算法
做为选择排序的改进版,堆排序能够把每一趟元素的比较结果保存下来,以便咱们在选择最小/大元素时对已经比较过的元素作出相应的调整。数组
做为选择排序的改进版,堆排序能够把每一趟元素的比较结果保存下来,以便咱们在选择最小/大元素时对已经比较过的元素作出相应的调整。性能
堆排序是一种树形选择排序,在排序过程当中能够把元素当作是一颗彻底二叉树,每一个节点都大(小)于它的两个子节点,当每一个节点都大于等于它的两个子节点时,就称为大顶堆,也叫堆有序; 当每一个节点都小于等于它的两个子节点时,就称为小顶堆。3d
下面是咱们要保存在数组中的堆的形式code
1.将长度为n的待排序的数组进行堆有序化构形成一个大顶堆 2.将根节点与尾节点交换并输出此时的尾节点 3.将剩余的n -1个节点从新进行堆有序化 4.重复步骤2,步骤3直至构形成一个有序序列
{5, 2, 6, 0, 3, 9, 1, 7, 4, 8}
在构造有序堆时,咱们开始只须要扫描一半的元素(n/2-1 ~ 0)便可,为何?blog
由于(n/2-1)~0的节点才有子节点,如图1,n=8,(n/2-1) = 3 即3 2 1 0这个四个节点才有子节点排序
第一次找到[n/2]处,进行构造:for循环
咱们比较父节点,左右孩子结点的大小,将最大的做为堆顶性能分析
第二次,咱们对上次找到位置-1便可,找到结点0,对其左右孩子比较,构造这三个结点的最大堆二叉树
第三次,咱们找到结点6,要对其进行构造,结果以下
第四次(重点),咱们不止要构造双亲和左右孩子,咱们还要比较其孩子结点为根的堆是否正确,否则咱们须要进行调整
咱们发现将8,7,2三个结点变为了最大堆,可是其中2,3子树再也不是一个最大堆,咱们须要对其修改
第五次:选取结点9进行构造
发现以结点5为根的子树不是最大堆,咱们须要进行调整
void swap(int K[], int i, int j) { int temp = K[i]; K[i] = K[j]; K[j] = temp; } //大顶堆的构造,传入的i是父节点 void HeapAdjust(int k[],int p,int n) { int i,temp; temp = k[p]; for (i = 2 * p; i <= n;i*=2) //逐渐去找左右孩子结点 { //找到两个孩子结点中最大的 if (i < n&&k[i] < k[i + 1]) i++; //父节点和孩子最大的进行判断,调整,变为最大堆 if (temp >= k[i]) break; //将父节点数据变为最大的,将原来的数据仍是放在temp中, k[p] = k[i]; //如果孩子结点的数据更大,咱们会将数据上移,为他插入的点提供位置 p = i; } //当咱们在for循环中找到了p子树中,知足条件的点,咱们就加入数据到该点p,注意:p点原来数据已经被上移动了 //若没有找到,就是至关于对其值不变 //插入 k[p] = temp; } //大顶堆排序 void HeapSort(int k[], int n) { int i; //首先将无序数列转换为大顶堆 for (i = n / 2; i > 0;i--) //注意因为是彻底二叉树,因此咱们从一半向前构造,传入父节点 HeapAdjust(k, i, n); //上面大顶堆已经构造完成,咱们如今须要排序,每次将最大的元素放入最后 //而后将剩余元素从新构造大顶堆,将最大元素放在剩余最后 for (i = n; i >1;i--) { swap(k, 1, i); HeapAdjust(k, 1, i - 1); } } int main() { int i; int a[11] = {-1, 5, 2, 6, 0, 3, 9, 1, 7, 4, 8 }; HeapSort(a, 10); for (i = 1; i <= 10; i++) printf("%d ", a[i]); system("pause"); return 0; }
运行时间主要消耗在构造堆和重建堆时的反复筛选上。 构造堆的时间复杂度为O(n) 重建堆时时间复杂度为O(nlogn)。 因此整体就是O(nlogn)。 不适合排序序列个数较少的状况