堆排序

堆排序

  • 對於一個數組,我們可以用完全二叉樹來表示.其第一個元素是它的根節點.
  • 下標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();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章