堆排序
- 对于一个数组,我们可以用完全二叉树来表示.其第一个元素是它的根节点.
- 下标i的元素,其左孩子节点是2i+1,右孩子节点是2i+2,根节点是(i-1)/2
- 大根堆:任何一棵子树的root都是这棵子树的最大值
- 小根堆:任何一棵子树的root都是这棵子树的最小值
建堆(自下而上)
给定一个数组,如果想利用堆结构去进行逻辑运算,首先要建立一个堆(大根堆or小根堆)。建堆的时候应该自行脑补一下完全二叉树,尽管我们的遍历,交换都是在数组中完成的。
拿建立大根堆来说
遍历数组,遍历到的元素跟根节点比较,如果比根节点大,就交换。但是此时还需要跟已交换节点的根节点比较,总之就是一直保证每棵子树root都是此子树的最大值!
import java.util.Arrays;
/**
* 对于一个数组,我们可以用完全二叉树来表示.其第一个元素是它的根节点.
* 下标i的元素,其左孩子节点是2i+1,右孩子节点是2i+2,根节点是(i-1)/2
* 大根堆:此二叉树的树根是整棵树中的最大值
* 小根堆:~是最小值
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[]{2, 5, 6, 4, 1, 8};
System.out.println(Arrays.toString(arr));
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapSort(int arr[]) {
int size = arr.length;
//先通过插入元素的方式,建立一个大顶堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
//换下最大元素至数组末尾
swap(arr, 0, --size);
//当size等于1的时候,就是只剩一个元素啦,数组就有序了
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
/**
* 建立大根堆的方法
*
* @param arr 原始数组,把它想象成一颗完全二叉树
* @param index 数组中的某个元素下标
*/
public static void heapInsert(int[] arr, int index) {
//如果大于它的父节点,就交换他们的值,然后让index指向它的父节点,这是为了循环向上
//查找调整
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
/**
* 调整大根堆的方法.具体做法是取下这颗完全二叉树的根节点,也就是数组的最大值.
* 然后数组长度size要减一.再对剩余元素组成的完全二叉树做调整.
*
* @param arr 长度为size的数组
* @param index 当前完全二叉树或者其子树的根节点(他可能已经不是最大值了,需要做调整)
* @param size 当前数组的长度
*/
public static void heapify(int[] arr, int index, int size) {
//当前子树的左孩子节点
int left = 2 * index + 1;
//数组不能下标越界
while (left < size) {
//先比较index的左右孩子谁大
// arr[left + 1] > arr[left] ? left + 1 : left;
// 这条语句必须这么写,left和left+1不能反过来
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
//那个大的再和index比较
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
//交换了,但是还需要比较剩下的子树,所以要将index指向largest
index = largest;
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int temp;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
例题
数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
class Solution {
public int findKthLargest(int[] nums, int k) {
//默认建立的是小顶堆
PriorityQueue<Integer> pq=new PriorityQueue();
for(int i=0;i<nums.length;i++)
{
//向堆中添加元素,维持一个窗口
pq.add(nums[i]);
if(pq.size()>k)
{
//保证窗口的大小是3,如果超过3,就弹出当前堆中最小的元素,剩下的是
//较大的元素,随着窗口的推进,可以将n-3个最小的元素都依次弹出
//那么剩下的就是最大的3个元素
pq.poll();
}
}
//堆顶是最大的三个元素里最小的那个,也就是第三大的元素
return pq.peek();
}
}
来源:CSDN
作者:Ga_Lip
链接:https://blog.csdn.net/qq_35646931/article/details/104132394