栈与队列类典型题目的常规思路—Leetcode-thinking_record04

目录

 

预备知识

栈的原理

栈的STL实现 

队列

队列的原理

队列的STL实现

使用队列实现栈(Easy)

总体思路

扫描二维码关注公众号,回复: 9962100 查看本文章

细节设计

代码实现

经验总结

使用栈实现队列(Easy)

总体思路

细节设计

代码实现

经验总结

包含min函数的栈(Easy)

总体思路

用一个变量来记录最小值?(行不通)

用另一个栈,存储各个状态最小值(行得通)

细节设计

代码实现

经验总结

合法的出栈序列

总体思路

细节设计

 代码实现

经验总结

简单的计算器(Hard)

总体思路

细节设计

代码实现

经验总结


预备知识

栈的原理

栈的STL实现 

队列

队列的原理

队列的STL实现

使用队列实现栈(Easy)

LeetCode 225.Implement Stack using Queues

设计一个栈,支持如下操作,这些操作的算法复杂度需要是常数级,O(1),栈的内部存储数据的结构为队列,队列的方法只能包括push、peek(front)、pop、size、empty等标准的队列方法。

总体思路

队列的特定是"先进先出",栈的特定是"先进后出",那么,想用一个"先进先出"的结构去实现一个"先进后出"的结构,就是本题的关键,最理想的做法就是通过一种方式,是将压入队列的数据逆序排列,这样的话,对队列的操作数据的时候,和对栈操作数据的方式就一样了,那该怎样实现呢?最笨的方法,弄俩队列,先把数据压入到一个队列里面,再把这个队列里的内容逐个取出来,再压入到另外一个队列中去,那另外一个队列中的数据排列,就成了逆序排列了。

细节设计

创建一个临时队列,来作为将队列中的元素逆序排列的中转工具。

代码实现

#include <stdio.h>

#include <queue>
class MyStack {
public:
    MyStack() {        
    }
    void push(int x) {
    	std::queue<int> temp_queue;
    	temp_queue.push(x);
    	while(!_data.empty()){
	    	temp_queue.push(_data.front());
	    	_data.pop();
	    }
	    while(!temp_queue.empty()){
   			_data.push(temp_queue.front());
   			temp_queue.pop();
    	}
    }
    int pop() {
        int x = _data.front();
    	_data.pop();
    	return x;
    }
    int top() {
        return _data.front();
    }
    bool empty() {
        return _data.empty();
    }
private:
	std::queue<int> _data;
};

int main(){
	MyStack S;
	S.push(1);
	S.push(2);
	S.push(3);
	S.push(4);
	printf("%d\n", S.top());
	S.pop();
	printf("%d\n", S.top());
	S.push(5);
	printf("%d\n", S.top());	
	return 0;
}

经验总结

  • 两个队列可以实现一个栈,两个栈可以实现一个队列。

使用栈实现队列(Easy)

LeetCode 232.Implement Queue using Stacks

设计一个队列,队列支持如下操作,这些操作的算法复杂度需要是常数级,O(1),队列的内部存储数据的结构为栈,栈的方法只能包括push、top、pop、size、empty等标准的栈方法。

总体思路

用一个数据栈("先进后出")实现一个队列("先进先出"),依然是利用临时栈来调换元素的次序,来实现,这道题和上一道题是类似的。

细节设计

代码实现

#include <stdio.h>

#include <stack>	
class MyQueue {
public:
    MyQueue() {
    }
    void push(int x) {
        std::stack<int> temp_stack;
        while(!_data.empty()){
        	temp_stack.push(_data.top());
        	_data.pop();
        }
        temp_stack.push(x);
        while(!temp_stack.empty()){
        	_data.push(temp_stack.top());
        	temp_stack.pop();
        }
    }
    int pop() {
    	int x = _data.top();
    	_data.pop();
        return x;
    }
    int peek() {
        return _data.top();
    }
    bool empty() {
        return _data.empty();
    }
private:
	std::stack<int> _data;
};

int main(){
	MyQueue Q;
	Q.push(1);
	Q.push(2);
	Q.push(3);
	Q.push(4);
	printf("%d\n", Q.peek());
	Q.pop();
	printf("%d\n", Q.peek());	
	return 0;
}

经验总结

  • 两个队列可以实现一个栈,两个栈可以实现一个队列。

包含min函数的栈(Easy)

LeetCode 155.MinStack

