leetcode经典题系列------栈和队列

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

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;
};

题目二:使用栈实现队列
描述:设计一个队列,队列支持如下操作,这些操作的算法复杂度需要是常数级, O(1),队列的内部存储数据的结构为栈,栈的方法只能包括push、top、pop、size、 empty等标准的栈方法
思路:利用2个栈,在 队列调用push的时候,利用临时栈 调换元素次序

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;
};

题目三:包含min函数的栈
描述:设计一个栈,支持如下操作,这些操作的算 法复杂度需要是常数级,O(1)

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


思路:
1.1个变量MIN无法完成记录栈中所有状态下的最小值。
2.栈的每个状态,都需要有一个变量记录最小值。可以用一个最小值栈,记录栈每个状态下的最小值

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;
};

题目四:合法的出栈序列
描述:已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈, 也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法?
比如 3 2 5 4 1 合法 3 1 2 4 5不合法,因为1不可能在2之前

思路:
使用栈和队列模拟入站出站操作
步骤:
1 出站结果存储在队列order中
2 按元素顺序,将元素push进入栈
3 每push一次元素,即检查是否和队列首部元素相同,若相同则弹出队列首元素,弹出战顶元素,直到两元素不同
4 若最终栈为空,说明序列合法,反之不合法

bool check_is_valid_order(std::queue<int>&order)//检查序列存储在队列
{
    std::stack<int>S;//模拟战
    int n=(int)order.size();//n为序列长度,将1-n按顺序入站
    for (int i=1; i<=n; i++) {
        S.push(i); //将i入站
        while (!S.empty()&&S.top()==order.front()) {
            S.pop();
            order.pop();//只要S不空且队列头部与战顶相同,即弹出元素
        }
    }
    if(S.empty()!=false){//如果最终栈不空,则说明序列不合法
        return false;
    }
    return true;
}

题目五 简单计算器


利用状态机的概念,在操作符状态或数字状态下进行相应的处理
测试:
std::string s = "1+(2-3)";
Solution solve;
printf("%d\n", solve.calculate(s));

//字符串转数字
void changeStrToNum(std::string s){
    int number=0;
    for(int i=0;i<s.length();i++){
        if (s[i] >= '0' && s[i] <= '9'){
            number = number * 10 + s[i] - '0';
        }
    }
    printf("%d\n",number);
}
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 compute_flag=0;//1 为准备计算 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--;//这次只是判断,所以应该退格,因为for循环中i会递增
                    break;
                    
                case NUMBER_STATE:
                    if(s[i]>='0'&&s[i]<='9'){//是数字
                        number=number*10+s[i]-'0';//字符转数字
                    }
                    else{//不是数字,说明到操作符了
                        number_stack.push(number);
                        if(compute_flag==1){
                            //可以计算了
                            compute(number_stack, operation_stack);
                        }
                        number=0;//本次数字存储完毕,清零
                        i--;//只是判断下下次应该的状态,所以i退格
                        STATE=OPERATION_STATE;
                    }
                    break;
                case OPERATION_STATE:
                    if(s[i]=='+'||s[i]=='-'){
                        operation_stack.push(s[i]);
                        compute_flag=1;//先标记为可以计算,如果遇到(,在标示为不可计算
                    }
                    else if(s[i]=='('){//左括号,将要为数字模式,并且先不能计算
                        STATE=NUMBER_STATE;
                        compute_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()){
            
            int ii=0;
            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();
    }
};

题目六 :求数组中第K大的数

描述:
已知一个未排序的数组,求这个数组中第K大的数字。 如,array = [3,2,1,5,6,4] , k = 2,return 5
思路:常规我们可以对数组进行排序,然后找出第K大的数,时间复杂度为O(nlogn),有没有更优化的方法,有的,利用二叉堆,时间负责度位nlogk
维护一个K大小的最小堆,堆中元素个数小于K时,新元素直接进入堆;否则,当 堆顶小于新元素时,弹出堆顶,将新元素加入堆。
解释: 由于堆是最小堆,堆顶是堆中最小元素,新元素都会保证比堆顶小(否则新元素
替换堆顶),故堆中K个元素是已扫描的元素里最大的K个;堆顶即为第K大的数。

