数据结构——栈、堆以及队列

洛谷P1981 表达式求值

1)涉及栈的知识点不太多,主要是栈的进栈和出栈操作。注意在c++中,pop() 没有返回值,一般和 top() 结合起来使用!

一些细节:

  • c++ 的 cin 会根据输入的类型自动进行判断读取,不必像 c 语言那么僵硬的一直用字符类型进行处理。
  • c++ 里面的 cin/cout 效率不如 scanf()/printf() ,所以在数据很大的时候最好还是使用后者。

leetcode224基本计算器

本题和洛谷P1981类似,都属于表达式求值类型的题目,该类题目可以使用递归进行求解,其中,我们可以具有以下思路:

  1. 遇到左括号直接进入递归处理,算出括号内的值之后返回结果

  2. 优先处理数字,同时考虑处理多位数据的情况,当遇到非数字或者到s的最后一个元素的时候,需要对数据进行处理!

基于以上思路,代码如下:

class Solution {
public:
    int calculate(string s) {
        int i = 0;
        return evaluate(s, i);
    }
    
private:
    int evaluate(string& s, int& i) {
        int res = 0;
        int num = 0;
        char op = '+';
        while (i < s.size()) {
            char c = s[i];
            if (c == '(') {
                num = evaluate(s, ++i);
            }
            else if (isdigit(c)) {
                num = num * 10 + (c - '0');
            }
            if ((!isdigit(c) && c != ' ') || i == s.size() - 1) {
                if (op == '+') {
                    res += num;
                } else if (op == '-') {
                    res -= num;
                }
                num = 0;
                op = c;
            }
            if (c == ')') {
                break;
            }
            i++;
        }
        return res;
    }
};

洛谷P5788单调栈

1)单调栈概念:栈中元素递增或者递减。

2)应用:单调栈内存储的元素一般都为下标,而不是真正的元素。

可以结合具体实例进行理解,被前面的人挡住视野这种理解方式就很合适,非常的贴切易懂!

3)特点:当遍历到 a[i] 时,如果a[i]的值对其后面的数不会产生影响(部分区间单调时),往往都需要使用单调栈的思想。

补充:

  • 关于开数组大小的注意事项。一般在函数内部开的数组(栈中开辟连续的空间),大小只有1~2Mb。而全局变量或者 static 声明的变量则是开在全局静态区,内存一般有 2G。开个10的六次方没问题。
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<stack>
using namespace std;
int n, a[3000005], f[3000005];//a是需要判断的数组(即输入的数组),f是存储答案的数组
stack<int> s;//模拟用的栈
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = 1; i <= n; i++) {
		while (!s.empty() && (a[s.top()] < a[i])) {
			f[s.top()] = i; s.pop();
		}
		s.push(i);
	}
	f[n] = 0;
	for (int i = 1; i <= n; i++) printf("%d ", f[i]);
	return 0;
}

洛谷P6510 奶牛排队

1)单调栈的深度理解并进行利用!!!
2)单调栈的含义

以从栈顶到栈底单调递减的栈为例(即每次入栈的元素都比栈内其他元素要大),每次入栈 a[i] ,对于 a[i] 而言,找到了其左侧比它小的第一个元素,而对于出栈的元素而言,相当于找到了其右侧比它小的第一个元素。

3)什么时候可以考虑使用单调栈?

涉及到一维数组的前后元素大小关系判断以及位置索引的时候!

洛谷P2367语文成绩

1)考察内容:差分算法中的优化解法。

60分的答案:(最后两个超时)

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>

using namespace std;

int n, p;
int a[5000005];

int main() {
	scanf("%d %d", &n, &p);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = 1; i <= p; i++) {
		int x, y, z;
		scanf("%d %d %d", &x, &y, &z);
		for (int j = x; j <= y; j++) a[j] += z;
	}
	int min = 10000000;
	for (int i = 1; i <= n; i++)
		if (a[i] < min) min = a[i];
	printf("%d", min);
	return 0;
}

2)读入技巧:如何将字符转换成数字

int _read() {
	int temp = 0;
	char ch;
	ch = getchar();
	while (ch >= '0' && ch <= '9') {
		temp = (temp << 3) + (temp << 1) + (ch ^ 48);
		ch = getchar();
	}
	return temp;
}
  1. 使用c++小技巧
