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

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