大廠算法面試之leetcode精講17.棧

大廠算法面試之leetcode精講17.棧

視頻講解(高效學(xué)習(xí)):點(diǎn)擊學(xué)習(xí)

目錄:

1.開篇介紹

2.時(shí)間空間復(fù)雜度

3.動(dòng)態(tài)規(guī)劃

4.貪心

5.二分查找

6.深度優(yōu)先&廣度優(yōu)先

7.雙指針

8.滑動(dòng)窗口

9.位運(yùn)算

10.遞歸&分治

11剪枝&回溯

12.堆

13.單調(diào)棧

14.排序算法

15.鏈表

16.set&map

17.棧

18.隊(duì)列

19.數(shù)組

20.字符串

21.樹

22.字典樹

23.并查集

24.其他類型題

  • Stack的特點(diǎn):先進(jìn)后出(FILO)

  • 使用場(chǎng)景:十進(jìn)制轉(zhuǎn)2進(jìn)制 函數(shù)調(diào)用堆棧

  • js里沒有棧,但是可以用數(shù)組模擬

    42/2 42%2=0
    21/2 21%2=1
    10/2 10%2=0
    5/2   5%2=1
    2/2   2%2=0
    1/2   1%2=1
    stack: [0,1,0,1,0,1]
    res:    1 0 1 0 1 0
    
    fn1(){
      fn2()
    }
    fn2(){
        fn3()
      }
    fn3(){}
    fn1()
    
    stack:[fn1,fn2,fn3]
    
  • 棧的時(shí)間復(fù)雜度:入棧和出棧O(1),查找O(n)
ds_24

20. 有效的括號(hào) (easy)

方法1.棧
ds_25
  • 思路:首先如果字符串能組成有效的括號(hào),則長度一定是偶數(shù),我們可以遍歷字符串,遇到左括號(hào)則暫存,期待后面有右括號(hào)可以和它匹配,如果遇到右括號(hào)則檢查是否能和最晚暫存的做括號(hào)匹配。這就和棧這種數(shù)據(jù)結(jié)構(gòu)先進(jìn)后出的特性相吻合了。所以我們可以準(zhǔn)備一個(gè)棧存放括號(hào)對(duì),遍歷字符串的時(shí)候,如果遇到左括號(hào)入棧,遇到右括號(hào)則判斷右括號(hào)是否能和棧頂元素匹配,在循環(huán)結(jié)束的時(shí)候還要判斷棧是否為空,如果不為空,則不是有效括號(hào)匹配的字符串
  • 復(fù)雜度分析:時(shí)間復(fù)雜度O(n),空間復(fù)雜度O(n),n為字符串的長度

js:

var isValid = function(s) {
    const n = s.length;
    if (n % 2 === 1) {//如果字符串能組成有效的括號(hào),則長度一定是偶數(shù)
        return false;
    }
    const pairs = new Map([//用棧存儲(chǔ)括號(hào)對(duì)
        [')', '('],
        [']', '['],
        ['}', '{']
    ]);
    const stk = [];
    for (let ch of s){//循環(huán)字符串
        if (pairs.has(ch)) {
            //遇到右括號(hào)則判斷右括號(hào)是否能和棧頂元素匹配
            if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
                return false;
            }
            stk.pop();
        } else {
            stk.push(ch);//如果遇到左括號(hào)入棧
        }
    };
    return !stk.length;//循環(huán)結(jié)束的時(shí)候還要判斷棧是否為空
};

Java:

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1) {
            return false;
        }

        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Deque<Character> stack = new LinkedList<Character>();
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else {
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

232. 用棧實(shí)現(xiàn)隊(duì)列 (easy)

方法1.棧

動(dòng)畫過大,點(diǎn)擊查看

  • 思路:這是一道模擬題,不涉及到具體算法,考察的就是對(duì)棧和隊(duì)列的掌握程度。使用棧來模式隊(duì)列的行為,如果僅僅用一個(gè)棧,是一定不行的,所以需要兩個(gè)棧一個(gè)輸入棧,一個(gè)輸出棧,這里要注意輸入棧和輸出棧的關(guān)系。在push數(shù)據(jù)的時(shí)候,只要數(shù)據(jù)放進(jìn)輸入棧就好,但在pop的時(shí)候,操作就復(fù)雜一些,輸出棧如果為空,就把進(jìn)棧數(shù)據(jù)全部導(dǎo)入進(jìn)來(注意是全部導(dǎo)入),再從出棧彈出數(shù)據(jù),如果輸出棧不為空,則直接從出棧彈出數(shù)據(jù)就可以了。最后如果進(jìn)棧和出棧都為空的話,說明模擬的隊(duì)列為空了。
  • 復(fù)雜度分析:push時(shí)間復(fù)雜度O(1),pop時(shí)間復(fù)雜度為O(n) ,因?yàn)閜op的時(shí)候,輸出棧為空,則把輸入棧所有的元素加入輸出棧??臻g復(fù)雜度O(n),兩個(gè)棧空間