int findKthLargest(std::vector<int>&nums,int k)
{
    //构建最小堆
    std::priority_queue<int, std::vector<int>, std::greater<int> > Q;
    for (int i=0; i<nums.size(); i++) {
        if(Q.size()<k){
            Q.push(nums[i]);
        }
        else if(nums[i]>Q.top()){
            Q.pop();
            Q.push(nums[i]);
        }
    }
    return Q.top();
}

题目七 寻找中位数

描述:
设计一个数据结构,该数据结构动态维护一组数据,且支持如下操作: 1.添加元素: void addNum(int num),将整型num添加至数据结构中。 2.返回数据的中位数: double findMedian(),返回其维护的数据的中位数。
中位数定义:
1.若数据个数为奇数,中位数是该组数排序后中间的数。[1,2,3] -> 2 2.若数据个数为偶数,中位数是该组数排序后中间的两个数字的平均值。[1,2,3,4] -> 2.5

分析:
最直观的方法:
存储结构使用数组,每次添加元素或查找中位数时对数组排序,
再计算结果。
时间复杂度:
1.比如 若添加元素时排序,addNum复杂度O(n)[插入排序,最好为O(n),最差为n的平方],findMedian复杂度O(1)
2.若查询中位数时排序,addNum复杂度O(1),findMedian复杂度 O(nlogn)
若添加元素或查询中位数是随机的操作,共n次操作,按上述思想,整体复 杂度最佳为O(n^2)
所以应该寻找最优的方法:
思路:利用堆的性质
动态维护一个最大堆与一个最小堆,最大堆存储一半数据,最小堆存储 一般数据,维持最大堆的堆顶比最小堆的堆顶小,并且2个差一个元素
比如 如果最后 最大堆和最小堆个数一样,那么中位数为两个堆顶的平方,如果其中一个比另一个多一个元素,那么中位数就是多一个元素的堆的堆顶
添加元素的时候对堆进行调整有3种情况:
这里 最大堆简写为 A,最小堆简写为 B
情况一:A和B一样多
a 新元素大于最大堆,新元素插入B
b 新元素小于最大堆,新元素插入A
情况二:A比B多1
a 新元素大于A,新元素插入B
b 新元素小于A,A堆顶插入B,A堆顶移除,然后A插入新元素
情况三:A比B少1
a 新元素小于B,新元素插入A
b 新元素大于B,B堆顶插入A,B堆顶移除,然后B插入新元素

class MedianFinder{
public:
    MedianFinder(){}
    void addNum(int num){
        if(big_queue.empty()){
            big_queue.push(num);
            return;
        }
        if (big_queue.size() == small_queue.size()){
            if (num < big_queue.top()){
                big_queue.push(num);
            }
            else{
                small_queue.push(num);
            }
        }
        else if(big_queue.size() > small_queue.size()){
            if (num > big_queue.top()){
                small_queue.push(num);
            }
            else{
                small_queue.push(big_queue.top());
                big_queue.pop();
                big_queue.push(num);
            }
        }
        else if(big_queue.size() < small_queue.size()){
            if (num < small_queue.top()){
                big_queue.push(num);
            }
            else{
                big_queue.push(small_queue.top());
                small_queue.pop();
                small_queue.push(num);
            }
        }
    }
    double findMedian(){
        
        if (big_queue.size() == small_queue.size()){
            return (big_queue.top() + small_queue.top()) / 2;
        }
        else if (big_queue.size() > small_queue.size()){
            return big_queue.top();
        }
        return small_queue.top();
    }
private:
    std::priority_queue<double> big_queue;
    std::priority_queue<double, std::vector<double>,
    std::greater<double> > small_queue;
};

猜你喜欢

转载自www.cnblogs.com/xiaonanxia/p/10142787.html