LeetCode刷題之棧、隊(duì)列

棧(Stack)

又名堆棧,它是一種重要的數(shù)據(jù)結(jié)構(gòu)。從數(shù)據(jù)結(jié)構(gòu)角度看,棧也是線性表,其特殊性在于棧的基本操作是線性表操作的子集,它是操作受限的線性表,因此,可稱為限定性的數(shù)據(jù)結(jié)構(gòu)。限定它僅在表尾進(jìn)行插入或刪除操作。表尾稱為棧頂,相應(yīng)地,表頭稱為棧底。棧的基本操作除了在棧頂進(jìn)行插入和刪除外,還有棧的初始化,判空以及取棧頂元素等。

隊(duì)列(Queue)

是一種先進(jìn)先出(FIFO,F(xiàn)irst-In-First-Out)的線性表。
在具體應(yīng)用中通常用鏈表或者數(shù)組來實(shí)現(xiàn)。隊(duì)列只允許在后端(稱為 rear)進(jìn)行插入操作,在前端(稱為 front)進(jìn)行刪除操作。
隊(duì)列的操作方式和堆棧類似,唯一的區(qū)別在于隊(duì)列只允許新數(shù)據(jù)在后端進(jìn)行添加。
Queue接口與List、Set同一級(jí)別,都是繼承了Collection接口。LinkedList實(shí)現(xiàn)了Queue接 口。
隊(duì)列常用的方法有:add、remove、element、offer、poll、peek、put、take,方法解釋:

  • add
    增加一個(gè)元索 如果隊(duì)列已滿,則拋出一個(gè)IIIegaISlabEepeplian異常
  • remove
    移除并返回隊(duì)列頭部的元素 如果隊(duì)列為空,則拋出一個(gè)NoSuchElementException異常
  • element
    返回隊(duì)列頭部的元素 如果隊(duì)列為空,則拋出一NoSuchElementException異常
  • offer 添加一個(gè)元素并返回true 如果隊(duì)列已滿,則返回false
  • poll 移除并返問隊(duì)列頭部的元素 如果隊(duì)列為空,則返回null
  • peek 返回隊(duì)列頭部的元素 如果隊(duì)列為空,則返回null
  • put 添加一個(gè)元素 如果隊(duì)列滿,則阻塞
  • take 移除并返回隊(duì)列頭部的元素

面試題 03.04. 化棧為隊(duì)

實(shí)現(xiàn)一個(gè)MyQueue類,該類用兩個(gè)棧來實(shí)現(xiàn)一個(gè)隊(duì)列。

示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false

題解:
/**
     * 實(shí)現(xiàn)思路:
     * 創(chuàng)建2個(gè)棧,棧1只負(fù)責(zé)入棧,棧2只負(fù)責(zé)出棧
     * 存入幾個(gè)元素到棧1后,如果此時(shí)棧2為空,則將棧1中的元素轉(zhuǎn)移到棧2,從棧2出棧
     * 如果此時(shí)棧2中不為空,則直接從棧2出棧
     */
class MyQueue {
        Stack<Integer> stack1;  //stack1只負(fù)責(zé)入棧
        Stack<Integer> stack2;  //stack2只負(fù)責(zé)出棧

        /** Initialize your data structure here. */
        public MyQueue() {
            stack1 = new Stack<>();
            stack2 = new Stack<>();
        }

        /** Push element x to the back of queue. */
        public void push(int x) {
            stack1.push(x);
        }

        /** Removes the element from in front of queue and returns that element. */
        public int pop() {
            if (!stack2.isEmpty()) {
                return stack2.pop();
            } else {
                //將stack1中所有元素轉(zhuǎn)移到stack2
                while (!stack1.isEmpty()) {
                    stack2.push(stack1.pop());
                }
                return stack2.pop();
            }
        }

        /** Get the front element. */
        public int peek() {
            if (!stack2.isEmpty()) {
                return stack2.peek();
            } else {
                //將stack1中所有元素轉(zhuǎn)移到stack2
                while (!stack1.isEmpty()) {
                    stack2.push(stack1.pop());
                }
                return stack2.peek();
            }
        }

        /** Returns whether the queue is empty. */
        public boolean empty() {
            if (stack1.isEmpty() && stack2.isEmpty()) {
                return true;
            } else {
                return false;
            }
        }
    }

1047. 刪除字符串中的所有相鄰重復(fù)項(xiàng)

