堆的底層和實現(xiàn)及相關(guān)應(yīng)用

一、定義

堆是一種基于樹的數(shù)據(jù)結(jié)構(gòu),通常用完全二叉樹實現(xiàn)。

  • 完全二叉樹:除了最后一層外,其他層的節(jié)點都達(dá)到最大,并且最后一層的節(jié)點從左到右排列。
  • 滿二叉樹:每一層的節(jié)點都被完全填滿的二叉樹,并且每個非葉子節(jié)點都有兩個子節(jié)點

完全二叉樹

       1
     /   \
    2     3
   / \   /
  4   5 6

滿二叉樹

       1
     /   \
    2     3
   / \   / \
  4   5 6   7

滿二叉樹也是完全二叉樹

1.堆的特性

大頂堆(Max Heap)和小頂堆(Min Heap)是完全二叉樹的一種特殊形式

  • 最頂層的節(jié)點(沒有父親)稱之為 root 根節(jié)點
  • 在大頂堆中,任意節(jié)點 C 與它的父節(jié)點 P 符合 P.value ≥ C.value
  • 小頂堆中,任意節(jié)點 C 與它的父節(jié)點 P 符合 P.value ≤ C.value

大頂堆

    10
   /  \
  5    3
 / \
2   4

小頂堆

    2
   / \
  3   4
 / \
5   6

2.堆的存儲

完全二叉樹這種非線性的結(jié)構(gòu)可以使用數(shù)組這種線性的結(jié)構(gòu)來表示:

image.png

節(jié)點在數(shù)組中索引的計算

從索引 0 開始存儲節(jié)點數(shù)據(jù)

  • 節(jié)點 i 的父節(jié)點索引為 floor((i-1)/2),(i>0)
  • 節(jié)點 i 的左孩子節(jié)點索引為 2i+1,右孩子節(jié)點索引為 2i+2,計算的結(jié)果需要小于size

從索引 1 開始存儲節(jié)點數(shù)據(jù)

  • 節(jié)點 i 的父節(jié)點索引為 floor(i/2),(i > 1)
  • 節(jié)點 i 的左孩子節(jié)點索引為 2i,右孩子節(jié)點索引為 2i+1,計算的結(jié)果需要小于size

二、實現(xiàn)優(yōu)先級隊列

1.無序數(shù)組實現(xiàn)

package com.hcx.algorithm.queue;

/**
 * @Title: PriorityQueue1.java
 * @Package com.hcx.algorithm.queue
 * @Description: 優(yōu)先級隊列:無序數(shù)組實現(xiàn)
 * 入隊:跟普通隊列一樣
 * 出隊:優(yōu)先級最高的出隊
 * @Author: hongcaixia
 * @Date: 2025/1/12 17:33
 * @Version V1.0
 */
public class PriorityQueue1<E extends Priority> implements Queue<E> {

    Priority[] array;

    // 數(shù)組當(dāng)前大小
    int size;

    public PriorityQueue1(int capacity) {
        array = new Priority[capacity];
    }

    @Override
    public boolean offer(E e) {
        if(isFull()){
            return false;
        }
        array[size++] = e;
        return true;
    }

    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        // 找到優(yōu)先級最高的元素出隊
        int maxIndex = 0;
        for (int i = 0; i < size; i++) {
            if (array[i].getPriority() > array[maxIndex].getPriority()) {
                maxIndex = i;
            }
        }
        E e = (E) array[maxIndex];
        // 出隊后,該位置后的元素往前移動
        if (maxIndex < size - 1) {
            System.arraycopy(array, maxIndex + 1, array, maxIndex, size - maxIndex - 1);
        }
        // help gc
        array[--size] = null;
        return e;
    }

    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        // 找到優(yōu)先級最高的元素
        int maxIndex = 0;
        for (int i = 0; i < size; i++) {
            if (array[i].getPriority() > array[maxIndex].getPriority()) {
                maxIndex = i;
            }
        }
        E e = (E) array[maxIndex];
        return e;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }
}

