刷題記錄

LZC發表於2021-08-29

最長無重複子陣列

給定一個陣列arr,返回arr的最長無重複元素子陣列的長度,無重複指的是所有數字都不相同。子陣列是連續的,比如[1,3,5,7,9]的子陣列有[1,3],[3,5,7]等等,但是[1,3,7]不是子陣列

輸入:
[1,2,3,1,2,3,2,2]
返回值:
3
複製
說明:
最長子陣列為[1,2,3] 

方法一:

public class Solution {
    /**
     * 
     * @param arr int整型一維陣列 the array
     * @return int整型
     */
    public int maxLength (int[] arr) {
        if(arr.length <= 1) {
            return arr.length;
        } 
        // 無重複的起點
        int a = 0;
        // 當前無重複的最大長度
        int max = 1;
        // 1,2,3,1,4
        for(int b = 1; b < arr.length;b++) {
            // 尋找[a, b-1]是否存在於arr[b]相等的數, 如果存在則要更新a的值
            for(int i = b-1; i >= a; i--) {
                if(arr[i] == arr[b]) {
                    a = i + 1;
                    break;
                }
            }
            max = Math.max(max, b - a + 1);
        }
        return max;
    }
}

方法二:

public class Solution {
    /**
     * 
     * @param arr int整型一維陣列 the array
     * @return int整型
     */
    public int maxLength (int[] arr) {
        if(arr.length <= 1) {
            return arr.length;
        } 
        // 無重複的起點
        int a = 0;
        // 當前無重複的最大長度
        int max = 0;
        // 使用一個Map來儲存已經遍歷過的元素
        Map<Integer, Integer> map = new HashMap();
        // 1,2,3,1,4
        for(int b = 0; b < arr.length;b++) {
            if(map.containsKey(arr[b])) {
                // 每次的取值都要大於等於a, 防止a往前走
                a = Math.max(a, map.get(arr[b]) + 1);
            } 
            map.put(arr[b], b);
            max = Math.max(max, b - a + 1);
        }
        return max;
    }
}

方法三:

public class Solution {
    /**
     * 
     * @param arr int整型一維陣列 the array
     * @return int整型
     */
    public int maxLength (int[] arr) {
        if(arr.length <= 1) {
            return arr.length;
        } 
        int max = 0;
        // 使用一個佇列來儲存當前無重複的資料
        LinkedList<Integer> queue = new LinkedList();
        for(int i = 0; i < arr.length; i++) {
            // 遇到重複元素時,從頭部一直出棧,知道無重複元素
            while(queue.contains(arr[i])) {
                queue.removeFirst();
            }
            queue.addLast(arr[i]);
            max = Math.max(max, queue.size());
        }
        return max;
    }
}

子陣列的最大累加和問題

給定一個陣列arr,返回子陣列的最大累加和

例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子陣列中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12.

題目保證沒有全為負數的資料

[要求]

時間複雜度為O(n),空間複雜度為O(1)

輸入:
[1, -2, 3, 5, -2, 6, -1]
返回值:
12

方法一:空間複雜度為O(n)

public class Solution {
    /**
     * 
     * 狀態轉移方程如下
     * dp[0] = arr[0];
     * dp[i - 1] > 0 ==> dp[i] = dp[i - 1] + arr[i];
     * dp[i - 1] <= 0 ==> dp[i] = arr[i];
     */
    public int maxsumofSubarray (int[] arr) {
        if(arr.length == 0) {
            return 0;
        }
        if(arr.length == 1) {
            return arr[0];
        }
        int[] dp = new int[arr.length];
        dp[0] = arr[0];
        int max = dp[0];
        for(int i = 1; i < arr.length; i++) {
            if(dp[i - 1] > 0) {
                dp[i] = dp[i - 1] + arr[i];
            } else {
                dp[i] = arr[i];
            }
            max = Math.max(max, dp[i]);
        }
        return max;
    }
}

方法二:空間複雜度為O(1)

public class Solution {
    /**
     * 狀態轉移方程如下
     * dp[0] = arr[0];
     * dp[i - 1]  > 0 ==> dp[i] = dp[i - 1] + arr[i];
     * dp[i - 1] <= 0 ==> dp[i] = arr[i];
     */
    public int maxsumofSubarray (int[] arr) {
        if(arr.length == 0) {
            return 0;
        }
        if(arr.length == 1) {
            return arr[0];
        }
        int pre = arr[0];
        int max = pre;
        for(int i = 1; i < arr.length; i++) {
            if(pre > 0) {
                pre = pre + arr[i];
            } else {
                pre = arr[i];
            }
            max = Math.max(max, pre);
        }
        return max;
    }
}

