数据结构与算法– 栈(Stack)
1、栈定义
栈是限定仅在表尾进行插入和删除操作的线性表。
- 后进先出(Last in, First out)
- push 入栈,pop 出栈,top栈顶
2、栈的数据类型
ADT 栈
Data
同线性表 元素具有相同类型,相邻元素具有前驱后继关系
Operation
InitStack(*S); // 初始化操作 建立空栈S
DestroyStack(*S) //若栈存在则销毁
ClearStack(*S) //将栈清空
StackEmpty(S); // 若栈为空,返回true,否则返回false
GetTop(S,*e) //若栈存在且为空,用e返回S的栈顶元素
Push(*S,e) //若栈S存在,插入新元素e到栈S的栈顶元素中
Pop(*S,*e) //删除栈中栈顶元素,并用e返回其值
StackLength(S) // 返回栈S中元素个数
endADT
3、结构定义
typedef int SElemType;
// 建栈
typedef struct {
SElemType data[MAXSIZE];
int *top; // 栈顶指针
}sqStack;
4、算法题解
232. Implement Queue using Stacks (Easy) --用栈实现队列
栈的顺序为后进先出,而队列的顺序为先进先出。使用两个栈实现队列,一个元素需要经过两个栈才能出队列,在经过第一个栈时元素顺序被反转,经过第二个栈时再次被反转,此时就是先进先出顺序。
思路
将一个栈当作输入栈,用于压入 push传入的数据;另一个栈当作输出栈,用于pop 和peek 操作。
每次pop 或peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。
class MyQueue {
private Stack<Integer> in = new Stack<>();
private Stack<Integer> out = new Stack<>();
public void push(int x) {
in.push(x);
}
public int pop() {
in2out();
return out.pop();
}
public int peek() {
in2out();
return out.peek();
}
private void in2out() {
if (out.isEmpty()) {
while (!in.isEmpty()) {
out.push(in.pop());
}
}
}
public boolean empty() {
return in.isEmpty() && out.isEmpty();
}
}
225. Implement Stack using Queues (Easy) --用队列实现栈
在将一个元素 x 插入队列时,为了维护原来的后进先出顺序,需要让 x 插入队列首部。而队列的默认插入顺序是队列尾部,因此在将 x 插入队列尾部之后,需要让除了 x 之外的所有元素出队列,再入队列。
class MyStack {
private Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
public void push(int x) {
queue.add(x);
int cnt = queue.size();
while (cnt-- > 1) {
queue.add(queue.poll());
}
}
public int pop() {
return queue.remove();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}
155. Min Stack (Easy) --最小值栈
思路
我们通过两个栈来实现,栈A用来执行正常的操作,栈B用来保存栈A当前的最小元素,
出栈时,需要进行对比,若栈 A 和栈 B 栈顶元素相同,则同时出栈,此时 B 的栈顶保存的仍为此时栈 A 的最小元素。
class MinStack {
public:
stack<int> data;
stack<int> minstack;
MinStack() {
}
void push(int val) {
data.push(val);
if(minstack.empty() || val <= minstack.top()){
minstack.push(val);
}
}
void pop() {
if(data.top() == minstack.top()){
minstack.pop();
}
data.pop();
}
int top() {
return data.top();
}
int getMin() {
return minstack.top();
}
};
1310. 子数组异或查询
思路
如果数组 arr 的长度为 nn,数组queries 的长度为m (即有 m个查询),则最坏情况下每个查询都需要 O(n)O(n) 的时间计算结果,总时间复杂度是 O(nm),会超出时间限制,因此必须优化。
由于有 m个查询,对于每个查询都要计算结果,因此应该优化每个查询的计算时间。理想情况下,每个查询的计算时间应该为 O(1)。为了将每个查询的计算时间从 O(n) 优化到 O(1),需要计算数组的前缀异或。
class Solution {
public:
vector<int> xorQueries(vector<int>& arr, vector<vector<int>>& queries) {
int n = arr.size();
vector<int> xors(n + 1);
for (int i = 0; i < n; i++) {
xors[i + 1] = xors[i] ^ arr[i];
}
int m = queries.size();
vector<int> ans(m);
for (int i = 0; i < m; i++) {
ans[i] = xors[queries[i][0]] ^ xors[queries[i][1] + 1];
}
return ans;
}
};
PYTHON3:
- 时间复杂度:令 arr 数组长度为 n,qs 数组的长度为 m。创建树状数组复杂度为 O(nlogn);
- 查询的复杂度为 O(mlogn)。整体复杂度为 O((n+m)logn)
- 空间复杂度:O(n)O(n)
class Solution:
def xorQueries(self, arr: List[int], queries: List[List[int]]) -> List[int]:
prexor = list(accumulate([0] + arr, xor))
return [prexor[i] ^ prexor[j + 1] for i, j in queries]
20. Valid Parentheses (Easy) -用栈实现括号匹配
class Solution {
public:
bool isValid(string s) {
if (s.length()%2 == 1)
{
return false;
}
stack<char> st;
for (char& c:s)
{
if (c == '(' || c == '[' || c == '{')
{
st.push(c);
continue;
}
if (c == ')' || c == ']' || c == '}')
{
if (st.empty())
{
return false;
}
else
{
if( (c ==')' && st.top() == '(' ) || ( c == ']' && st.top() == '[' ) || ( c == '}' && st.top()== '{' ) )
{
st.pop();
}
else return false;
}
}
}
return st.empty();
}
};
739. Daily Temperatures (Medium)–数组中元素与下一个比它大的元素之间的距离
Input: [73, 74, 75, 71, 69, 72, 76, 73]
Output: [1, 1, 4, 2, 1, 1, 0, 0]
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> dp(temperatures.size(),0);
deque<int> q;
for(int i = 0 ; i < temperatures.size(); i++){
if(q.empty()){
q.push_back(i);
continue;
}
while(!q.empty()&&temperatures[i] > temperatures[q.back()]){
dp[q.back()] = i-q.back();
q.pop_back();
}
q.push_back(i);
}
return dp;
}
};
503. Next Greater Element II (Medium)–循环数组中比当前元素大的下一个元素
Input: [1,2,1]
Output: [2,-1,2]
Explanation: The first 1's next greater number is 2;
The number 2 can't find next greater number;
The second 1's next greater number needs to search circularly, which is also 2.
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
int n = nums.size();
vector<int> ret(n, -1);
stack<int> stk;
for (int i = 0; i < n * 2 - 1; i++) {
while (!stk.empty() && nums[stk.top()] < nums[i % n]) {
ret[stk.top()] = nums[i % n];
stk.pop();
}
stk.push(i % n);
}
return ret;
}
}