優(yōu)先級接口:

public interface Priority {
    /**
     * 返回對象的優(yōu)先級(數(shù)字越大,優(yōu)先級越高)
     * @return
     */
    int getPriority();
}

2.有序數(shù)組實現(xiàn)

package com.hcx.algorithm.queue;

/**
 * @Title: PriorityQueue1.java
 * @Package com.hcx.algorithm.queue
 * @Description: 優(yōu)先級隊列:有序數(shù)組實現(xiàn)
 * 入隊:跟普通隊列一樣
 * 出隊:優(yōu)先級最高的出隊
 * @Author: hongcaixia
 * @Date: 2025/1/12 17:33
 * @Version V1.0
 */
public class PriorityQueue2<E extends Priority> implements Queue<E> {

    Priority[] array;

    // 數(shù)組當(dāng)前大小
    int size;

    public PriorityQueue2(int capacity) {
        array = new Priority[capacity];
    }

    @Override
    public boolean offer(E e) {
        if (isFull()) {
            return false;
        }
        // 按照優(yōu)先級,插入到正確的位置
        int index = size - 1;
        // 從數(shù)組末尾開始往前找,如果數(shù)組中元素的優(yōu)先級比當(dāng)前的高,就繼續(xù)往前,同時把數(shù)組的元素往后移(空出位置給當(dāng)前元素)
        while (index >= 0 && array[index].getPriority() > e.getPriority()) {
            array[index + 1] = array[index];
            index--;
        }
        // 找到了比當(dāng)前優(yōu)先級小的則退出了循環(huán),那么index所指向的上一個就是要插入的位置
        array[index + 1] = e;
        size++;
        return true;
    }

    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        E e = (E) array[size - 1];
        // help gc
        array[--size] = null;
        return e;
    }

    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return (E) array[size - 1];
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }
}

3.大頂堆實現(xiàn)

package com.hcx.algorithm.queue;

/**
 * @Title: PriorityQueue1.java
 * @Package com.hcx.algorithm.queue
 * @Description: 優(yōu)先級隊列:使用大頂堆實現(xiàn)
 * @Author: hongcaixia
 * @Date: 2025/1/12 17:33
 * @Version V1.0
 */
public class PriorityQueue3<E extends Priority> implements Queue<E> {

    Priority[] array;

    // 數(shù)組當(dāng)前大小
    int size;

    public PriorityQueue3(int capacity) {
        array = new Priority[capacity];
    }

    /**
     * 1.新元素添加到數(shù)組的尾部
     * 2.要符合大頂堆的特性,還需要對堆進(jìn)行調(diào)整:
     *   循環(huán)比較新元素與父節(jié)點的優(yōu)先級,如果父節(jié)點的優(yōu)先級低,則往下移動到子節(jié)點的位置,直到父節(jié)點的優(yōu)先級更高或者childIndex==0
     * @param e
     * @return
     */
    @Override
    public boolean offer(E e) {
        if (isFull()) {
            return false;
        }
        int childIndex = size;
        int parentIndex = (childIndex - 1) / 2;
        // array[childIndex] = e;
        while (childIndex > 0 && e.getPriority() > array[parentIndex].getPriority()) {
            // 把父節(jié)點放到子節(jié)點上 上層的元素依次往下一層放
            array[childIndex] = array[parentIndex];
            // 兩個指針繼續(xù)往上找,直到不符合條件為止
            childIndex = parentIndex;
            parentIndex = (childIndex - 1) / 2;
        }
        array[childIndex] = e;
        size++;
        return true;
    }