給出由小寫字母組成的字符串 S,重復(fù)項(xiàng)刪除操作會(huì)選擇兩個(gè)相鄰且相同的字母,并刪除它們。
在 S 上反復(fù)執(zhí)行重復(fù)項(xiàng)刪除操作,直到無法繼續(xù)刪除。
在完成所有重復(fù)項(xiàng)刪除操作后返回最終的字符串。答案保證唯一。

示例:

輸入:"abbaca"
輸出:"ca"
解釋:
例如,在 "abbaca" 中,我們可以刪除 "bb" 由于兩字母相鄰且相同,這是此時(shí)唯一可以執(zhí)行刪除操作的重復(fù)項(xiàng)。之后我們得到字符串 "aaca",其中又只有 "aa" 可以執(zhí)行重復(fù)項(xiàng)刪除操作,所以最后的字符串為 "ca"。

題解:
/**
     * 用棧的結(jié)構(gòu)來維護(hù)沒有重復(fù)項(xiàng)的字母序列:
     * 循環(huán)從字符串中取出一個(gè)字符,與棧頂字符對(duì)比,如果不同,則放入棧;如果相同,則兩個(gè)字符消掉,
     * 即刪除字符串中該字符,同時(shí)刪除棧中該字符,最終棧中剩下來的字符數(shù)組為最終字符串
     *
     */
    class Solution {

        public String removeDuplicates(String S) {
            Stack<Character> stack = new Stack();
            char[] chars = S.toCharArray();

            for (char ch : chars) {
                if (stack.isEmpty()) {  //如果棧為空,則直接入棧
                    stack.push(ch);
                } else {  //如果棧不為空,則判斷當(dāng)前字符與棧頂字符是否相同,相同就從棧中彈出棧
                    if (ch != stack.peek()) {
                        stack.push(ch);
                    } else {
                        stack.pop();
                    }
                }
            }

            StringBuilder stringBuilder = new StringBuilder();
            for (Character ch : stack) {
                stringBuilder.append(ch);
            }
            return stringBuilder.toString();
        }
    }

682. 棒球比賽

你現(xiàn)在是棒球比賽記錄員。
給定一個(gè)字符串列表,每個(gè)字符串可以是以下四種類型之一:
1.整數(shù)(一輪的得分):直接表示您在本輪中獲得的積分?jǐn)?shù)。

  1. "+"(一輪的得分):表示本輪獲得的得分是前兩輪有效 回合得分的總和。
  2. "D"(一輪的得分):表示本輪獲得的得分是前一輪有效 回合得分的兩倍。
  3. "C"(一個(gè)操作,這不是一個(gè)回合的分?jǐn)?shù)):表示您獲得的最后一個(gè)有效 回合的分?jǐn)?shù)是無效的,應(yīng)該被移除。

每一輪的操作都是永久性的,可能會(huì)對(duì)前一輪和后一輪產(chǎn)生影響。
你需要返回你在所有回合中得分的總和。

示例 1:

輸入: ["5","2","C","D","+"]
輸出: 30
解釋:
第1輪:你可以得到5分。總和是:5。
第2輪:你可以得到2分??偤褪牵?。
操作1:第2輪的數(shù)據(jù)無效。總和是:5。
第3輪:你可以得到10分(第2輪的數(shù)據(jù)已被刪除)??倲?shù)是:15。
第4輪:你可以得到5 + 10 = 15分??倲?shù)是:30。

題解:

/**
     * 思路:
     * 用Stack來存儲(chǔ)每一個(gè)輪的得分,最后累加得分。
     * 根據(jù)每種字符規(guī)則,處理當(dāng)前輪的得分
     */
    class Solution {

        public int calPoints(String[] ops) {
            //stack為Integer類型,只存儲(chǔ)每輪分?jǐn)?shù)數(shù)值
            Stack<Integer> stack = new Stack();
            for (String op : ops) {
                if (op.equals("+")) {
                    int last = stack.pop();  //上一輪分?jǐn)?shù)
                    int cur = stack.peek() + last;  //當(dāng)前輪分?jǐn)?shù)
                    stack.push(last);
                    stack.push(cur);

                } else if (op.equals("C")) {
                    //移除上一輪得分
                    stack.pop();
                } else if (op.equals("D")) {
                    //當(dāng)前分?jǐn)?shù)為上一輪的2倍
                    int score = stack.peek()*2;
                    stack.push(score);
                } else {
                    //當(dāng)前為數(shù)字,直接加入
                    stack.push(Integer.valueOf(op));
                }
            }

            int totalScore = 0;
            for (Integer score : stack) {
                totalScore += score;
            }
            return totalScore;
        }
    }