刪除有序連結串列中重複的元素-II

給出一個升序排序的連結串列,刪除連結串列中的所有重複出現的元素,只保留原連結串列中只出現一次的元素。
例如:
給出的連結串列為1→2→3→3→4→4→5, 返回1→2→5.
給出的連結串列為1→1→1→2→3, 返回2→3.

方法一:空間複雜度O(n)

過程:

public class Solution {
    /**
     * 
     * @param head ListNode類 
     * @return ListNode類
     */
    public ListNode deleteDuplicates (ListNode head) {
        // 設定一個偽節點,主要是為了方便處理
        ListNode result = new ListNode(0);
        ListNode resultTemp = result;

        ListNode h = head;

        while(h != null) {
            ListNode cur = h;
            ListNode node = cur.next;
            boolean flag = false;
            // 這裡是為了找到當前節點是否存在重複元素
            while(node != null) {
                if(node.val == cur.val) {
                    flag = true;
                    // 這裡主要是過濾掉重複的
                    node = node.next;
                } else {
                    break;
                }
            }
            if(!flag) {
                // 不存在重複元素,則新建一個元素
                resultTemp.next = new ListNode(cur.val);
                resultTemp = resultTemp.next;
            }
              // h指向node: 因為此時的node是指向一個與cur不相等的節點, 如果h直接指向node就行了
            h = node;
        }
        return result.next;
    }
}

方法二:空間複雜度O(1),直接在原連結串列上面操作

public class Solution {
    /**
     * 
     * @param head ListNode類 
     * @return ListNode類
     */
    public ListNode deleteDuplicates (ListNode head) {
        // 設定一個偽節點,主要是為了方便處理
        ListNode result = new ListNode(0);
        result.next = head;
        // 前指標
        ListNode pre = result;
        // 當前指標
        ListNode cur = head;
        // 每次都判斷當前指標的下一個指標是否與當前指標的值相同
        // 如果相同, 則找到第一個與當前指標不相同的節點node, 然後將前指標的next指標指向node
        // 如果不相同, 則前指標和當前指標都指向下一個
        // 1 2 3 3 4 4 5
        while(cur != null) {
            if(cur.next != null && cur.next.val == cur.val) {
                // 存在相同值
                ListNode node = cur.next;
                // 這裡主要是讓node指向一個不與cur相同的節點
                while(node != null && node.val == cur.val) {
                    node = node.next;
                }
                // 直接將 pre.next 指向 node, 達到了刪除相同元素的目的
                pre.next = node;
                cur = node;
            } else {
                // 不存在相同值, 都指向下一個指標
                pre = pre.next;
                cur = cur.next;
            }
        }
        return result.next;
    }
}

最長迴文子串

對於一個字串,請設計一個高效演算法,計算其中最長迴文子串的長度。

給定字串A以及它的長度n,請返回最長迴文子串的長度。

輸入:"abc1234321ab",12
返回:7

方法一: 暴力法