    /**
     * 1.移除并返回優(yōu)先級最高的元素,即根元素
     * 2.要符合堆的特性,還需要對堆進(jìn)行調(diào)整
     *   - 把堆頂元素與最末尾元素進(jìn)行交換,移除并返回最末尾的元素,大小減1
     *   - 調(diào)整堆:對堆頂?shù)母匾来闻c孩子節(jié)點作比較,如果優(yōu)先級比孩子節(jié)點小,往下移動,直到父節(jié)點優(yōu)先級大于孩子節(jié)點優(yōu)先級或者沒有孩子節(jié)點為止。
     * @return
     */
    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        // 1.堆頂元素和最末尾元素交換
        Priority top = array[0];
        array[0] = array[size - 1];
        array[size - 1] = top;

        // 2.數(shù)組大小-1
        size--;

        // 要返回的元素
        Priority e = array[size];
        // help gc
        array[size] = null;

        // 3.調(diào)整堆頂元素位置,依次往下找到正確的位置
        downToProper(0);

        return (E) e;
    }

    /**
     * 將父節(jié)點元素下沉到正確的位置
     * 從堆頂開始,依次將父元素與孩子節(jié)點中較大的進(jìn)行交換,直到父元素大于兩個孩子,或者沒有孩子為止。
     * @param parentIndex 父節(jié)點索引
     */
    public void downToProper(int parentIndex) {
        // 該節(jié)點的左孩子
        int leftChildIndex = parentIndex * 2 + 1;
        // 右孩子
        int rightChildIndex = leftChildIndex + 1;

        // 父節(jié)點索引 先假設(shè)本身的優(yōu)先級最高,如果有比他高的 就替換掉他
        int maxIndex = parentIndex;

        if (leftChildIndex < size && array[leftChildIndex].getPriority() > array[parentIndex].getPriority()) {
            // 左孩子節(jié)點的優(yōu)先級比父節(jié)點大,將maxIndex 設(shè)置為左孩子索引
            maxIndex = leftChildIndex;
        }
        if (rightChildIndex < size && array[rightChildIndex].getPriority() > array[parentIndex].getPriority()) {
            // 右孩子節(jié)點的優(yōu)先級比父節(jié)點大,將maxIndex設(shè)置為右孩子索引
            maxIndex = rightChildIndex;
        }

        // 說明有更大的孩子節(jié)點
        if (maxIndex != parentIndex) {
            // 把父節(jié)點和該節(jié)點交換
            Priority temp = array[maxIndex];
            array[maxIndex] = array[parentIndex];
            array[parentIndex] = temp;
            // 繼續(xù)往下找
            downToProper(maxIndex);
        }
    }

    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return (E) array[0];
    }


    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == array.length;
    }
}

總結(jié):

  • 無序數(shù)組實現(xiàn)優(yōu)先級隊列:出隊效率較低:O(n)
  • 有序數(shù)組實現(xiàn)優(yōu)先級隊列:入隊效率較低:O(n)
  • 大頂堆實現(xiàn)優(yōu)先級隊列:出隊和入隊:log(n)

三、Floyd建堆算法

  • 找到最后一個非葉子節(jié)點
  • 從最后一個非葉子節(jié)點從后向前,對每一個節(jié)點執(zhí)行下沉操作(就是所有有孩子的父節(jié)點,都下沉到正確的位置(大頂堆:保證父節(jié)點是最大的,如果本身就是大的比較之后不需要下沉))
    /**
     * 建堆
     * 1.找到最后一個非葉子節(jié)點
     * 2.從最后一個非葉子節(jié)點從后向前,對每一個節(jié)點執(zhí)行下沉操作(就是所有有孩子的父節(jié)點,都下沉到正確的位置(大頂堆:保證父節(jié)點是最大的,如果本身就是大的比較之后不需要下沉))
     */
    private void heapify() {
        // 最后一個非葉子節(jié)點索引 最后一個元素的父元素 (size-2)/2;
        int index = size / 2 - 1;
        for (int i = index; i >= 0; i--) {
            downToProper(i);
        }
    }

四、堆排序

  • 1.建立大頂堆
  • 2.讓堆頂?shù)脑嘏c堆底的元素交換,縮小堆的大小,調(diào)整堆
  • 3.重復(fù)步驟二直到堆中僅剩一個元素