设计一个栈,支持如下操作,这些操作的算法复杂度需要是常数级O(1):

  1. push(x):将元素x压入栈中
  2. pop():弹出(移除)栈顶元素
  3. top():返回栈顶元素
  4. getMin():返回栈内最小元素

总体思路

本题的难点是实现算法复杂度是O(1)级别的,那么,肯定不能用创建临时对象的方式,通过遍历来实现,因为这些操作的复杂度都是O(N)级别的。

用一个变量来记录最小值?(行不通)

那能不能用一个变量来记录栈中的最小值呢?假设创建一个存放最小值的变量min,变量栈中的每个元素,出现比存放在m中的变量更小的值,就把这个值放到min中去,然后,继续遍历,但按照这种方式去遍历的话有个弊端,如下图所示过程:

 当栈中只有一个值"-2"的时候,通过getMin()可以获得栈中的最小值是"-2",这没问题;

开始往栈中填入一个"0"的时候,去拿新填入的值和原来的min值"-2"去比,发现还是"-2"更小,所以min不变;

同理,当再在栈中填入一个"-5"的时候,拿"-5"跟"-2"比,"-5"是更小的,所以,把min值更替为"-5";

但是,当将栈中的"-5"给弹出到栈外之后,如何对min值进行更新呢?这个时候最小值"-2"是被压在栈底的,根本无法在时间复杂度O(1)的前提下用一个变量去获取弹出"-5"后栈中剩下的元素的最小值。可见这种方式是行不通的!

总结一下:

  • 一个变量min是无法完成记录栈中所有状态下的最小值的。
  • 栈的每个状态,都需要有一个变量记录最小值。

用另一个栈,存储各个状态最小值(行得通)

这种思路的核心是,无论我们怎么的push,pop,我们专门新开一个栈,去专门同步(一一对应)的存储栈的每个状态下的最小值。

细节设计

用另一个栈,存储各个状态最小值

代码实现

#include <stdio.h>
#include <stack>

class MinStack {
public:
    MinStack() {
    }
    void push(int x) {
    	_data.push(x);
    	if (_min.empty()){
	    	_min.push(x);
	    }
	    else{
	    	if (x > _min.top()){
	    		x = _min.top();
	    	}
    		_min.push(x);
    	}
    }
    void pop() {
    	_data.pop();
    	_min.pop();
    }
    int top() {
        return _data.top();
    }
    int getMin() {
        return _min.top();
    }
private:
	std::stack<int> _data;
	std::stack<int> _min;
};

int main(){
	MinStack minStack;
	minStack.push(-2);
	printf("top = [%d]\n", minStack.top());
	printf("min = [%d]\n\n", minStack.getMin());	
	minStack.push(0);
	printf("top = [%d]\n", minStack.top());
	printf("min = [%d]\n\n", minStack.getMin());
	minStack.push(-5);
	printf("top = [%d]\n", minStack.top());
	printf("min = [%d]\n\n", minStack.getMin());
	minStack.pop();
	printf("top = [%d]\n", minStack.top());
	printf("min = [%d]\n\n", minStack.getMin());	
	return 0;
}

经验总结

  • 栈的每个状态,都需要有一个变量专门记录。而且,应该专门开一个栈来存放记录栈状态的变量,以达到与被记录栈的同步操作,时间复杂度为常量O(1),开销最小。

合法的出栈序列

poj1363

已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈,也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法?(Medium)

总体思路

使用栈与队列栈去模拟操作,只要模拟的入栈出栈操作,与给定的是一致的,那就证明是合法的。那么难点来了,如何模拟直接入栈出栈是容易的,但如何模拟在栈中停留呢?这就需要队列了,来记录栈中每个时刻的状态了。

细节设计

 代码实现

#include <stdio.h>

#include <stack>
#include <queue>

bool check_is_valid_order(std::queue<int> &order){
	std::stack<int> S;
	int n = order.size();
	for (int i = 1; i <= n; i++){
		S.push(i);
		while(!S.empty() && order.front() == S.top()){
			S.pop();
			order.pop();
		}
	}
	if (!S.empty()){
		return false;
	}
	return true;
}

