网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
可以参考下面的图示进行理解
💬 代码演示
**1)**首先检查堆是否满,如果不满,设 i 等于新堆的大小(n+1)。
**2)**使用 while 循环从最大堆的新叶子结点开始,沿着根结点的路径走。
**3)**一直到根结点或者位置 i ,使其父结点 i/2 的值不小于要插入的值。
void insert_max_heap(element item ,int *n){ //将数插入当前大小为 n 的最大堆中 if(HEAP_FULL(*n)){ return; } int i = ++(*n); while((i != 1) && (item.key>heap[i/2].key)){ heap[i] = heap[i/2]; i/=2; } heap[i]=item; }
🍁最大堆的删除
🔺算法分析
最大堆的删除操作,总是从堆的根结点删除元素。同样根元素被删除之后为了能够保证该树还是一个完全二叉树,我们需要来移动完全二叉树的最后一个结点,让其继续符合完全二叉树的定义,从这里可以看作是最大堆最后一个结点的下沉。
可以参考下面的图示进行理解
现在看来该二叉树虽然是一个完全二叉树,但是它并不符合最大堆的相关定义。我们要在删除完成之后,该完全二叉树依然是最大堆。因此就需要我们来做一些相关的操作。
💬 代码演示
- **1)**首先删除最大堆的根结点
- **2)**根结点被删除之后,为了能够保证该树还是一个完全二叉树,我们应该移动完全二叉树的最后一个结点,让其继续符合完全二叉树的定义。这里可以看作是最大堆最后一个结点的下沉
- **3)**该二叉树虽然是一个完全二叉树,但是它并不符合最大堆的相关定义,我们在左右结点中选择大的一方和下沉的结点比较,令大的一方上浮。
- **4)**重复步骤 3 ,直到到达叶子结点。
element delete_max_heap(int *n){ int parent, child; element temp, item; temp = heap[--*n]; item = heap[1]; parent = 1,child=2; for(;child <= *n; child = child * 2) { if( (child < *n) && heap[child].key < heap[child+1].key) {// 这一步是为了看当前结点是左子结点大还是右子结点大,然后选择较大的那个子结点 child++; } if(temp.key >= heap[child].key) { break; } heap[parent] = heap[child];//这就是上图中第二步和第三步中黄色部分操作 arent = child;// 这其实就是一个递归操作,让parent指向当前子树的根结点 } heap[parent] = temp; return item; }
🌺堆排序
🔺算法分析
通过上文介绍的最大堆的插入算法和删除算法可以直接得到一个时间复杂度为 O(nlogn) 的排序算法。
- **1)**将n个数插入到一个初始为空的堆中,构建出最大堆。
- **2)**整个序列的最大值就是堆顶的根结点。
- **3)**将其与末尾元素进行交换,此时末尾就为最大值
- **4)**将剩余元素重新构造成一个最大堆,这样会得到剩余元素的次小值。
- **5)**如此反复执行,便能得到一个有序序列了。
可以参考下面的图示进行理解
根据二叉树的结构与性质,我们可以推理出来最后一个非叶子节点的索引就是 【arr.len / 2 -1】,我们从最后一个非叶子结点开始,从左至右,从下至上进行调整。
这里对非叶子结点进行判断,交换【4,5】
对下一个非叶子结点进行判断,交换【5,2】
交换导致了子根【2,3,4】结构混乱,进行调整,交换【2,4】
我们构造出来一个最大堆,下面来进行排序,先将顶点元素5与末尾元素2交换位置,此时末尾数字为最大值。
排除已经确定的最大元素,将剩下元素重新构建最大堆
再将堆顶元素4与末尾元素2进行交换,再重新构建最大堆
重复上述操作,得到最终排序结果
💬 代码演示
void heapSort(int[] arr) { //构造最大堆 heapInsert(arr); int size = arr.length; while (size > 1) { //固定最大值 swap(arr, 0, size - 1); size--; //构造大根堆 heapify(arr, 0, size); } } //构造最大堆(通过新插入的数上升) void heapInsert(int[] arr) { for (int i = 0; i < arr.length; i++) { //当前插入的索引 int currentIndex = i; //父结点索引 int fatherIndex = (currentIndex - 1) / 2; //如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点 //然后继续和上面的父结点值比较,直到不大于父结点,则退出循环 while (arr[currentIndex] > arr[fatherIndex]) { //交换当前结点与父结点的值 swap(arr, currentIndex, fatherIndex); //将当前索引指向父索引 currentIndex = fatherIndex; //重新计算当前索引的父索引 fatherIndex = (currentIndex - 1) / 2; } } } //将剩余的数构造成最大堆(通过顶端的数下降) void heapify(int[] arr, int index, int size) { int left = 2 * index + 1; int right = 2 * index + 2; while (left < size) { int largestIndex; //判断孩子中较大的值的索引(要确保右孩子在size范围之内) if (arr[left] < arr[right] && right < size) { largestIndex = right; } else { largestIndex = left; } //比较父结点的值与孩子中较大的值,并确定最大值的索引 if (arr[index] > arr[largestIndex]) { largestIndex = index; } //如果父结点索引是最大值的索引,那已经是最大堆了,则退出循环 if (index == largestIndex) { break; } //父结点不是最大值,与孩子中较大的值交换
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
…(img-bU9xJJaS-1715816579731)]
[外链图片转存中…(img-GeNrVUHw-1715816579731)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新