//P1160队列安排(只展示部分残缺代码)
using Iter = list<int>::iterator;

int main(){
	for(int i = 2; i <= N; i++){
        else{
            auto Next = next(pos[k]);
            pos[i] = L.insert(Next, i);
        }
	}
    for(int x: L){//学习这样写代码! 
        cout << x;
    }
	return 0;
} 

堆是一种用数组表示的二叉树。其父子节点的实现依靠的是元素在数组中下标的大小关系。并没有传统的左右指针。

堆的性质

根据父节点与每个子节点的大小关系。堆可以分为最大堆和最小堆。同时,总是最大(或小)的元素在第一个位置(也就是根节点),所以堆也被称为优先队列。

堆的应用

使用堆主要是为了快速找到一个数组中的最值。这也是堆最广泛的应用。

leetcode373

题目:
给定两个以 升序排列 的整数数组 nums1 和 nums2 , 以及一个整数 k 。

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。

请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。

下面的ac代码为了方便,使用了Go语言标准库中的方法。免去了自己书写调整算法的麻烦。

ac代码:

func kSmallestPairs(nums1, nums2 []int, k int) (ans [][]int) {
    m, n := len(nums1), len(nums2)
    h := hp{nil, nums1, nums2}
    for i := 0; i < k && i < m; i++ {
        h.data = append(h.data, pair{i, 0})
    }
    for h.Len() > 0 && len(ans) < k {
        p := heap.Pop(&h).(pair)
        i, j := p.i, p.j
        ans = append(ans, []int{nums1[i], nums2[j]})
        if j+1 < n {
            heap.Push(&h, pair{i, j + 1})
        }
    }
    return
}

type pair struct{ i, j int }
type hp struct {
    data         []pair
    nums1, nums2 []int
}
func (h hp) Len() int            { return len(h.data) }
func (h hp) Less(i, j int) bool  { a, b := h.data[i], h.data[j]; return h.nums1[a.i]+h.nums2[a.j] < h.nums1[b.i]+h.nums2[b.j] }
func (h hp) Swap(i, j int)       { h.data[i], h.data[j] = h.data[j], h.data[i] }
func (h *hp) Push(v interface{}) { h.data = append(h.data, v.(pair)) }
func (h *hp) Pop() interface{}   { a := h.data; v := a[len(a)-1]; h.data = a[:len(a)-1]; return v }

队列

单调队列

与普通的队列不同,单调队列队尾也可以退出元素,其队尾的操作与单调栈的操作类似。但是单调队列队首的操作比较灵活,根据不同需求可以进行不同改变。

详细内容可以参考这篇博客

以单调递增队列为例:

每个元素 X 进队之前,需要进行两步操作。

第一:队首元素是否需要出队

第二:将X与队尾元素进行依次比较,如果X小于队尾元素,则将队尾元素出队;否则,再将X入队(也就是单调栈的操作流程)

洛谷P1886 滑动窗口/单调队列模板题

用一个单调递增队列以及一个单调递减队列,依次入队即可。

AC代码:

#include<iostream>
#include<deque>
using namespace std;
int n, k;
int a[1000005], maxN[1000005], minN[1000005];
deque<int> maxq, minq;
int main() {
    cin >> n;
    cin >> k;
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for(int i = 0; i < n; i++) {
        if(!maxq.empty() && i - maxq.front() >= k) maxq.pop_front();//滑动队列中的头部退队操作!
        while(!maxq.empty() && a[i] > a[maxq.back()]) maxq.pop_back();
        maxq.push_back(i);//保存下标
        if(!minq.empty() && i - minq.front() >= k) minq.pop_front();
        while(!minq.empty() && a[i] < a[minq.back()]) minq.pop_back();
        minq.push_back(i);
        if( i >= k-1) {
            maxN[i-k+1] = a[maxq.front()];
            minN[i-k+1] = a[minq.front()];
        }
    }
    for(int i = 0; i <= n-k; i++) cout << minN[i] << " ";
    cout << endl;
    for(int i = 0; i <= n-k; i++) cout << maxN[i] << " ";
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lingwu_hb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值