933. 最近的請(qǐng)求次數(shù)

寫一個(gè) RecentCounter 類來計(jì)算最近的請(qǐng)求。
它只有一個(gè)方法:ping(int t),其中 t 代表以毫秒為單位的某個(gè)時(shí)間。
返回從 3000 毫秒前到現(xiàn)在的 ping 數(shù)。
任何處于 [t - 3000, t] 時(shí)間范圍之內(nèi)的 ping 都將會(huì)被計(jì)算在內(nèi),包括當(dāng)前(指 t 時(shí)刻)的 ping。
保證每次對(duì) ping 的調(diào)用都使用比之前更大的 t 值。

示例:

輸入:inputs = ["RecentCounter","ping","ping","ping","ping"], inputs = [[],[1],[100],[3001],[3002]]
輸出:[null,1,2,3,3]

題解:
class RecentCounter {
        Queue<Integer> queue;

        public RecentCounter() {
            queue = new LinkedList<>();
        }

        public int ping(int t) {
            queue.add(t); //將當(dāng)前時(shí)間入列

            //遍歷隊(duì)列,判斷當(dāng)前元素是否是當(dāng)前時(shí)間前3000的值,如果是,則從隊(duì)列中移除
            while (queue.peek() < t - 3000) {
                queue.poll();
            }

            return queue.size();
        }
    }

劍指 Offer 59 - I. 滑動(dòng)窗口的最大值

給定一個(gè)數(shù)組 nums 和滑動(dòng)窗口的大小 k,請(qǐng)找出所有滑動(dòng)窗口里的最大值。

示例:

輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:

滑動(dòng)窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

題解:
class Solution {
    /**
     * 思路:
     *  從nums數(shù)組中取出前k個(gè)數(shù),存入隊(duì)列中
     *  獲取隊(duì)列的最大值存儲(chǔ)到集合中
     *  然后隊(duì)列移除頭部,在尾部添加第k+1個(gè)數(shù),獲取隊(duì)列中最大值存儲(chǔ)到集合中
     *
     * @param nums
     * @param k
     * @return
     */
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 0) {
            //如果數(shù)組為空,則返回空數(shù)組
            return new int[0];
        }
        int[] result = new int[nums.length-k+1];  //總共需要輸出的數(shù)組

        Queue<Integer> queue = new LinkedList<>();

        //首次添加前k個(gè)數(shù)到隊(duì)列中作為初始隊(duì)列
        for (int j=0;j<k;j++) {
            queue.add(nums[j]);
        }

        //遍歷隊(duì)列獲取最大值
        int maxNum = queue.peek();
        for (Integer num : queue) {
            if (num > maxNum) {
                maxNum = num;
            }
        }
        result[0] = maxNum;

        for (int i=0;i<nums.length-k;i++) {

            queue.poll();  //移除隊(duì)列頭部
            queue.add(nums[i+k]);  //添加數(shù)組下一個(gè)值到隊(duì)列尾部

            //遍歷隊(duì)列獲取最大值
            int max = queue.peek();
            for (Integer num : queue) {
                if (num > max) {
                    max = num;
                }
            }
            result[i+1] = max;
        }

        return result;
    }
}

有效的括號(hào)

給定一個(gè)只包括 '(',')','{','}','[',']' 的字符串 s ,判斷字符串是否有效。

有效字符串需滿足:

左括號(hào)必須用相同類型的右括號(hào)閉合。
左括號(hào)必須以正確的順序閉合。
每個(gè)右括號(hào)都有一個(gè)對(duì)應(yīng)的相同類型的左括號(hào)。

題解:
class Solution {
    public static boolean isValid(String s) {
        //思路:1.在hashmap中存入左右括號(hào)對(duì)應(yīng)分別作為鍵值對(duì)
        //遍歷字符,如果為左括號(hào),就在棧中存入右括號(hào),看后面的字符是否有該右括號(hào)來消掉
        //3.用stack來存,先存就后取,后存就要先取
        Stack<Character> stack = new Stack();
        HashMap<Character,Character> map = new HashMap();
        map.put('(', ')');
        map.put('[', ']');
        map.put('{', '}');
        for(Character ch : s.toCharArray()) {
            if(map.containsKey(ch)) { //當(dāng)前字符為左括號(hào)
                stack.push(map.get(ch));
            } else {  //當(dāng)前字符為右括號(hào),那棧中最頂部必然要與之相同才行
                if(stack.isEmpty() || stack.pop() != ch) {
                    return false;
                }
            }
        }

        //最后看stack還有沒有沒有正確關(guān)閉括號(hào)
        return stack.isEmpty();
    }
}

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