package com.hcx.algorithm.heap;

/**
 * @Title: HeapSort.java
 * @Package com.hcx.algorithm.heap
 * @Description: 堆排序
 * 1.建立大頂堆
 * 2.讓堆頂?shù)脑嘏c堆底的元素交換,縮小堆的大小,調(diào)整堆
 * 3.重復(fù)步驟二直到堆中僅剩一個元素
 * @Author: hongcaixia
 * @Date: 2025/1/14 11:52
 * @Version V1.0
 */
public class HeapSort {

    static int[] arr;

    int size;

    public HeapSort(int[] array) {
        this.arr = array;
        this.size = array.length;
        heapify();
    }

    private void heapSort() {
        // 1.建堆
        heapify();

        while (size > 1) {
            // 2.堆頂元素與堆底交換
            swap(0, size - 1);
            // 縮小堆
            size--;
            // 重建堆
            downToProper(0);
        }
    }

    public static void main(String[] args) {
        int[] array = {1, 9, 3, 2, 6, 8, 7, 5};
        HeapSort heapSort = new HeapSort(array);
        heapSort.heapSort();
        for (int i = 0;i<arr.length;i++){
            System.out.println(arr[i]);
        }
    }


    public void heapify() {
        int index = (arr.length - 1) / 2 - 1;
        for (int i = index; i >= 0; i--) {
            downToProper(i);
        }
    }


    /**
     * 交換兩個元素
     * @param i
     * @param j
     */
    private void swap(int i,int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    /**
     * 將元素下沉到正確的位置
     * @param index 元素當(dāng)前下標(biāo)
     */
    private void downToProper(int index) {
        int maxIndex = index;

        // 計算左右孩子節(jié)點
        int leftChildIndex = 2 * index + 1;
        if (leftChildIndex < size && arr[leftChildIndex] > arr[maxIndex]) {
            maxIndex = leftChildIndex;
        }
        int rightChildIndex = leftChildIndex + 1;
        if (rightChildIndex < size && arr[rightChildIndex] > arr[maxIndex]) {
            maxIndex = rightChildIndex;
        }
        // 找到了更大的孩子 交換元素
        if (maxIndex != index) {
            swap(index, maxIndex);
            downToProper(maxIndex);
        }
    }
}

五、實現(xiàn)堆

package com.hcx.algorithm.heap;

/**
 * @Title: Heap.java
 * @Package com.hcx.algorithm.heap
 * @Description: 堆
 * @Author: hongcaixia
 * @Date: 2025/1/14 18:14
 * @Version V1.0
 */
public class Heap {

    int[] arr;

    int size;

    // 是否是大頂堆
    boolean maxHeapFlag;

    public int size() {
        return size;
    }

    public Heap(int capacity, boolean maxHeapFlag) {
        this.arr = new int[capacity];
        this.maxHeapFlag = maxHeapFlag;
    }

    public Heap(int[] arr, boolean maxHeapFlag) {
        this.arr = arr;
        this.size = arr.length;
        this.maxHeapFlag = maxHeapFlag;
        heapify();
    }

    /**
     * 獲取堆頂元素
     *
     * @return
     */
    public int peek() {
        return arr[0];
    }

    /**
     * 移除堆頂元素并返回
     * 1.堆頂元素和末尾元素交換
     * 2.size--
     * 3.堆頂?shù)脑匾来蜗鲁恋秸_的位置
     *
     * @return
     */
    public int poll() {
        int top = arr[0];
        swap(0, size - 1);
        size--;

        // 對堆頂元素依次執(zhí)行下沉操作到正確位置
        downToProper(0);

        return top;
    }

    /**
     * 移除指定索引的元素并返回
     *
     * @param index
     * @return
     */
    public int poll(int index) {
        int ele = arr[index];
        swap(index, size - 1);
        size--;
        downToProper(index);
        return ele;
    }

    /**
     * 替換堆頂元素
     *
     * @param replaced
     */
    public void replaceTop(int replaced) {
        arr[0] = replaced;
        downToProper(0);
    }

