剑指LeetCode面试算法:栈、队列、堆

一、基础知识
栈:后进先出的线性表;STL方法包含push(x),pop(),top(),empty()
队列:先进先出的线性表;STL方法包含push(x),pop(),top(),empty()

二、目录

  1. 使用队列实现栈

三、 解题

1.使用队列实现栈

这题在剑指offer中是面试题9(用两个队列实现栈),leetcode229。使用队列实现栈。
对于1,2,3,4,5这样一个序列,
用栈存储,从上至下为5,4,3,2,1。5为栈顶,使用top返回5,push(x)则x变为栈顶;
用队列存储,从上至下为5,4,3,2,1。1为队头,使用top返回1,push(x)x插入尾部,1还是队头;
思路:
使用两个队列,queue1队列依次push 1 2 3,这时1为队头,使用pop弹出的是1,不符合栈后进先出的要求,于是先将1,2 push进queue2,query1剩余3,此时pop弹出为3,实现了弹出栈顶元素的需求。如需要弹出2,则重复步骤,先将1 push进queue1,直到queue2剩下最后一个元素即2,此时弹出可得到2

待写
2.使用栈实现队列

这题在剑指offer中是面试题9(用两个栈实现队列),leetcode232。
思路:使用两个栈,push值到stack1中,front时将stack1的栈顶push进stack2中,原先stack1中的栈顶就变成stack2的栈底,原先stack1的栈底(队首)变为stack2栈顶,用top可得到stack2的栈顶,也就是队首元素。

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

class MyQueue
{
private:
    std::stack<int> stack1;
    std::stack<int> stack2;
public:
    void push(int x){
        stack1.push(x);
    }
    int front(){    // 实现队列的front方法,获得首元素
        if(stack2.empty()){
            // 每次将stack1栈top元素push进stack2栈, 最后一次push值为栈的最底端, 也就是队头
            while (!stack1.empty()) 
            {
                int stacktop = stack1.top();
                stack2.push(stacktop);
                stack1.pop();
            }
        }
        int res = stack2.top();
        return res;
    }
    void pop(){
        stack2.pop();
    }
};

int main(){
    MyQueue que;
    que.push(1);
    que.push(2);
    que.push(3);
    que.push(4);
    printf("front: %d ", que.front());
    que.pop();
    printf("front: %d ", que.front());
    que.push(5);
    printf("front: %d ", que.front());
}
3.包含Min函数的栈

这题在剑指offer中是面试题9(用两个栈实现队列),leetcode155。
设计一个栈,包含push,pop,top,getMin,要求算法复杂度为O(1)
思路:由于要求复杂度为常数级,故不能采取遍历的方式记录最小值;故使用一个最小值栈,数据栈中每push一个值,直接push就好;对应最小值栈需要将新值和栈顶元素进行进行比较,谁小就push谁。
在这里插入图片描述

class MinStack
{
private:
    std::stack<int> data_stack;
    std::stack<int> min_stack;
public:
    void push(int x){
        data_stack.push(x);
        if(min_stack.empty()){  // 如果最小栈是空的,则直接push
            min_stack.push(x);
        }else{
            if(x > min_stack.top())     // push进x和minstack中较小的那个
                x = min_stack.top();
            min_stack.push(x);
        }
    }
    void pop(){
        data_stack.pop();
        min_stack.pop();
    }
    int top(){
        return data_stack.top();
    }
    int getMin(){
        return min_stack.top();
    }
};
4.出栈序列是否合法

已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈,也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法?
这题在剑指offer中是面试题31。

思路:
1.出栈结果存储在队列 order中(也可以用辅助栈)
2.按元素顺序,将元素push进入栈,
3.每push1个元素,即检查是否与队列首部元素相同,若相同则弹出队列首元素,弹出栈顶元素,直到两元素不同结束
4.若最终栈为空,说明序列合法,否则不合法
整体复杂度O(n)

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

class CheckOrderValid
{
public:
    bool checkOrderIsValid(std::queue<int> &order){
        std::stack<int> s;
        int n = order.size();
        for(int i = 1; i <= n; i++){
            s.push(i);
            // 每push 1个元素,即检查是否与队列首部元素相同,若相同则弹出队列首元素,弹出栈顶元素,直到两元素不同结束
            while (!s.empty() && s.top()==order.front())
            {
                s.pop();
                order.pop();
            }
        }
        if(!s.empty())  // s不空说明还有元素,即序列不合法
            return false;
        return true;
    }
};

int main(){
    CheckOrderValid valid;
    std::queue<int> order;
    // order.push(6);
    order.push(1);
    order.push(2);
    order.push(3);
    order.push(4);
    order.push(5);
    if(valid.checkOrderIsValid(order)){
        printf("yes/n");
    }else{
        printf("No/n");
    }
    return 0;
}
5.把字符串转换成整数