請(qǐng)你僅使用兩個(gè)隊(duì)列實(shí)現(xiàn)一個(gè)后入先出(LIFO)的棧,并支持普通棧的全部四種操作(push、top、pop 和 empty)。

實(shí)現(xiàn) MyStack 類:

void push(int x) 將元素 x 壓入棧頂。
int pop() 移除并返回棧頂元素。
int top() 返回棧頂元素。
boolean empty() 如果棧是空的,返回 true ;否則,返回 false 。

題解:
/**
     * 用兩個(gè)隊(duì)列實(shí)現(xiàn)棧
     * 這里需要實(shí)現(xiàn)一個(gè)隊(duì)列元素的倒序,每次從queue2入列,從queue1中取出
     */
    class MyStack {
        Queue<Integer> queue1;
        Queue<Integer> queue2;

        /** Initialize your data structure here. */
        public MyStack() {
            queue1 = new LinkedList();  //入隊(duì)列
            queue2 = new LinkedList();  //出隊(duì)列
        }

        /** Push element x onto stack. */
        public void push(int x) {
            queue2.offer(x);
            while(!queue1.isEmpty()) { //將queue1中的元素移入queue2中,實(shí)現(xiàn)現(xiàn)有元素的倒序
                queue2.offer(queue1.poll());
            }
            //交換數(shù)據(jù),所以每次添加完數(shù)據(jù)都在queue1隊(duì)列中
            Queue<Integer> temp = queue1;
            queue1 = queue2;
            queue2 = temp;
        }

        /** Removes the element on top of the stack and returns that element. */
        public int pop() {
            return queue1.poll();
        }

        /** Get the top element. */
        public int top() {
            return queue1.peek();
        }

        /** Returns whether the stack is empty. */
        public boolean empty() {
            return queue1.isEmpty();
        }
    }

用兩個(gè)棧實(shí)現(xiàn)隊(duì)列

用兩個(gè)棧實(shí)現(xiàn)一個(gè)隊(duì)列。隊(duì)列的聲明如下,請(qǐng)實(shí)現(xiàn)它的兩個(gè)函數(shù) appendTail 和 deleteHead ,分別完成在隊(duì)列尾部插入整數(shù)和在隊(duì)列頭部刪除整數(shù)的功能。(若隊(duì)列中沒有元素,deleteHead 操作返回 -1 )

題解:
/**
     * 實(shí)現(xiàn)思路:
     * 創(chuàng)建2個(gè)棧,棧1只負(fù)責(zé)入棧,棧2只負(fù)責(zé)出棧
     * 存入幾個(gè)元素到棧1后,如果此時(shí)棧2為空,則將棧1中的元素轉(zhuǎn)移到棧2,從棧2出棧
     * 如果此時(shí)棧2中不為空,則直接從棧2出棧
     */
    class CQueue {
        Stack<Integer> stack1;  //stack1只負(fù)責(zé)入棧
        Stack<Integer> stack2;  //stack2只負(fù)責(zé)出棧

        public CQueue() {
            stack1 = new Stack<>();
            stack2 = new Stack<>();
        }

        /**
         * 入列
         * @param value
         */
        public void appendTail(int value) {
            stack1.push(value);
        }

        /**
         * 出列并返回當(dāng)前出列元素
         * @return
         */
        public int deleteHead() {
            if (!stack2.isEmpty()) {
                return stack2.pop();
            } else {
                //將stack1中所有元素轉(zhuǎn)移到stack2
                while (!stack1.isEmpty()) {
                    stack2.push(stack1.pop());
                }

                //若隊(duì)列中沒有元素,deleteHead 操作返回 -1
                return stack2.isEmpty() ? -1 : stack2.pop();
            }
        }
}

最小棧

設(shè)計(jì)一個(gè)支持 push ,pop ,top 操作,并能在常數(shù)時(shí)間內(nèi)檢索到最小元素的棧。

實(shí)現(xiàn) MinStack 類:

MinStack() 初始化堆棧對(duì)象。
void push(int val) 將元素val推入堆棧。
void pop() 刪除堆棧頂部的元素。
int top() 獲取堆棧頂部的元素。
int getMin() 獲取堆棧中的最小元素。

