栈
洛谷P1981 表达式求值
1)涉及栈的知识点不太多,主要是栈的进栈和出栈操作。注意在c++中,pop() 没有返回值,一般和 top() 结合起来使用!
一些细节:
- c++ 的 cin 会根据输入的类型自动进行判断读取,不必像 c 语言那么僵硬的一直用字符类型进行处理。
- c++ 里面的 cin/cout 效率不如 scanf()/printf() ,所以在数据很大的时候最好还是使用后者。
leetcode224基本计算器
本题和洛谷P1981类似,都属于表达式求值类型的题目,该类题目可以使用递归进行求解,其中,我们可以具有以下思路:
-
遇到左括号直接进入递归处理,算出括号内的值之后返回结果
-
优先处理数字,同时考虑处理多位数据的情况,当遇到非数字或者到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;
}
- 使用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;
}