劍指 offer——鏈表、棧與隊列篇

6. 從尾到頭打印鏈表

題意:面試題06. 從尾到頭打印鏈表
思路:首先遍歷一遍鏈表獲得鏈表的長度,使用此長度初始化數組。而後再從頭至尾遍歷一遍鏈表,並將遍歷獲得的數字從後往前插入數組。java

class Solution {
    public int[] reversePrint(ListNode head) {
        int len = 0;
        ListNode p = head;
        while (p != null) {
            p = p.next;
            len ++;
        }
        int[] res = new int[len];
        p = head;
        while (p != null) {
            res[--len] = p.val;
            p = p.next;
        }
        return res;
    }
}

9. 用兩個棧實現隊列

題意:面試題09. 用兩個棧實現隊列
思路:「出隊」操做,將一個棧的數據所有倒入到另外一個空棧中,以後另外一個棧的操做順序即爲隊列的出棧順序。node

class CQueue {
    Stack<Integer> in;
    Stack<Integer> out;

    public CQueue() {
        in = new Stack<>();
        out = new Stack<>();
    }

    public void appendTail(int value) {
        in.push(value);
    }

    public int deleteHead() {
        if (out.isEmpty()) {
            while (!in.isEmpty()) {
                out.push(in.pop());
            }
        }
        return out.isEmpty() ? -1 : out.pop();
    }
}

18. 刪除鏈表的節點

題意:面試題18. 刪除鏈表的節點
思路1:要刪除單鏈表中的某一個節點node,首先須要找到node的前一個節點pre,而後把pre的next指針指向node的下一個節點便可。面試

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if (head == null) {
            return head;
        }
        if (head.val == val) {
            return head.next;
        }
        ListNode p = head;
        while (p.next != null) {
            if (p.next.val == val) {
                p.next = p.next.next;
                break;
            }
            p = p.next;
        }
        return head;
    }
}

思路2:上述思路1是在不能修改鏈表節點值的狀況下的操做。若是能夠修改鏈表的值,或者題目中沒有給出鏈表頭節點,只給出了要被刪除的節點。
這時咱們能夠使用後面的節點值覆蓋前面節點的值來完成刪除節點操做。參照面試題 02.03. 刪除中間節點數組

class Solution {
    public void deleteNode(ListNode node) {
        ListNode p = node;
        ListNode q = node.next;
        while (q.next != null) {
            p.val = q.val;
            p = p.next;
            q = q.next;
        }
        p.val = q.val;
        p.next = null;
    }
}

22. 鏈表中倒數第k個節點

題意:面試題22. 鏈表中倒數第k個節點
思路:快慢雙指針法。讓快指針先走k步,而後快慢指針一塊兒走,當快指針走到鏈表結尾的時候,慢指針就指向倒數第k個節點app

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode p = head;
        while (p != null && k > 0) {
            p = p.next;
            k --;
        }
        if (p == null && k > 0) {
            return null;
        }
        ListNode q = head;
        while (p != null) {
             p = p.next;
             q = q.next;
        }
        return q;
    }
}

24. 反轉鏈表

題意:面試題24. 反轉鏈表
思路:遞歸。先反轉當前節點的後面節點,reverseList(head.next),這個函數返回的是反轉以後的鏈表頭,即最後一個節點。進行這步操做以後,當前節點下一個節點指向的是反轉以後鏈表的尾部節點,即head.next,這時將head.next的下一個節點指向當前節點便可完成反轉。dom

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null) {
            return null;
        }
        if (head.next == null) {
            return head;
        }
        ListNode next = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return next;
    }
}

25. 合併兩個排序的鏈表

題意:面試題25. 合併兩個排序的鏈表
思路:雙指針法。使用兩個指針分別指向l1和l2的頭結點,若是l1.val < l2.val,那麼將l1指向的結點加入新的鏈表中,不然將l2指向的結點加入新的鏈表。函數

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(-1);
        ListNode r = head;
        while (l1 != null && l2 != null) {
            if (l1.val > l2.val) {
                r.next = l2;
                l2 = l2.next;
            } else {
                r.next = l1;
                l1 = l1.next;
            }
            r = r.next;
        }
        r.next = (l1 == null) ? l2 : l1;
        return head.next;
    }
}

30. 包含min函數的棧

題意:面試題30. 包含min函數的棧
思路:使用兩個棧,一個棧data用來保存數據,另外一個棧min用來存data中最小值的信息。
1)入棧時,若當前入棧的元素x小於min棧中棧頂元素,那麼將當前元素x同時壓入data棧和min棧。
2)出棧時,若出棧元素x等於min的棧頂元素,那麼將x也從min棧中彈出。ui

class MinStack {
    Stack<Integer> data;
    Stack<Integer> min;
    /** initialize your data structure here. */
    public MinStack() {
        data = new Stack<>();
        min = new Stack<>();
    }

    public void push(int x) {
        data.push(x);
        if (min.isEmpty() || x <= min.peek()) {
            min.push(x);
        }
    }

    public void pop() {
        if (data.isEmpty()) {
            return;   
        }
        int num = data.pop();
        if (num == min.peek()) {
            min.pop();
        }
    }

