LeetCode刷題日記(一)——鏈表、棧相關

226. 翻轉二叉樹

這道題的核心是: 翻轉就是將所有節點的左右子樹都交換
就是要遍歷到每個節點, 然後交換

遍歷二叉樹:

  • 前序遍歷

  • 中序遍歷

  • 後序遍歷

  • 層序遍歷

遍歷二叉樹的四種遍歷方法都可以

public class _226_翻轉二叉樹 {
//    public TreeNode invertTree(TreeNode root) {
//        if (root == null) return null;
//
//        TreeNode tmp = root.left;
//        root.left = root.right;
//        root.right = tmp;
//
//        invertTree(root.left);
//        invertTree(root.right);
//
//        return root;
//    }

//    public TreeNode invertTree(TreeNode root) {
//        if (root == null) return null;
//
//        invertTree(root.left);
//        invertTree(root.right);
//
//        TreeNode tmp = root.left;
//        root.left = root.right;
//        root.right = tmp;
//
//        return root;
//    }

//    public TreeNode invertTree(TreeNode root) {
//        if (root == null) return null;
//
//        invertTree(root.left);
//
//        TreeNode tmp = root.left;
//        root.left = root.right;
//        root.right = tmp;
//
//        invertTree(root.left); // 注意
//
//        return root;
//    }

    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;

            if (node.left != null) {
                queue.offer(node.left);
            }

            if (node.right != null) {
                queue.offer(node.right);
            }
        }

        return root;
    }
}

232. 用棧實現隊列

搞兩個棧

public class _232_用棧實現隊列 {
    private Stack<Integer> inStack;
    private Stack<Integer> outStack;

    /** Initialize your data structure here. */
    public _232_用棧實現隊列() {
        inStack = new Stack<>();
        outStack = new Stack<>();
    }

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

    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        checkOutStack();
        return outStack.pop();
    }

    /** Get the front element. */
    public int peek() {
        checkOutStack();
        return outStack.peek();
    }

    /** Returns whether the queue is empty. */
    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }

    private void checkOutStack() {
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                outStack.push(inStack.pop());
            }
        }
    }
}

20. 有效的括號

([{}]) [](){}

要拿 { 和 } 匹配很容易想到用棧這種數據結構, 因爲棧的後進先出特點, 導致後進的 { 會先彈出棧來和 } 匹配, 這正好符合題目的要求.

所以做法是: 遍歷到左括號入棧, 遍歷到右括號, 彈出棧頂元素匹配, 如果最後棧是空的說明有效. 因爲有可能有這種特殊情況 ([])[ ([{

另外每次遍歷到右括號都要檢查棧是否爲空, 如果棧是空的說明沒有左括號與右括號匹配了. 對應這種情況: [{}]]

public class _20_有效的括號 {
    private HashMap<Character, Character> map = new HashMap<>();

    public _20_有效的括號() {
        map.put('(', ')');
        map.put('{', '}');
        map.put('[', ']');
    }

    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();

        int len = s.length();
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (map.containsKey(c)) {
                stack.push(c);
            } else {
                if (stack.isEmpty()) return false;

                if (c != map.get(stack.pop())) return false;
            }
        }
        return stack.isEmpty();
    }

    public boolean isValid1(String s) {
        Stack<Character> stack = new Stack<>();

        int len = s.length();
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else {
                if (stack.isEmpty()) return false; // 注意特殊情況

                char left = stack.pop();
                if (left == '(' && c != ')') return false;
                if (left == '{' && c != '}') return false;
                if (left == '[' && c != ']') return false;
            }
        }
        return stack.isEmpty();
    }

    public boolean isValid2(String s) {
        while (s.contains("{}")
        || s.contains("[]")
        || s.contains("()")) {
            s = s.replace("{}", "");
            s = s.replace("()", "");
            s = s.replace("[]", "");
        }
        return s.isEmpty();
    }
}

83. 刪除排序鏈表中的重複元素

因爲本題中的鏈表是排序鏈表, 所以重複元素只可能在這個元素的後繼位置, 如果重複多次的話, 只能是 1 -> 1 -> 1 -> 2 -> 3 -> 5 這種, 不可能重複元素1在5的後面出現, 因爲是排好序的鏈表.

public class _83_刪除排序鏈表中的重複元素 {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode headNode = head;
        if (headNode == null) return null;
        while (headNode.next != null) {
            if (headNode.val == headNode.next.val) {
                headNode.next = headNode.next.next;
            } else {
                headNode = headNode.next;
            }
        }
        return head;
    }
}

141. 環形鏈表

判斷一個鏈表是否有環, 用快慢指針的思想.

一個慢指針指向第一個節點, 一個快指針指向第二個節點, 慢指針一次next一步, 快指針一次next2步, 這樣可以保證如果有環的話一定可以追到.

  • 爲什麼快指針不一次next3步呢?

  • 因爲next3步有可能會錯過慢指針

public class _141_環形鏈表 {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) return false;
        ListNode slow = head;
        ListNode fast = head.next;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) return true;
        }
        return false;
    }
}

203. 移除鏈表元素

public class _203_移除鏈表元素 {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) return null;
        while (head.val == val) {
            head = head.next;
            if (head == null) {
                return null;
            }
        }
        ListNode headNode = head;

        while (headNode.next != null) {
            if (headNode.next.val == val) {
                headNode.next = headNode.next.next;
            } else {
                headNode = headNode.next;
            }
        }
        return head;
    }
}

206. 反轉鏈表

  • 遞歸解法:

示例:

輸入: 5->4->3->2->1->NULL
輸出: 1->2->3->4->5->NULL

遞歸解法的關鍵是搞清楚遞歸的方法的作用是什麼, 然後充分利用他的作用去做事情.

比如給定一個節點4, 這個函數會返回什麼? 返回 1->2->3->4->NULL
然後在head.next.next = head;4->5
head.next = null;5->null

最後做一些邊界條件作爲遞歸的出口.
if (head == null || head.next == null) return head;

public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
  • 迭代解法:

因爲只能拿到head, 所以只能從head開始一個一個串起來.

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode newHead = null;
    while (head != null) {
        ListNode tmp = head.next;
        head.next = newHead;
        newHead = head;
        head = tmp;
    }
    return newHead;
}

237. 刪除鏈表中的節點

題目特別說明了要刪除的節點是非末尾的節點, 並且我們只能拿到要求被刪除的節點.

平常刪除鏈表的節點的做法都是利用要被刪除的節點的上一個節點然後node.next = node.next.next

現在拿不到他的上一個節點, 所以用他下一個節點的值覆蓋當前節點的值, 然後讓他的next指向他的next的next 來巧妙的達到目的.
node.val = node.next.val;
node.next = node.next.next;

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

876. 鏈表的中間結點

快慢指針思想

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

        ListNode slow = head;
        ListNode fast = head.next;
        while (fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (fast == null) {
                return slow;
            }
            if (fast.next == null) {
                slow = slow.next;
                return slow;
            }
        }
        return slow.next;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章