js:

var MyQueue = function() {
  //準(zhǔn)備兩個(gè)棧
   this.stack1 = [];
   this.stack2 = [];
};

MyQueue.prototype.push = function(x) {//push的時(shí)候加入輸入棧
   this.stack1.push(x);
};

MyQueue.prototype.pop = function() {
   const size = this.stack2.length;
   if(size) {//push的時(shí)候判斷輸出棧是否為空
       return this.stack2.pop();//不為空則輸出棧出棧
   }
   while(this.stack1.length) {//輸出棧為空,則把輸入棧所有的元素加入輸出棧
       this.stack2.push(this.stack1.pop());
   }
   return this.stack2.pop();
};

MyQueue.prototype.peek = function() {
   const x = this.pop();//查看隊(duì)頭的元素 復(fù)用pop方法,然后在讓元素push進(jìn)輸出棧
   this.stack2.push(x);
   return x;
};

MyQueue.prototype.empty = function() {
   return !this.stack1.length && !this.stack2.length
};

Java:

class MyQueue {

    Stack<Integer> stack1;
    Stack<Integer> stack2;

    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void push(int x) {
        stack1.push(x);
    }
    
    public int pop() {    
        dumpStack1();
        return stack2.pop();
    }
    
    public int peek() {
        dumpStack1();
        return stack2.peek();
    }
    
    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }

    private void dumpStack1(){
        if (stack2.isEmpty()){
            while (!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
    }
}

155. 最小棧 (easy)

ds_145
  • 思路:定義兩個(gè)棧stack和min_stack,stack正常push,min_stack只會(huì)push需要入棧和棧頂中較小的元素。getMin返回min_stack棧頂元素,top返回stack棧頂元素。
  • 復(fù)雜度:所有操作的時(shí)間復(fù)雜度是O(1)

js:

var MinStack = function () {
    this.stack = [];
    this.min_stack = [Infinity];
};

//stack正常push,min_stack只會(huì)push需要入棧和棧頂中較小的元素
MinStack.prototype.push = function (x) {
    this.stack.push(x);
    this.min_stack.push(Math.min(this.min_stack[this.min_stack.length - 1], x));
};

//stack正常pop,min_stack正常pop
MinStack.prototype.pop = function () {
    this.stack.pop();
    this.min_stack.pop();
};

//返回stack棧頂元素
MinStack.prototype.top = function () {
    return this.stack[this.stack.length - 1];
};

//返回min_stack棧頂元素
MinStack.prototype.getMin = function () {
    return this.min_stack[this.min_stack.length - 1];
};


java:

class MinStack {
  Deque<Integer> stack;
  Deque<Integer> minStack;

  public MinStack() {
      stack = new LinkedList<Integer>();
      minStack = new LinkedList<Integer>();
      minStack.push(Integer.MAX_VALUE);
  }
  
  public void push(int x) {
      stack.push(x);
      minStack.push(Math.min(minStack.peek(), x));
  }
  
  public void pop() {
      stack.pop();
      minStack.pop();
  }
  
  public int top() {
      return stack.peek();
  }
  
  public int getMin() {
      return minStack.peek();
  }
}

946. 驗(yàn)證棧序列 (medium)

動(dòng)畫過大,點(diǎn)擊查看

  • 思路:用棧模擬出棧入棧的過程,當(dāng)popped中index指向的位置的元素和stack棧頂?shù)脑匾恢聲r(shí),出棧 并且 index++,最后判斷stack是否為空
  • 復(fù)雜度:時(shí)間復(fù)雜度O(n),pushed中的元素入棧出棧一次,空間復(fù)雜度O(n),棧的大小

js:

const validateStackSequences = (pushed, popped) => {
    const stack = [];//用棧模擬出棧入棧的過程
    let index = 0;
    const len = pushed.length;
    for (let i = 0; i < len; i++) {
        stack.push(pushed[i]);
        //當(dāng)popped中index指向的位置的元素和stack棧頂?shù)脑匾恢聲r(shí),出棧 并且 index++
        while (popped[index] !== undefined && popped[index] === stack[stack.length - 1]) {
            stack.pop();
            index++;
        }
    }
    return !stack.length;//最后判斷stack是否為空
};

java:

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        if(pushed == null){
            return true;
        }
        Stack<Integer> stack = new Stack<>();
        int index = 0;
        for(int i=0;i<pushed.length;i++){
            stack.push(pushed[i]);
            while(!stack.isEmpty() && index < popped.length && popped[index] == stack.peek()){
                int pop = stack.pop();
                index++;
            }
        }
        return stack.isEmpty();
    }
}



445. 兩數(shù)相加 II (medium)

ds_180
  • 思路:將兩個(gè)鏈表的節(jié)點(diǎn)都推入棧中,然后不斷出棧,計(jì)算每個(gè)位置的值和進(jìn)位,串連成一個(gè)新的鏈表
  • 復(fù)雜度:時(shí)間復(fù)雜度O(max(m,n)),m,n是兩個(gè)鏈表的長度,空間復(fù)雜度O(m+n)

js:

var addTwoNumbers = function(l1, l2) {
    const stack1 = [];
    const stack2 = [];
    while (l1 || l2) {//兩鏈表入棧
        if (l1) {
            stack1.push(l1.val);
            l1 = l1.next;
        }
        if (l2) {
            stack2.push(l2.val);
            l2 = l2.next;
        }
    }
    let carry = 0;
    let ansList = null;
    while (stack1.length || stack2.length || carry !== 0) {//不斷出棧
        const s1 = stack1.length ? stack1.pop() : 0;
        const s2 = stack2.length ? stack2.pop() : 0;
        let val = s1 + s2 + carry;
        carry = parseInt(val / 10);//計(jì)算進(jìn)位
        val = val % 10;//計(jì)算當(dāng)前節(jié)點(diǎn)的值
        const curNode = new ListNode(val);
        curNode.next = ansList;//向鏈表前插入新節(jié)點(diǎn)
        ansList = curNode;//重新賦值ansList
    }
    return ansList;
};

java:

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Deque<Integer> stack1 = new LinkedList<Integer>();
        Deque<Integer> stack2 = new LinkedList<Integer>();
        while (l1 != null) {
            stack1.push(l1.val);
            l1 = l1.next;
        }
        while (l2 != null) {
            stack2.push(l2.val);
            l2 = l2.next;
        }
        int carry = 0;
        ListNode ansList = null;
        while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
            int s1 = stack1.isEmpty() ? 0 : stack1.pop();
            int s2 = stack2.isEmpty() ? 0 : stack2.pop();
            int val = s1 + s2 + carry;
            carry = val / 10;
            val %= 10;
            ListNode curNode = new ListNode(val);
            curNode.next = ansList;
            ansList = curNode;
        }
        return ansList;
    }
}

682. 棒球比賽 (easy)

  • 復(fù)雜度:時(shí)間復(fù)雜度O(n),空間復(fù)雜度O(n)

js:

let calPoints = function(ops) {
    let res = [];
    for(let i = 0; i < ops.length; i++){
        switch(ops[i]){
            case "C":
                res.pop();
                break;
            case "D":
                res.push(+res[res.length - 1] * 2);
                break;
            case "+":
                res.push(+res[res.length - 1] + +res[res.length - 2]);
                break;
            default:
                res.push(+ops[i]);
        }
    }
    return res.reduce((i, j) => i + j);
};

java:

class Solution {
    public int calPoints(String[] ops) {
        Stack<Integer> stack = new Stack();

        for(String op : ops) {
            if (op.equals("+")) {
                int top = stack.pop();
                int newtop = top + stack.peek();
                stack.push(top);
                stack.push(newtop);
            } else if (op.equals("C")) {
                stack.pop();
            } else if (op.equals("D")) {
                stack.push(2 * stack.peek());
            } else {
                stack.push(Integer.valueOf(op));
            }
        }

        int ans = 0;
        for(int score : stack) ans += score;
        return ans;
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容