把字符串转换成整数
思路:看题目似乎很简单,但是如果考虑清楚输入的可能性,代码量还是挺多的。

#include <cstdio>

long long StrToIntCore(const char* str, bool minus);

enum Status {kValid = 0, kInvalid};
int g_nStatus = kValid;

int StrToInt(const char* str)
{
    g_nStatus = kInvalid;
    long long num = 0;

    if(str != nullptr && *str != '\0') 
    {
        bool minus = false;
        if(*str == '+')
            str ++;
        else if(*str == '-') 
        {
            str ++;
            minus = true;
        }

        if(*str != '\0') 
            num = StrToIntCore(str, minus);
    }

    return (int)num;
}

long long StrToIntCore(const char* digit, bool minus)
{
    long long num = 0;

    while(*digit != '\0') 
    {
        if(*digit >= '0' && *digit <= '9') 
        {
            int flag = minus ? -1 : 1;
            num = num * 10 + flag * (*digit - '0');

            if((!minus && num > 0x7FFFFFFF) 
                || (minus && num < (signed int)0x80000000))
            {
                num = 0;
                break;
            }

            digit++;
        }
        else 
        {
            num = 0;
            break;
        }
    }

    if(*digit == '\0') 
        g_nStatus = kValid;

    return num;
}

// ====================测试代码====================
void Test(const char* string)
{
    int result = StrToInt(string);
    if(result == 0 && g_nStatus == kInvalid)
        printf("the input %s is invalid.\n", string);
    else
        printf("number for %s is: %d.\n", string, result);
}

int main(int argc, char* argv[])
{
    Test(nullptr);

    Test("");

    Test("123");

    Test("+123");
    
    Test("-123");

    Test("1a33");

    Test("+0");

    Test("-0");

    //有效的最大正整数, 0x7FFFFFFF
    Test("+2147483647");    

    Test("-2147483647");

    Test("+2147483648");

    //有效的最小负整数, 0x80000000
    Test("-2147483648");    

    Test("+2147483649");

    Test("-2147483649");

    Test("+");

    Test("-");

    return 0;
}
6.求数组中第K大的数(二叉堆)

一个未排序的数组,求其中第k大的值。 leetcode215题
一个数组中求第k大,只要将前k大的值存入堆中,最小堆会自动调整,始终保证堆顶为堆中最小值,使用top即求得第k大值。

#include <stdio.h>
#include <queue>
主要是std::priority_queue<int, std::vector<int>, std::greater<int> > Q;最小堆


class ArrayKthHeap
{
public:
    int findKthLargest(std::vector<int>& nums, int k){
        // 数组中第k大元素,则只需要留下数组中前k大元素,最小堆的性质可使得较大的数成为堆顶,直接用pop可求得
        std::priority_queue<int, std::vector<int>, std::greater<int> > Q;    // 会自动调整堆
        for (int i = 0; i < nums.size(); i++)
        {
            if(Q.size() < k){     // 如果堆中个数小于k,则直接push进堆中
                Q.push(nums[i]);
            }else if(Q.top() < nums[i]){    // 如果堆顶小于元素,则push进该元素,原堆顶删除
                Q.pop();
                Q.push(nums[i]);
            }
        }
        return Q.top();
    }
};


int main(){
    std::vector<int> nums;
    nums.push_back(3);
    nums.push_back(2);
    nums.push_back(1);
    nums.push_back(5);
    nums.push_back(6);
    nums.push_back(4);
    ArrayKthHeap smailheap;
    int kbig = smailheap.findKthLargest(nums, 2);
    printf("%d ", kbig);
    return 0;
}
7.求动态数组中位数

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

思路:
最大堆的堆顶是堆中最大的,最小堆的堆顶是堆中最小的,最大堆的堆顶<最小堆的堆顶
动态添加数据到最大最小堆中:需要两个堆大小差距不超过1,新增的数加入到较少的堆中
堆顶相加/2即为中位数
插入时间复杂度nlogn,得到中位数复杂度n
在这里插入图片描述

#include <stdio.h>
#include <queue>

class FindMiddleNum
{
private:
    std::priority_queue<int, std::vector<int>, std::greater<int> > big_queue;
    std::priority_queue<int, std::vector<int>, std::less<int> > small_queue;
public:
    /*
        最大堆的堆顶是堆中最大的,最小堆的堆顶是堆中最小的,最大堆的堆顶<最小堆的堆顶
        动态添加数据到最大最小堆中:需要两个堆大小差距不超过1,新增的数加入到较少的堆中
        堆顶相加/2即为中位数
    */
    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()){  // big_queue.top()要保持比small_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();
        }else{
            return small_queue.top();
        }
    }
};

int main(){
    FindMiddleNum finder;
    int test[] = {6,10,1,7,99,4,33};
    for (int i = 0; i < 7 ; i++)
    {
        finder.addNum(test[i]);
        printf("%lf ", finder.findMedian());
    }
    return 0;    
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章