    /**
     * 在堆的尾部添加元素
     *
     * @param offered
     * @return
     */
    public void offer(int offered) {
        if (size == arr.length) {
            arrGrow();
        }
        upToProper(offered, size);
        size++;
    }

    /**
     * 將元素上浮到正確位置
     *
     * @param offered 元素
     * @param index   元素當(dāng)前下標(biāo)
     */
    private void upToProper(int offered, int index) {
        int childIndex = index;

        while (childIndex > 0) {
            // 計算他的父節(jié)點下標(biāo)
            int parentIndex = (childIndex - 1) >> 1;
            boolean compare = maxHeapFlag ? offered > arr[parentIndex] : offered < arr[parentIndex];
            if (compare) {
                // 父節(jié)點往下移動
                arr[childIndex] = arr[parentIndex];
            } else {
                break;
            }
            // 改變孩子節(jié)點為當(dāng)前的父節(jié)點
            childIndex = parentIndex;
        }
        arr[childIndex] = offered;
    }

    /**
     * 將元素下沉到正確的位置
     *
     * @param index 元素當(dāng)前下標(biāo)
     */
    private void downToProper(int index) {
        int minIndex = index;

        // 計算左右孩子節(jié)點
        int leftChildIndex = 2 * index + 1;

        if ((leftChildIndex < size) && (maxHeapFlag ? arr[leftChildIndex] > arr[minIndex] : arr[leftChildIndex] < arr[minIndex])) {
            minIndex = leftChildIndex;
        }
        int rightChildIndex = leftChildIndex + 1;
        if ((rightChildIndex < size) && (maxHeapFlag ? arr[rightChildIndex] > arr[minIndex] : arr[rightChildIndex] < arr[minIndex])) {
            minIndex = rightChildIndex;
        }
        // 找到了更大的孩子 交換元素
        if (minIndex != index) {
            swap(index, minIndex);
            downToProper(minIndex);
        }
    }

    /**
     * 交換兩個元素
     *
     * @param i
     * @param j
     */
    private void swap(int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    /**
     * 建堆
     * 1.找到最后一個非葉子節(jié)點
     * 2.從最后一個非葉子節(jié)點從后向前,對每一個節(jié)點執(zhí)行下沉操作(就是所有有孩子的父節(jié)點,都下沉到正確的位置(大頂堆:保證父節(jié)點是最大的,如果本身就是大的比較之后不需要下沉))
     */
    private void heapify() {
        // 最后一個非葉子節(jié)點索引 最后一個元素的父元素 (size-2)/2;
        int index = size / 2 - 1;
        for (int i = index; i >= 0; i--) {
            downToProper(i);
        }
    }

    public boolean isFull() {
        return size == arr.length;
    }


    /**
     * 數(shù)組擴容:每次擴為原來的1.5倍
     */
    private void arrGrow() {
        int capacity = size + (size >> 1);
        int[] newArr = new int[capacity];
        System.arraycopy(arr, 0, newArr, 0, size);
        arr = newArr;
    }
}

六、Leetcode703.數(shù)據(jù)流中的第K大元素

package com.hcx.algorithm.heap;

/**
 * @Title: KthLargest.java
 * @Package com.hcx.algorithm.heap
 * @Description: Leetcode703.數(shù)據(jù)流中的第K大元素
 * @Author: hongcaixia
 * @Date: 2025/1/14 16:42
 * @Version V1.0
 */
public class KthLargest {

    MinHeap minHeap;

    public KthLargest(int k, int[] nums) {
        minHeap = new MinHeap(k);
        for (int num : nums) {
            add(num);
        }
    }

    /**
     * 插入數(shù)據(jù)流后,返回當(dāng)前第k大元素
     * @param val
     * @return
     */
    public int add(int val) {
        // 堆沒滿
        if(!minHeap.isFull()){
            minHeap.offer(val);
        }else if (val > minHeap.peek()) {
            minHeap.replaceTop(val);
        }
        return minHeap.peek();
    }
}

七、Leetcode295.數(shù)據(jù)流的中位數(shù)

分成兩部分:一部分是較小的,一部分是較大的