題解:
/**
     * 思路:用一個(gè)棧和一個(gè)最小棧來做,棧保存存入的數(shù),最小棧的數(shù)目與存數(shù)棧一樣,只是棧頂始終保存的是當(dāng)前棧中最小的數(shù)
     */
    class MinStack {
        Stack<Integer> stack;
        Stack<Integer> minStack;

        public MinStack() {
            stack = new Stack<>();
            minStack = new Stack<>();
        }

        public void push(int val) {
            stack.push(val);
            if (minStack.isEmpty()) {
                minStack.push(val);
            } else {
                minStack.push(Math.min(minStack.peek(), val));
            }
        }

        public void pop() {
            stack.pop();
            minStack.pop();
        }

        public int top() {
            return stack.peek();
        }

        public int getMin() {
            return minStack.peek();
        }
    }

去除重復(fù)字母

給你一個(gè)字符串 s ,請(qǐng)你去除字符串中重復(fù)的字母,使得每個(gè)字母只出現(xiàn)一次。需保證 返回結(jié)果的字典序最小(要求不能打亂其他字符的相對(duì)位置)。

題解:
class Solution {
    /**
     * 解題思路:
     * 1.第一個(gè)條件:去重字母
     * 2.第二個(gè)條件:保證字典序從小到大
     * 3.第三個(gè)條件:保證字母存在至少一個(gè)
     * @param s
     * @return
     */
    public String removeDuplicateLetters(String s) {
        //第三個(gè)條件:保證字母存在至少一個(gè)
        //做法:給每一種字母維護(hù)一個(gè)計(jì)數(shù)器,先遍歷獲取其數(shù)量,如果數(shù)量為1,則在第二個(gè)條件中不要彈出棧,否則可以彈出棧
        int[] charCount = new int[256];
        for (int i=0;i<s.length();i++) {
            charCount[s.charAt(i)]++;
        }

        Stack<Character> stk = new Stack<>();
        boolean[] inStack = new boolean[256]; //用于存儲(chǔ)這個(gè)字符是否已經(jīng)記錄過
        // 第一個(gè)條件:去重字母
        for(char c : s.toCharArray()) {
            // 每遍歷過一個(gè)字符,都將對(duì)應(yīng)的計(jì)數(shù)減一
            charCount[c]--;

            if (inStack[c]) {
                continue;
            }

            //第二個(gè)條件:保證字典序從小到大,在插入字母之前,判斷與前一個(gè)字母的字典序,
            // 如果比前一個(gè)小,則循環(huán)將字符彈出棧頂,清除在棧中的記錄
            while(!stk.empty() && stk.peek() > c) {
                if (charCount[stk.peek()] == 0) {
                    break;
                }
                inStack[stk.pop()] = false;
            }
            stk.push(c);
            inStack[c] = true;
        }
        StringBuilder sb = new StringBuilder();
        while (!stk.empty()) {
            sb.append(stk.pop());
        }
        return sb.reverse().toString();
    }
}

劍指 Offer 59 - I. 滑動(dòng)窗口的最大值

給定一個(gè)數(shù)組 nums 和滑動(dòng)窗口的大小 k,請(qǐng)找出所有滑動(dòng)窗口里的最大值。

題解:
class Solution {
        /**
     * 思路:
     *  從nums數(shù)組中取出前k個(gè)數(shù),存入隊(duì)列中
     *  獲取隊(duì)列的最大值存儲(chǔ)到集合中
     *  然后隊(duì)列移除頭部,在尾部添加第k+1個(gè)數(shù),獲取隊(duì)列中最大值存儲(chǔ)到集合中
     *
     * @param nums
     * @param k
     * @return
     */
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 0) {
            //如果數(shù)組為空,則返回空數(shù)組
            return new int[0];
        }
        int[] result = new int[nums.length-k+1];  //總共需要輸出的數(shù)組

        Queue<Integer> queue = new LinkedList<>();

        //首次添加前k個(gè)數(shù)到隊(duì)列中作為初始隊(duì)列
        for (int j=0;j<k;j++) {
            queue.add(nums[j]);
        }

        //遍歷隊(duì)列獲取最大值
        int maxNum = queue.peek();
        for (Integer num : queue) {
            if (num > maxNum) {
                maxNum = num;
            }
        }
        result[0] = maxNum;

        for (int i=0;i<nums.length-k;i++) {

            queue.poll();  //移除隊(duì)列頭部
            queue.add(nums[i+k]);  //添加數(shù)組下一個(gè)值到隊(duì)列尾部

            //遍歷隊(duì)列獲取最大值
            int max = queue.peek();
            for (Integer num : queue) {
                if (num > max) {
                    max = num;
                }
            }
            result[i+1] = max;
        }

        return result;
    }
}
最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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