堆排序
- 對於一個數組,我們可以用完全二叉樹來表示.其第一個元素是它的根節點.
- 下標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();
}
}