  • 較小數(shù)據(jù)中讓最大的在堆頂,較大的數(shù)據(jù)中讓最小的在堆頂
  • 堆頂?shù)膬蓚€元素就是中間的兩個
  • 左邊是大頂堆 右邊是小頂堆
public class MedianFinder {

    // 大頂堆,存儲前半部分元素;
    static Heap maxHeap = new Heap(10, true);

    // 小頂堆,存儲后半部分元素
    static Heap minHeap = new Heap(10, false);

    public static void addNum(int num) {
        if (maxHeap.size == minHeap.size) {
            //元素加入左邊 加入左邊之前 要保證元素是最小的 先加入右邊,再從右邊的堆頂取出最小的加入左邊
            minHeap.offer(num);
            maxHeap.offer(minHeap.poll());
        } else {
            // 元素加入右邊,加入右邊之前,要保證元素是大的,先加入左邊,再從左邊的堆頂取出最大的加入右邊
            maxHeap.offer(num);
            minHeap.offer(maxHeap.poll());
        }
    }

    public static double findMedian() {
        if (maxHeap.size == minHeap.size) {
            return (maxHeap.peek() + minHeap.peek()) / 2.0;
        } else {
            return maxHeap.peek();
        }
    }
}

使用jdk的堆實現(xiàn):

class MedianFinder {

    // 使用優(yōu)先級隊列 默認(rèn)是小頂堆
    // 存儲前半部分元素
    static PriorityQueue<Integer> maxQueue = new PriorityQueue<>((o1, o2) -> Integer.compare(o2, o1));

    // 存儲后半部分元素
    static PriorityQueue<Integer> minQueue = new PriorityQueue<>((o1, o2) -> Integer.compare(o1, o2));
    
    public void addNum(int num) {
        // 往前半部分加
        if (minQueue.size() == maxQueue.size()) {
            minQueue.offer(num);
            maxQueue.offer(minQueue.poll());
        } else {
            // 往右邊加 加在右邊的要保證是大的
            maxQueue.offer(num);
            minQueue.offer(maxQueue.poll());
        }
    }
    
    public double findMedian() {
        if(maxQueue.size() == minQueue.size()){
            return (maxQueue.peek() + minQueue.peek()) / 2.0;
        }else{
            return maxQueue.peek();
        }
    }
}

救命,這個實現(xiàn)方法在力扣上一直不對,評論區(qū)的大神救救孩子,本地是好的,找不出來哪里的問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 一、何為“堆” (一)基本概念 1、滿二叉樹 ?一個二叉樹,如果每一個層的結(jié)點數(shù)都達(dá)到最大值,則這個二叉樹就是滿二...
    ITsCLG閱讀 1,038評論 0 4
  • 堆定義 堆是一個樹形結(jié)構(gòu),其底層實現(xiàn)是一棵完全二叉樹。而完全二叉樹是一層一層按照進(jìn)入的順序排成的。按照這個特性,我...
    熱血大桃子閱讀 2,395評論 1 2
  • 堆(Heap)是計算機科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱。堆通常是一個可以被看做一棵完全二叉樹的數(shù)組對象。 1.假設(shè)父...
    名字是亂打的閱讀 91評論 0 1
  • 前言 上一篇寫了數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹、AVL自平衡樹,這次來寫堆。 堆的創(chuàng)造者 很久以前排序算法的時間復(fù)雜度一直是...
    李嘉的博客閱讀 824評論 0 0
  • 1、思考 設(shè)計一種數(shù)據(jù)結(jié)構(gòu),用來存放整數(shù),要求提供 3 個接口1. 添加元素2. 獲取最大值3. 刪除最大值 有沒...
    咸魚Jay閱讀 386評論 0 0