当前位置:网站首页>堆排序(heapsort)与比较器
堆排序(heapsort)与比较器
2022-06-13 12:14:00 【StepByStep~】
堆结构:
(1)逻辑概念上:完全二叉树(满二叉树或者是从左往右依次变满的二叉树)
(2)存储表示:数组从0出发的连续一段表示一颗完全二叉树,使用size描述该树有多少结点,在i位置上的结点,其左孩子下标为(2*i+1),右孩子下标为(2*i+2),父节点为(i-1)/2.
(3)分类:
大根堆:堆顶为整个堆最大的元素,同样的每个子树也满足子树的最大值是头结点的值
小根堆:堆顶为整个堆最小的元素,同样的每个子树也满足子树的最小值是头结点的值
两个重要过程:
heapinsert:向上跟父节点比较调整的过程
/*
从堆底向上升的过程
某个数在arr的index位置,通过该方法找到它应该在的大根堆的位置
方法:依次向上找父节点,直到父节点比该数大
*/
public static void heapinsert(int[] arr, int index){
while(arr[index] > arr[(index - 1)/2]){//包含index在0位置的情况
swap(arr, index, (index-1)/2);
index = (index - 1) / 2;
}
}
heapify:向下跟子节点比较调整的过程
/*
某个数在index位置,是否能往下沉找到应该在的位置
大根堆方法:与左右孩子的最大值交换,在依次和下面的孩子比较
*/
public static void heapify(int[] arr, int index, int heapSize){
int left = index * 2 + 1; //左孩子的下标
while(left < heapSize){ //如果有孩子
//如果有右孩子,找出左右孩子中的较大的下标
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//将较大孩子和自己比较,取较大的
largest = arr[index] >= arr[largest] ? index : largest;
if(index == largest) break;//如果自己比孩子都大,则停止下沉
swap(arr, index, largest);//否则交换,继续比较
index = largest;
left = index * 2 + 1;
}
}
堆排序(由小到大——大根堆)时复:O(N*logN),空复:O(1)
先形成堆,再从堆顶弹出元素,从后往前放入数组。
形成堆时:
法1:从数组的前面开始,依次向堆中添加元素建堆
法2:假设所有元素已经构成了堆形的树,然后从后往前下沉,先调整好局部小堆,在调整大堆。最后一层约有N/2个节点,每个结点处理1次(看一眼);倒数第二层约有N/4个节点,每个结点处理2次(看1眼,可能向下移动1步)....所有的相加为总的操作次数(式1)。将该式左右都乘以2(式2),将两式子错位相减,可以得到最后的时间复杂度为O(N)。
//由小到大排序
public static void heapSortMax(int[] arr){
if(arr == null || arr.length < 2) return;
int heapSize = 0;
//让数组构成大根堆,法1:O(N*logN)
// for(int i = 0; i < arr.length; i++){//构成一个大根堆 O(N)
// heapinsert(arr, i); //O(logN)
// }
//让数组构成大根堆,法2:O(N)
for(int i = arr.length - 1; i >= 0; i--){
heapify(arr, i, arr.length);//这里的第三个参数是堆的大小,由于是倒序依次下沉的,所以取最大值
}
//大根堆堆顶元素依次弹出
heapSize = arr.length;
swap(arr, 0, --heapSize);//如果将先swap后heapify都写到循环中,就多执行了一次没有意义的heapify
while (heapSize > 0 ){//O(N)
heapify(arr, 0, heapSize);//需要 len-1 次heapify即可。O(logN)
swap(arr, 0, --heapSize);//O(1)
}
}
由大到小(小根堆),Java中系统自带的优先级队列默认是小根堆
//小根堆
public static void heapnsert_min(int[] arr, int index){
while (arr[index] < arr[(index - 1) / 2]){
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
public static void heapify_min(int[] arr, int index, int heapSize){
int left = index * 2 + 1;
while (left < heapSize){
//注意这里只能是满足条件才能取右孩子,否则如果是else取右孩子,还存在右孩子不存在的情况,就会越界
int less = (left+1 < heapSize) && arr[left + 1] < arr[left] ? left + 1 : left;
less = arr[less] < arr[index] ? less : index;
if(less == index) break;
swap(arr, less, index);
index = less;
left = index * 2 + 1;
}
}
//从大到小排序
public static void heapSortMin(int[] arr){
//建小根堆
for(int i = arr.length - 1; i >= 0; i--){
heapify_min(arr, i, arr.length);
}
//从堆顶依次弹出
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while (heapSize > 0){
heapify_min(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
例题:
/** * 堆排序扩展题目 *问题描述: 已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话, * 每个元素移动的距离都不超过k,并且k相对于数组来说比较小, * 请选择一个合适的排序算法针对这个数据进行排序。 * 使用结构:优先级队列(小根堆) * 时复:O(N*logK) * @param arr * @param k */
解决:使用默认的优先级队列 PriorityQueue将前(k+1)个元素建立一个小根堆,堆顶的元素一定放在arr[0]的位置,依次在向后添加新的元素构建新的小根堆,将弹出元素放入arr[1]的位置,直到整个数组的后(k+1)个元素都在堆中,然后从小到大依次弹出放入数组中。时间复杂度为O(N*logK)
如果考虑到堆的扩容问题,是成倍扩容,即扩容logN次,每次扩容完都要将原来的元素拷贝,即单次扩容代价是O(N),所以总的操作次数是O(N*logN),每个元素均摊的扩容代价为O[(N*logN)/N] = O(logN)
public static void sortedArrDistanceLessK(int[] arr, int k){
PriorityQueue<Integer> heap = new PriorityQueue<>();
int index = 0;
for(; index < Math.min(k, arr.length); index++){//把前(k+1)个放入小根堆
heap.add(arr[index]);
}
int i = 0;
for(; index < arr.length; index++, i++){//弹出一个,添加一个,直到添加完
arr[i] = heap.poll();//堆顶弹出放入k范围下的第一个位置
heap.add(arr[index]);//k范围整体后移
}
while (!heap.isEmpty()){//堆中剩余元素按顺序弹出
arr[i++] = heap.poll();
}
}
注意:
系统给的优先级队列(堆结构)与自己手写堆的区别:系统堆结构在初始建堆时结构元素大小已经固定,当如果想改变某一个数重新调整成堆时,可能需要重新扫描所有元素,heapify和heapinsert,有很高的代价,这时就需要手写堆,直接判断该变化的数是需要heapify还是heapinsert。如果只是简单的需要添加和弹出数,可以使用系统堆,涉及到更改堆元素,需要手写
比较器:
(1)实质:重载比较运算符
(2)可以很好的应用在比较特殊的排序规则上
(3)可以很好的应用在根据特殊标准排序的结构上
实现方法:自己编写一个比较器类实现Comparator<要比较的类>接口,重新compare
!!!规则:
在Arrays.sort()、 PriorityQueue、TreeSet中都可以传入比较器对象,按照给定的要求建立。在堆中,比较器中排在前面的数应该放在上面,这样默认的小根堆也可以改成大根堆
边栏推荐
- 小程序配置分享的一种实践
- selenium实操-自动化登录
- Wallys/Network_Card/DR-NAS26/AR9223/2x2 MIMO
- [tcapulusdb knowledge base] Introduction to tcapulusdb tcapsvrmgr tool (I)
- 机器学习(四)—PCA(主成分分析)理论与代码详解
- 机器学习(一)—线性回归理论与代码详解
- Tamigou equity project sharing: transfer of 1637900 shares of Beijing Huadahua New Technology Co., Ltd
- Branch prediction of CPU
- Intelligent customer service system framework rasa
- [tcaplusdb knowledge base] Introduction to tcaplusdb tcaplusadmin tool
猜你喜欢
陈宏智:字节跳动自研万亿级图数据库ByteGraph及其应用与挑战
基於STM32F103+AS608指紋模塊+4X4矩陣按鍵+SIM900A發短信——智能門禁卡系統
Envoyer un SMS - système de carte d'accès intelligent basé sur stm32f103 + as608 module d'empreintes digitales + clé matricielle 4x4 + sim900a
Projet de développement web, développement d'une page Web
How to use dataX to update the data in the downstream Oracle database with the update semantics?
14. Notes on using border decorator of WPF
Wallys/Network_Card/DR-NAS26/AR9223/2x2 MIMO
Text error correction -- crisp model
Query the current number of computer CPU cores
Kubernetes problem sorting
随机推荐
Interview questions MySQL database
如何基于Ceph设计与构建一套软件定义存储系统
杜邦分析法剖析:居然之家新零售集团股份有限公司企业财务分析
基于STM32F103+AS608指纹模块+4X4矩阵按键+SIM900A发短信——智能门禁卡系统
高光谱真彩色图像合成原理及具体操作过程
8、DeepFM介绍
我们的B端SaaS为什么生存得如此艰难
Details of fitfi sports money making chain game system development mode
想发自己的NFT,你要先搞清楚这6个问题
如何基于Swift开源技术构建云存储集群
Query the current number of computer CPU cores
UE4, ue5 phantom engine, command console command, parameter set
M1 experience win11
浅谈常见的web攻击以及如何防范
Based on STM32F103 - as608 fingerprint module + serial port printing
Projet de développement web, développement d'une page Web
SMS sending + serial port printing based on stm32f103-sim900a
11. PCA introduction
对话 CTO:如何避免陷入管理困境 | Q推荐
Wallys/Network_ Card/DR-NAS26/AR9223/2x2 MIMO