网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
注意:
1.注意循环结束条件,和if语句里的边界问题child + 1 < n
2.注意完全二叉树父子关系公式
#include <stdio.h>
//交换
void swap(int\* x, int\* y)
{
int t = 0;
t = \*x;
\*x = \*y;
\*y = t;
}
//向下调整
void AdjustDown(int\* a, int n, int root)
{
int parent = root;
int child = parent \* 2 + 1;
while (child < n)
{
//每次调整都需要从左右两边选出孩子最大的那个
//假设坐孩子较大,选出左右孩子大的那个
if (child + 1 < n && a[child + 1] > a[child])
{
++child;
}
//开始调整。
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent \* 2 + 1;
}
//不满足就跳出,开始下次for循环调整。
else
{
break;
}
}
}
void HeapSort(int\* a, int n)
{
//向下调整建堆
int i = 0;
for (i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, n, i);
}
}
int main()
{
int arr[] = { 4,2,7,8,5,1,0,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
HeapSort(arr, sz);
return 0;
}
2.向下调整排序
调整思路
1.从堆底依次 和 堆顶的数据进行交换。
2.对交换后的 堆顶的值 进行向下调整。向下调整时请无视交换到堆底那个最大的值。
3.继续循环第一步和第二步,直到到正数第二个数结束。
排序整体代码
void swap(int\* x, int\* y)
{
int t = 0;
t = \*x;
\*x = \*y;
\*y = t;
}
void AdjustDown(int\* a, int n, int root)
{
int parent = root;
int child = parent \* 2 + 1;
while (child < n)
{
//每次调整都需要从左右两边选出孩子最大的那个
//假设坐孩子较大,选出左右孩子大的那个
if (child + 1 < n && a[child + 1] > a[child])
{
++child;
}
//开始调整。
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent \* 2 + 1;
}
//不满足就跳出,开始下次for循环调整。
else
{
break;
}
}
}
void HeapSort(int\* a, int n)
{
//向下调整建堆
int i = 0;
for (i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, n, i);
}
//向下调整排序
int end = 0;
for (end = n-1; end > 0; end--)
{
swap(&a[0], &a[end]);
//向下调整时无视最大的那个值,所以end是n-1。
AdjustDown(a, end, 0);
}
}
int main()
{
int arr[] = { 4,2,7,8,5,1,0,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
HeapSort(arr, sz);
return 0;
}
3.时间复杂度(难点)
向下建堆O(N)
//向下调整建堆
int i = 0;
for (i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, n, i);
}
很多人的误区在于他的时间复杂度是N*Log2N。这是错误的。
时间复杂度的计算是看思想,而不是看循环猜测。
当是满二叉树,在最坏的情况下,除了最后一层,上面所有层都需要进行向下调整。
最坏情况下的调整次数 = 每层数据个数 * 向下调整次数
第一层向下调整次数是h-1,节点个数是21-1
第二层向下调整次数是h-2, 节点个数是22-1
第h-1层向下调整次数是1,节点个数是2h-1-1
所以总的调整次数为n:
n = 20*(h-1) + 21 *(h-2)+… + 2h-1-1 *(1)
根据高中错位相减得到
n = 1−h+21+22+…+2h−2+2h−1
由等比数列前n项和得到
n = 2h−h−1
由二叉树性质N=2h−1和 h = log2(N+1) 得到
n=N−log2(N+1)
大O渐进表示法为n= O(N)
向下调整(N*LogN)
需要向下调整n-1次。每次需要调整的高度为LogN,N为节点的个数,因为节点个数每次少一个。
所以n-1次调整总次数 = log2+log3+…+log(n-1)+log(n)≈log(n!)
由数学知识得log(n!)和nlog(n)是同阶函数。
所以向下调整排序时间复杂度为N*LogN
所以堆排序时间复杂度为:N + N*LogN
大O渐进表示法为:O(N*LogN)
总结:堆排序时间复杂度 O(N*LogN)
二、向上调整堆排序
向上调整排序和向下调整排序的唯一不同在于建堆的不同,导致二者的建堆的时间复杂度略微不同。
1.向上调整建堆
向上调整建堆时间复杂度为N*LogN.
具体原因还需要经过残酷的数学计算。孩子不会啊。但是经过网上查阅资料我又找到了计算方法。如图。
根据二叉树的性质:h = Log2(N+1)
可以将T(h) = 2h * (h-2) + 2换为:
所以总体来说就是向上调整的建堆时间复杂度为O(N * LogN).
2.建堆代码
思路:从第二个元素开始,只关注前两个元素建堆,然后再依次增加元素建堆,使它一直为堆。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
(img-lJNX2YfY-1715564303133)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新