二叉堆以及堆排序

  • 二叉堆是一种特殊的完全二叉树,只不过它存储在数组中。
    (1)大顶堆:每个结点都大于等于它的两个子结点的二叉堆。
    (2)大顶堆:每个结点都小于等于它的两个子结点的二叉堆。
  • 如果根结点的下标是 0,那么对于某个结点 i:
    (1)其左结点下标:2 * i + 1
    (2)其右结点的下标:2 * i + 2
    (3)其父结点的下标:(i - 1) / 2
  • 大顶堆的实现:
    (1)swim():如果堆的有序状态因为某个结点变的比它的父结点更大而被打破,那么我们就需要通过交换它和它的父结点来修复堆。交换后,这个节点比它的两个子结点都大(一个是曾经的父结点,另一个比它更小,因为它是曾经父结点的子结点),但这个结点仍然可能比它现在的父结点更大,我们可以一遍遍地用同样的方法恢复秩序。
    (2)sink():如果堆的有序状态因为某个结点变的比它的两个子结点或是其中之一更小了而被打破,那么我们就需要通过交换它和它的两个子结点中的较大者来修复堆。交换可能会在子结点处继续打破堆的有序状态,因此我们需要不断地用相同的方式将其修复。
class Heap{
    
    
    int[] pq; // 存储元素的数组
    int n = 0; // 数组中元素的个数。pq[0 ... n - 1]
    public Heap(int maxN){
    
    
        pq = new int[maxN];
    }

    // 插入新元素
    public void insert(int e){
    
    
        // 将新元素加到最后
        pq[n++] = e;
        // 让它上浮到正确的位置
        swim(n - 1);
    }

    // pq[i] 是否比 pq[j] 小
    public boolean less(int i, int j){
    
    
        return pq[i] < pq[j];
    }
    
    // 交换 pq[i] 和 pq[j] 的值
    public void exch(int i, int j){
    
    
        int temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;
    }
    // 上浮
    public void swim(int k){
    
    
        // 如果浮到堆顶,就不能再上浮了
        // 如果还没浮到堆顶 && 当前结点的父结点小于当前结点
        while(k > 0 && less((k - 1) / 2, k)){
    
    
            exch((k - 1) / 2, k);
            k = (k - 1) / 2;
        }
    }
    // 下沉
    public void sink(int k){
    
    
        // 如果沉到堆底,就沉不下去了
        while(2 * k + 1 <= n - 1){
    
    
            // 假设当前结点的左边结点较大
            int bigger = 2 * k + 1;
            // 如果当前结点的右边结点存在 && 当前结点的左边结点小于当前结点的右边结点
            if(bigger + 1 < n && less(bigger, bigger + 1)){
    
    
                bigger++;
            }
            // 如果当前结点比左右结点都大,就不必下沉
            if(!less(k, bigger)){
    
    
                break;
            }
            // 否则,需要下沉当前结点
            exch(k, bigger);
            k = bigger;
        }
    }
}
  • 堆排序:
public class HeapSort {
    
    
    public static void main(String[] args) {
    
    
        int[] nums = {
    
    3, 7, 8, 3, 4, 2, 9, 1};
        heapSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    public static void heapSort(int[] nums){
    
    
        int n = nums.length;
        // 将整个数组构造成大顶堆(从最后一个非叶子结点开始调整)
        for(int k = n / 2 - 1; k >= 0; k--){
    
    
            sink(nums, k, n);
        }
        while(n > 0){
    
    
            // 将最大的元素 nums[0] 和 nums[n - 1] 交换位置,交换了之后相当于把最大的元素删除,即 --n 操作。
            exch(nums, 0, --n);
            // 修复堆。交换了一次后堆已经不符合条件了,需要重新让它变成大顶堆
            sink(nums, 0, n);
        }
    }
    public static void sink(int[] nums, int k, int n){
    
    
        // 如果沉到堆底,就沉不下去了
        while(2 * k + 1 <= n - 1){
    
    
            // 假设当前结点的左边结点较大
            int bigger = 2 * k + 1;
            // 如果当前结点的右边结点存在 && 当前结点的左边结点小于当前结点的右边结点
            if(bigger + 1 < n && less(nums, bigger, bigger + 1)){
    
    
                bigger++;
            }
            // 如果当前结点比左右结点都大,就不必下沉
            if(!less(nums, k, bigger)){
    
    
                break;
            }
            // 否则,需要下沉当前结点
            exch(nums, k, bigger);
            k = bigger;
        }
    }
    public static boolean less(int[] nums, int i, int j){
    
    
        return nums[i] < nums[j];
    }
    // 交换 nums[i] 和 nums[j] 的值
    public static void exch(int[] nums, int i, int j){
    
    
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

Guess you like

Origin blog.csdn.net/weixin_46497503/article/details/120226505