    public int top() {
        if (data.isEmpty()) {
            return -1;
        }
        return data.peek();
    }

    public int min() {
        if (min.isEmpty()) {
            return -1;
        }
        return min.peek();
    }
}
/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

31. 棧的壓入、彈出序列

題意:面試題31. 棧的壓入、彈出序列
思路:建一個棧來模擬題目中的壓入、彈出操做。因爲彈出序列中的第一個數字,必定是出如今棧頂時彈出的,如指針

pushed = [1,2,3,4,5], popped = [4,5,3,2,1]

中,彈出序列中的第一個元素4出棧時,壓棧序列中必定是將4以及以前的元素壓入棧內了。這時模擬彈出棧頂的元素4,而後接着比較彈出序列的下一個元素是否還與棧頂相同。
即每次將彈出序列的元素,與棧頂元素比較,相同則彈出,不一樣則繼續入棧元素,最後判斷棧是否爲空便可判斷是否爲合法的彈出序列。code

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

35. 複雜鏈表的複製

題意:面試題35. 複雜鏈表的複製
思路:鏈表除了next指針,還包含random指針。使用一個Map記錄下已經建立的新結點,並將舊結點與新結點創建映射關係。在遍歷過程當中對於已經建立過的結點直接從Map中取便可。

class Solution {
    Map<Node, Node> map = new HashMap<>();

    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        if (map.get(head) != null) {
            return map.get(head);
        }
        Node newNode = new Node(head.val);
        map.put(head, newNode);
        newNode.next = copyRandomList(head.next);
        newNode.random = copyRandomList(head.random);
        return newNode;
    }
}

41. 數據流中的中位數

題意:面試題41. 數據流中的中位數
思路:構造兩個堆,一個大根堆,一個小根堆。使大根堆中記錄數據流中較小部分的元素,小根堆中記錄數據流中較大部分的元素。
使得小根堆中元素的值都大於大根堆中元素的值。即便小根堆的根結點值比大根堆的根結點值要大。
而且保證,在數據流的個數爲偶數時,兩個堆中的數據個數同樣(此時中位數爲兩個堆堆頂元素的平均值)。數據流個數爲奇數時,大根堆個數比小根堆多一個(此時中位數爲大根堆的堆頂元素)。

class MedianFinder {
    PriorityQueue<Integer> min;
    PriorityQueue<Integer> max;

    /** initialize your data structure here. */
    public MedianFinder() {
        min = new PriorityQueue();
        max = new PriorityQueue(Collections.reverseOrder());
    }

    public void addNum(int num) {
        if (max.size() == min.size()) {
            min.add(num);
            max.add(min.poll());
        } else {
            max.add(num);
            min.add(max.poll());
        }
    }

    public double findMedian() {
        return max.size() == min.size() ? (max.peek() + min.peek()) / 2.0 : max.peek();
    }
}

52. 兩個鏈表的第一個公共結點

題意:面試題52. 兩個鏈表的第一個公共節點
思路:先計算兩個鏈表的長度。算出兩個鏈表長度之差diff。較長的鏈表先走diff步以後,兩個鏈表同時向後遍歷,直到找到公共結點,或者到達兩個鏈表結尾。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        int lenA = len(headA);
        int lenB = len(headB);
        ListNode p = lenA > lenB ? headA : headB;
        ListNode q = p == headA ? headB : headA;
        int diff = Math.abs(lenA - lenB);
        while (diff > 0) {
            p = p.next;
            diff--;
        }
        while (p != null && q != null) {
            if (p == q) {
                return p;
            }
            p = p.next;
            q = q.next;
        }
        return null;
    }

    private int len(ListNode head) {
        ListNode tmp = head;
        int len = 0;
        while (tmp != null) {
            tmp = tmp.next;
            len ++;
        }
        return len;
    }
}

59-II. 隊列的最大值

題意:面試題59 - II. 隊列的最大值

思路:單調棧。除了使用一個數據隊列記錄入隊的元素,還要使用一個雙端隊列(單調的),維護一個從頭至尾遞減的序列。雙端隊列的隊頭元素即爲隊列的最大值。入數據隊列時,若是元素大於雙端隊列的隊尾元素,就要把隊尾元素依次出隊,而後把當前元素插入隊列中。
當數據隊列出隊的元素等於雙端隊列的頭元素時,雙端隊列的隊頭元素也要出隊。

class MaxQueue {
    Queue<Integer> queue;
    Deque<Integer> maxValue;

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

    public int max_value() {
        if (maxValue.isEmpty()) {
            return -1;
        }
        return maxValue.peek();
    }

    public void push_back(int value) {
        queue.add(value);
        while (!maxValue.isEmpty() && maxValue.getLast() < value) {
            maxValue.removeLast();
        }
        maxValue.add(value);
    }

    public int pop_front() {
        if (maxValue.isEmpty()) {
            return -1;
        }
        int val = queue.poll();
        if (val == maxValue.peek()) {
            maxValue.removeFirst();
        }
        return val;
    }
}
相關文章
相關標籤/搜索