刷題

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;
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結