1. 堆排序
- 堆排序是选择排序的一种。【
选择排序:
每次在待排序元素中选择最大/最小的一个元素,并加入到有序子序列中】
1.1 什么是堆
若n个关键字序列L[1…n]满足下面某一条规则,则称为
堆【Heap】
:
- 若满足:L[i] >= L[zi] && L[i] >= L[2i+1],则称为
大根堆【大顶堆】
- 若满足:L[i] <= L[zi] && L[i] <= L[2i+1],则称为
小根堆【小顶堆】
1.2 二叉树的顺序存储
- i 的左孩子【2i】
- i 的右孩子【2i+1】
- i 的父节点【i/2 向下取整】
- 若
完全二叉树
中共有n个节点:
- 判断 i 是否有左孩子【2i <= n】
- 判断 i 是否有右孩子【2i+1 <= n】
- 判断 i 是叶子节点/分支节点 <==>判断 i 是否有左孩子【i > n/2向下取整】
1.3 如何建立大根堆
大根堆
:根 >= 左、右- 思路:把所有非叶子结点都检查一遍,看是否都满足大根堆的要求,不符合则进行相应调整。
- 在顺序存储二叉树中,非叶子结点编号 i <= n/2向下取整
/**
* @author: zipeng Li
* 2021/5/20 10:17
*/
public class Heap {
public static void main(String[] args) {
int[] nums = new int[]{0,53,17,78,9,45,65,87,32};
Heap heap = new Heap();
heap.BuildMaxHeap(nums);
for(int item: nums){
System.out.print(item + " ");
}
}
/**
* 在数组 nums 中建立大根堆
* @param nums
*/
void BuildMaxHeap(int[] nums){
for(int i=nums.length/2;i>0;i--){
heapAdjust(nums, i);
}
}
/**
* 调整大根堆中 下标为 k 的节点
* @param nums
* @param k
*/
void heapAdjust(int[] nums, int k){
// 开始调整
for(int i=2*k; i<nums.length; i*=2){
// 找到待调整节点的作用子节点的最大值的下标
if(i+1 < nums.length){
i = nums[i] < nums[i+1] ? i+1 : i;
}
// 与待调整节点比较
if(nums[i] > nums[k]){
swap(nums, i, k);
}
// 更新待调整节点
k = i;
}
}
void swap(int[] nums, int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
1.4 基于大根堆进行排序
- 每次将堆顶元素与数组中待排序部分最后一个元素交换,后进行堆调整【在上述代码基础上,添加如下方法】
/**
* 基于大根堆的升序排序
* @param nums 大根堆
* @param index 待排序部分最大下标
*/
void maxHeapSort(int[] nums, int index){
while(index > 1){
// 交换
swap(nums,1, index);
// 下标前移
index--;
// 调整
heapAdjust(nums, 1, index);
}
}
【测试】
public static void main(String[] args) {
int[] nums = new int[]{0,53,17,78,9,45,65,87,32};
Heap heap = new Heap();
heap.BuildMaxHeap(nums);
System.out.println("构造大根堆:");
for(int item: nums){
System.out.print(item + " ");
}
heap.maxHeapSort(nums, nums.length-1);
System.out.println("\n大根堆排序:");
for(int item: nums){
System.out.print(item + " ");
}
}
1.5 算法效率分析
- 时间复杂度:O(nlogn)
- 是不稳定排序
1.6 小根堆排序
时间复杂:O(nlogn)
不稳定排序
/**
* @author: zipeng Li
* 2021/5/20 11:27
*/
public class MinHeap {
public static void main(String[] args) {
int[] nums = new int[]{0,53,17,78,9,45,65,87,32};
MinHeap minHeap = new MinHeap();
minHeap.minHeapSort(nums, nums.length-1);
System.out.println("\n小根堆排序:");
for (int i = 1; i < nums.length; i++) {
System.out.print(nums[i] + " ");
}
}
/**
* 利用小根堆进行降序排序
* @param nums
* @param index
*/
void minHeapSort(int[] nums, int index){
buildMinHeap(nums);
while(index > 1){
swap(nums, 1, index);
index--;
heapAdjust(nums, 1, index);
}
}
/**
* 根据 nums 建立小根堆
* @param nums
*/
void buildMinHeap(int[] nums){
for(int i = nums.length/2; i>0; i--){
heapAdjust(nums, i, nums.length-1);
}
}
/**
* 在指定范围内 调整下标为 k 的小根堆
* @param nums
* @param k
* @param maxIndex
*/
void heapAdjust(int[] nums, int k, int maxIndex){
for(int i=2*k; i<=maxIndex; i*=2){
if(i+1 <= maxIndex){
i = nums[i] > nums[i+1] ? i+1 : i;
}
if(nums[k] > nums[i]){
swap(nums, i, k);
}
k = i;
}
}
void swap(int[] nums,int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
1.7 堆的插入
新元素先插入到尾部,再与父节点进行比较,假设当前为大根堆,则若父节点比插入节点小,则进行位置互换。如此反复,直到到达根节点或者父节点比插入节点大为止。
/**
* 大根堆插入,用链表维护大根堆,且链表第一个元素留空
* @param nums
* @param item
*/
void insertMaxHeap(LinkedList<Integer> nums, int item){
// 先插入到尾部
nums.add(item);
// 调整
int index = nums.size()-1;
for(int i=index/2; i>=1; i/=2){
if(nums.get(i) < nums.get(index)){
int tmp = nums.get(i);
nums.set(i, nums.get(index));
nums.set(index, tmp);
}else{
break;
}
index = i;
}
}
1.8 堆的删除
以大根堆为例子,使用链表尾部元素代替待删除元素,移除表尾元素。并从代替的位置开始进行堆调整。
/**
* 大根堆删除,用链表维护大根堆,且链表第一个元素留空
* @param nums
* @param item
*/
void deleteMaxHeap(LinkedList<Integer> nums, int item){
// 获取待删除元素位置
int index = nums.indexOf(item);
// 替代
nums.set(index, nums.getLast());
// 移除
nums.removeLast();
// 调整
for(int i=2*index; i<nums.size();i*=2){
// 获取下标
if(i+1< nums.size()){
i = nums.get(i) < nums.get(i+1) ? i+1 : i;
}
// 交换
int tmp = nums.get(index);
nums.set(index, nums.get(i));
nums.set(i, tmp);
// 更新待调整位置
index = i;
}
}
后序
- 我是一名大三本科生,专业是软件工程【一本】。目前,正在准备找实习以及秋招,意向岗位是Java后端开发工程师。为此,在码云托管了一个项目,以整理我所有所学知识。涉及内容:计算机网络、操作系统、Java基础、主流Java后端框架、设计模式、Web前端框架等内容。欢迎大家访问我的开源项目编程之路
- 码云地址:https://gitee.com/alizipeng/the-way-of-programming
- 以上内容均记载在我的开源项目中