public class Solution {
    public int getLongestPalindrome(String A, int n) {
        // write code here
        if(n <= 1) {
            return n;
        }
        int max = 1;
        char[] chars = A.toCharArray();
        // 兩個for迴圈遍歷所有可能的子串
        for(int i = 0; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                // 如果當前字串長度大於 max 並且是迴文
                if(j - i + 1 > max && isHuiwen(chars, i, j)) {
                    max = j - i + 1;
                }
            }
        }
        return max;
    }

    public boolean isHuiwen(char[] chars, int i, int j) {
        while(i < j) {
            if(chars[i] != chars[j]) {
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
}

方法二: 動態規劃

dp[left][right]代表[left, right]之間是否為迴文
s.charAt(left) == s.charAt(right)
如果 right - left <= 2, dp[left][right] = true; 則字元長度為2或者3, 類似於這種: "aa""aba"
如果 right - left > 2, dp[left][right] = dp[left+1][right-1];
public class Solution {
    public int getLongestPalindrome(String A, int n) {
        if(n < 2) {
            return A.length();
        }
        boolean[][] dp = new boolean[n][n];
        int max = 1;
        char[] chars = A.toCharArray();
        for(int right = 1; right < n; right++) {
            for(int left = 0; left < right; left++) {
                // 如果不相同, 說明[left, right]肯定不是迴文
                if(chars[left] != chars[right]) {
                    continue;
                }
                if(right - left <= 2){
                    // aa 或者 aba
                    dp[left][right] = true;
                } else {
                    dp[left][right] = dp[left + 1][right - 1];
                }
                if(dp[left][right]) {
                    max = Math.max(max, right - left + 1);
                }
            }
        }
        return max;
    }
}

對稱二叉樹

public class Main {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(2);

        root.left.left = new TreeNode(3);
        root.left.right = new TreeNode(4);

        root.right.left = new TreeNode(4);
        root.right.right = new TreeNode(3);
        //
//        root.left.left.left = new Node(7);
//        root.left.left.right = new Node(8);
//        root.left.right.left = new Node(9);
//        root.left.right.right = new Node(10);

        System.out.println(isSymmetric2(root));;
    }
    /**
     * 遞迴法
     * 判斷是否為對稱二叉樹
     * https://leetcode-cn.com/problems/symmetric-tree/
     */
    public static boolean isSymmetric(TreeNode root){
        if (root == null) {
            return true;
        }
        return compare(root.left, root.right);
    }
    /**
     *       1
     *    /     \
     *   2           2
     * /   \    /    \
     * 3    4    4    3
     *
     * 比較左子樹跟右子樹
     * 1. 空判斷
     * 2.
     *  left.left  == right.right 外側比較
     *  left.right == right.left  內測比較
     * @param left
     * @param right
     * @return
     */
    public static boolean compare(TreeNode left, TreeNode right){
        if (left == null && right == null) {
            return true;
        } else if (left == null && right != null) {
            return false;
        } else if (left != null && right == null) {
            return false;
        } else if (left.value != right.value) {
            return false;
        }
        // 比較外側
        boolean outSide = compare(left.left, right.right);
        // 比較內側
        boolean inSide = compare(left.right, right.left);
        return outSide && inSide;
    }

    /**
     * 非遞迴法
     * 判斷是否為對稱二叉樹
     * https://leetcode-cn.com/problems/symmetric-tree/
     */
    public static boolean isSymmetric2(TreeNode root){
        if (root == null) {
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root.left);
        stack.push(root.right);
        while (!stack.isEmpty()) {
            // 出棧-拿出右樹
            TreeNode right = stack.pop();
            // 出棧-拿出左樹
            TreeNode left = stack.pop();
            if (right == null && left == null) {
                continue;
            } else if (right == null && left != null) {
                return false;
            } else if (right != null && left == null) {
                return false;
            } else if (right.value != left.value) {
                return false;
            }
            // 外側
            stack.push(left.left);
            stack.push(right.right);
            // 內側
            stack.push(left.right);
            stack.push(right.left);
        }
        return true;
    }
}

二叉樹的最大深度

/**
     * 二叉樹的最大深度
     * https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
     */
    public static int getMaxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = getMaxDepth(root.left);
        int right = getMaxDepth(root.right);
        return Math.max(left, right) + 1;
    }

二叉樹的最小深度

/**
     * 111. 二叉樹的最小深度
     * 最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
     * https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
     */
    public static int getMinDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = getMaxDepth(root.left);
        int right = getMaxDepth(root.right);
        // 左節點為空
        if (root.left == null && root.right != null) {
            return 1 + right;
        }
        // 右節點為空
        if (root.left != null && root.right == null) {
            return 1 + left;
        }
        return Math.min(left, right) + 1;
    }

計算二叉樹的節點個數

/**
     * 計算二叉樹的節點個數
     * 遞迴
     */
    public static int getNodeCount(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 計算左樹節點個數
        int leftCount = getNodeCount(root.left);
        // 計算右樹節點個數
        int rightCount = getNodeCount(root.right);
        // 加1是因為加上當前節點
        return leftCount + rightCount + 1;
    }
    /**
     * 計算二叉樹的節點個數
     * 非遞迴-使用層次遍歷
     */
    public static int getNodeCount2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        LinkedList<TreeNode> list = new LinkedList();
        list.addLast(root);
        int count = 0;
        while (!list.isEmpty()) {
            int size = list.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = list.removeFirst();
                if (node.left != null) {
                    list.addLast(node.left);
                }
                if (node.right != null) {
                    list.addLast(node.right);
                }
                count++;
            }
        }
        return count;
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結