Post

算法(五)--栈/堆

算法(五)--栈/堆

分享在存在多个类似的操作或运算时的封装方式: 函数封装器和lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    unordered_map<string,function<int(int,int)>> map={
        {"+",[](int a,int b){return a+b;}},
        {"-",[](int a,int b){return a-b;}},
        {"*",[](int a,int b){return a*b;}},
        {"/",[](int a,int b){return a/b;}}
    } ;
```..

## 堆的主要算法思想

堆其实被又被称为优先队列、二叉堆;
- 堆总是一颗完全二叉树,使用数组作为其储存结构。
- 任意节点你小于或大于其所有孩子的节点。
- 小根堆和大根堆:
  - 小根堆:根节点的值小于其所有孩子节点的值。每次取出是最小的元素。
  - 大根堆:根节点的值大于其所有孩子节点的值。每次取出是最大的元素。

**重点(牢记,这是堆算法的基础):i节点的父节点索引是(i - 1)/2, 左右子节点分别为2i+1 2i+2**

小根堆的实现

```cpp
#include <iostream>
#include <queue>
#include <vector>

int main() {
    // 使用std::greater<int>作为比较器
    std::priority_queue<int, std::vector<int>,  std::greater<int>> minHeap;

    // 插入元素
    minHeap.push(10);
    minHeap.push(5);
    minHeap.push(20);
    minHeap.push(1);

    // 取出元素(最小值优先)
    while (!minHeap.empty()) {
        std::cout << minHeap.top() << " "; // 输出堆顶元素
        minHeap.pop(); // 移除堆顶
    }

    return 0;
}

大根堆的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <queue>
#include <vector>

int main() {
    // 使用std::greater<int>作为比较器
    std::priority_queue<int> maxHeap;

    // 插入元素
    minHeap.push(10);
    minHeap.push(5);
    minHeap.push(20);
    minHeap.push(1);

    // 取出元素(最小值优先)
    while (!minHeap.empty()) {
        std::cout << minHeap.top() << " "; // 输出堆顶元素
        minHeap.pop(); // 移除堆顶
    }

    return 0;
}

单调栈!接雨水!

单调栈分为单调递增栈和单调递减栈

  • 单调递增栈:栈内元素保持单调递增的栈,栈顶元素最小
  • 单调递减栈:栈内元素保持单调递减的栈,栈顶元素最大

下面以单调递增栈为例:

操作:

  • 如果新元素比栈顶元素大,就入栈;
  • 如果新元素比栈顶元素小,就把栈顶元素弹出来,知道栈顶比新元素小或者栈为空。

单调栈的妙用:

  • 单调栈内的元素是递增的:
    • 元素出栈时,插入的新元素是出栈元素往后找的第一个比它小的元素
    • 元素出栈后,说明新栈顶元素是出栈元素向前找的第一个比它小的元素

总结:单调递增栈的作用就是找到某个元素的左右第一个比它小的元素。相反,单调递减栈的作用就是找到某个元素的左右第一个比它大的元素。

经典:接雨水

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
// 手搓单调栈!!!对于接雨水,我们需要找到某个元素左右第一个比它大的元素(木桶原理),所以要使用单调递减栈,即栈顶元素最小,且栈内元素单调递减
// 将所有的元素推入单调递减栈,当有元素出栈时,计算当前元素可接雨水的大小
    int trap(vector<int>& height) {
        stack<int> st; //单调递减栈
        int ans = 0;
        for(int i = 0; i<height.size(); i++) {
            // 栈不为空,且新元素比栈顶元素大时需要进行计算更新ans
            while(!st.empty() && height[st.top()] < height[i]) {
                int cur = st.top();
                st.pop();
                if(st.empty()) break;  // 栈内只有一个元素不能接雨水
                int l = st.top();
                int r = i;
                int h = std::min(height[l], height[r]) - height[cur];
                ans += h * (r - l -1);
            }
            st.push(i);
        }
        return ans;
    }
};

二叉堆!!!优先级队列容器的底层实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 最小堆 
class MinHeap {
private:
    vector<int> heap;

    void heapifyUp(int index) {
        if(index == 0) return;
        int parent = (intdex - 1) / 2;
        if(heap[parent] > heap[index]) {
            swap(heap[parent], heap[index]);
            heapifyUp(parent);
        }
    }

    void heapifyDown(int index) {
        int left = 2 * index + 1;
        int right 2 * index + 2;
        int smallest = index;
        if(left < heap.size() && heap[left] < heap[smallest]) {
            smallest = left;
        }
        if(right < heap.size() && heap[right] < heap[smallest]) {
            smallest = right;
        }
        if(smallest != index) {
            swap(heap[smallest], heap[index]);
            heapifyDown(smallest);
        }
    }
public:
    void insert(int val) {
        heap.push_back(val);
        heapifyUp(heap.size() - 1);
    }

    void pop() {
        if(heap.empty()) return;
        heap[0] = heap.back();
        heap.pop_back();
        heapifyDown(0);
    }

    int top() {
        if(heap.empty()) return -1;
        return heap[0];
    }
} 

## 最小栈

维护两个栈,一个正常栈,一个最小栈;当插入元素时,正常栈直接插入,如果最小栈为空或者插入元素小于等于最小栈栈顶元素,最小站也插入元素;当弹出元素时,如果正常栈弹出的栈顶元素等于最小栈栈顶元素,最小栈也弹出栈顶元素。

```cpp
class MinStack {
public:
    stack<int> st;
    stack<int> minSt;
    MinStack() {}

    void push(int val) {
        st.push(val);
        if (minSt.empty())
            minSt.push(val);
        else if (minSt.top() >= val)
            minSt.push(val);
    }

    void pop() {
        int temp = st.top();
        st.pop();
        if(minSt.top() == temp) minSt.pop();
    }

    int top() { return st.top(); }

    int getMin() { return minSt.top(); }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */
This post is licensed under CC BY 4.0 by the author.