int main(){
	int n;
	int train;
	scanf("%d", &n);
	while(n){
		scanf("%d", &train);
		while (train){
			std::queue<int> order;
			order.push(train);
			for (int i = 1; i < n; i++){
				scanf("%d", &train);
				order.push(train);
			}
			if (check_is_valid_order(order)){
				printf("Yes\n");
			}
			else{
				printf("No\n");
			}
			scanf("%d", &train);
		}
		printf("\n");
		scanf("%d", &n);
	}
	return 0;
}

经验总结

  • 此题和上一题也是类似的,关键在于记录栈每次操作的状态,这次用队列来记录。而且,此题比上一题难在多了一种栈的状态(在栈中停留)。

简单的计算器(Hard)

LeetCode 224.Basic Calculator

设计一个计算器,输入一个字符串存储的数学表达式,可以计算包括"("、")"、"+"、"-"四种符号的数字表达式,输入的数字表达式字符串保证是合法的。输入的数字表达式中可能存在空格字符。

总体思路

之所以称为简单的计算器,是因为只涉及加减两种运算,和括号操作,那么,就需要开始分析了,在这道题里面需要分两步:1)判断字符是否合法; 2)进行运算。

1)判断合不合法,其实就是看输入的字符串是数字还是规定的加减括号这四种运算符,如果不是,就不是合法的字符,同时,还需要注意的是,要把输入的字符串数字变成整型类型数字才可以计算。

2)在计算的时候,由于有括号的存在,需要实现优先级的操作,这就用到了栈,通过两个栈,一个存数字一个存运算符,再设定一个标记符,来决定当前的运算顺序。

细节设计

1)判断字符是否合法

  2)进行运算,用栈实现优先级运算

 

代码实现

#include <stdio.h>

#include <string>
#include <stack>

class Solution {
public:
    int calculate(std::string s) {
    	static const int STATE_BEGIN = 0;
    	static const int NUMBER_STATE = 1;
    	static const int OPERATION_STATE = 2;
        std::stack<int> number_stack;
        std::stack<char> operation_stack;
        int number = 0;
        int STATE = STATE_BEGIN;
        int compuate_flag = 0;
        for (int i = 0; i < s.length(); i++){
        	if (s[i] == ' '){
	        	continue;
	        }
	        switch(STATE){
        	case STATE_BEGIN:
        		if (s[i] >= '0' && s[i] <= '9'){
        			STATE = NUMBER_STATE;
				}
				else{
					STATE = OPERATION_STATE;
				}
				i--;
				break;
       		case NUMBER_STATE:
			  	if (s[i] >= '0' && s[i] <= '9'){
	  				number = number * 10 + s[i] - '0';
	    		}
	    		else{
	    			number_stack.push(number);
	    			if (compuate_flag == 1){
			    		compute(number_stack, operation_stack);
			    	}
	    			number = 0;
	    			i--;
	    			STATE = OPERATION_STATE;
	       		}
      			break;
  			case OPERATION_STATE:
  				if (s[i] == '+' || s[i] == '-'){
  					operation_stack.push(s[i]);
  					compuate_flag = 1;
			  	}
			  	else if (s[i] == '('){
	  				STATE = NUMBER_STATE;
	  				compuate_flag = 0;
	  			}
	  			else if (s[i] >= '0' && s[i] <= '9'){
			  		STATE = NUMBER_STATE;			  		
			  		i--;
			  	}
			  	else if (s[i] == ')'){
			  		compute(number_stack, operation_stack);
	  			}
  				break;
        	}
        }
        if (number != 0){
        	number_stack.push(number);
       		compute(number_stack, operation_stack);
        }
        if (number == 0 && number_stack.empty()){
        	return 0;
        }
        return number_stack.top();
    }
private:
	void compute(std::stack<int> &number_stack,
				 std::stack<char> &operation_stack){
		if (number_stack.size() < 2){
			return;
		}
		int num2 = number_stack.top();
		number_stack.pop();
		int num1 = number_stack.top();
		number_stack.pop();
		if (operation_stack.top() == '+'){
			number_stack.push(num1 + num2);
		}
		else if(operation_stack.top() == '-'){
			number_stack.push(num1 - num2);
		}
		operation_stack.pop();
	}
};

int main(){	
	std::string s = "1+121 - (14+(5-6) )";
	Solution solve;
	printf("%d\n", solve.calculate(s));
	return 0;
}

经验总结

  • 栈可以模拟计算操作,将数值与运算符分别存放。
  • 判断字符串是数字和操作符的方式可以借鉴。
  • 通过设置标记值来确定优先级。
发布了271 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_17846375/article/details/104667924