一 字符串中的最大回文串(第5題)
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of sis 1000.
Example:
Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
Example:
Input: "cbbd" Output: "bb"
1. 我的解法(accepted): 中心擴展
思路: 回文即代表有中心,一次遍歷中,對於每個位置上的數字求一下最大的回文串即可,初始處理剪枝,合並掉同樣的元素,如xxxxaaaxxxx,先常量級別把a合並掉; 遍歷時候再次剪枝,遍歷過的有一個maxLen,如果即將遍歷的元素最大可能回文長度都不可能超過maxLen,不再遍歷。

1 public class Test1218 { 2 3 public static void main(String[] args) { 4 String str = "ababababa"; 5 System.out.println(longestPalindrome(str)); 6 7 } 8 9 public static String longestPalindrome(String s) { 10 if (s == null) { 11 return null; 12 } 13 char[] chars = s.toCharArray(); 14 int length = chars.length; 15 int maxLen = 0; 16 String maxStr = ""; 17 18 for (int i = 0; i < length; i++) { 19 20 // cut branch 21 int possibleLength = getMaxPossibleLength(i, length); 22 if (possibleLength < maxLen) { 23 continue; 24 } 25 26 String maxStrTmp = getMaxStrByIndex(i, chars); 27 if (maxLen < maxStrTmp.length()) { 28 maxLen = maxStrTmp.length(); 29 maxStr = maxStrTmp; 30 } 31 } 32 return maxStr; 33 } 34 35 private static int getMaxPossibleLength(int index, int length) { 36 int head = 0; 37 int tail = length - 1; 38 if (index == head || index == tail) { 39 return 1; 40 } 41 int result1 = index - head; 42 int result2 = tail - index; 43 44 int min = result1 <= result2 ? result1 : result2; 45 return min * 2 + 1; 46 } 47 48 private static String getMaxStrByIndex(int index, char[] chars) { 49 StringBuilder sb = new StringBuilder(String.valueOf(chars[index])); 50 int length = chars.length; 51 int head = index - 1; 52 int tail = index + 1; 53 54 // middle deal 55 while (true) { 56 if (head >= 0 && chars[index] == chars[head]) { 57 sb.insert(0, String.valueOf(chars[head--])); 58 } else if (tail <= length - 1 && chars[index] == chars[tail]) { 59 sb.append(String.valueOf(chars[tail++])); 60 } else { 61 break; 62 } 63 } 64 65 // besides deal 66 while (true) { 67 if (head < 0 || tail > length - 1) { 68 break; 69 } 70 if (head >= 0 && tail <= length - 1 && chars[head] == chars[tail]) { 71 sb.insert(0, String.valueOf(chars[head--])); 72 sb.append(String.valueOf(chars[tail++])); 73 continue; 74 } 75 break; 76 77 } 78 return sb.toString(); 79 } 80 }
2. dp解法
思路: 設 p[i][j] 代表下標從i至j的子字符串是否是回文串,取值為boolean
轉移方程 p[i][j] = p[i+1][j-1] && chars[i] == chars[j]
初始化的狀態為 p[i][i] = true p[i][i+1] = chars[i] == chars[i+1]
看下面手繪圖理解一下,打勾的對角線p[i][i] 恆為true, 只有這一列是不夠狀態轉移的,因為按照轉移方程,必須要圖示的↗️方向遞推,那么要需要有圓圈的一列初始化,這列對應的是p[i][i+1]。 剩下的遞推即可,比如圖中的箭頭栗子🌰,p[0][2]要根據p[1][1]加上元素值推演過來,而p[0][4]需要p[1][3]的值推演過來。所以更新下表的順序是打勾列向↗️更新

public class Test1218 { public static void main(String[] args) { String str = "abcba"; System.out.println(longestPalindrome(str)); } public static String longestPalindrome(String str) { char[] chars = str.toCharArray(); int length = chars.length; boolean dp[][] = new boolean[1002][1002]; // 初始化間距為1的值 int maxLen = 1; String maxStr = str.substring(0, 1); for (int i = 0; i < length; i++) { dp[i][i] = true; } // 初始化間距為2的值 for (int i = 0; i < length - 1; i++) { dp[i][i + 1] = (chars[i] == chars[i + 1]); if (dp[i][i + 1]) { maxLen = 2; maxStr = str.substring(i, i + 2); } } // 狀態轉移 for (int minus = 2; minus < length; minus++) { for (int i = 0; i < length; i++) { int j = minus + i; if (j > length - 1) { break; } dp[i][j] = dp[i + 1][j - 1] && chars[i] == chars[j]; // 回文串且長度大於之前的最大長度,進行更新 if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1; maxStr = str.substring(i, j + 1); } } } return maxStr; } }
二 最大盛水量/面積(第11題)
1. 我的解法(tle): 暴力遍歷,剪枝掉低於兩端的"局部較低的數據"

1 public class Test1218 { 2 public static void main(String[] args) { 3 int i = maxArea(new int[]{1, 3, 2, 1, 5}); 4 System.out.println(i); 5 } 6 7 public static int maxArea(int[] height) { 8 9 int length = height.length; 10 int[] leftMaxArray = new int[length]; 11 int leftMax = 0; 12 for (int i = 0; i < length; i++) { 13 leftMax = height[i] > leftMax ? height[i] : leftMax; 14 leftMaxArray[i] = leftMax; 15 } 16 17 int[] rightMaxArray = new int[length]; 18 int rightMax = 0; 19 for (int i = length - 1; i >= 0; i--) { 20 rightMax = height[i] > rightMax ? height[i] : rightMax; 21 rightMaxArray[i] = rightMax; 22 } 23 24 25 // 減枝 26 boolean[] flag = new boolean[length]; 27 flag[0] = true; 28 flag[length - 1] = true; 29 for (int i = 1; i < length - 1; i++) { 30 flag[i] = !(height[i] < leftMaxArray[i - 1] && height[i] < rightMaxArray[i + 1]); 31 } 32 33 int max = 0; 34 for (int i = 0; i < length; i++) { 35 for (int j = i + 1; j < length; j++) { 36 if (!flag[j]) { 37 continue; 38 } 39 int minH = height[i] > height[j] ? height[j] : height[i]; 40 int tmpArea = (j - i) * minH; 41 if (tmpArea > max) { 42 max = tmpArea; 43 } 44 } 45 } 46 return max; 47 } 48 }
2. 貪心
兩個指針l和r,初始化為數組的兩端,然后向中間移動找到最大的面積。如果l指向的數字小,則l需要右移才有可能獲得更大容量,因為r左移,得到的容量肯定比r左移之前的容量小(高度已經被較小的l限制住了)。同理,r指向的數據小的話,則需要進行左移r。
時間復雜度:O(n)
空間復雜度:O(1)

1 public class Test1218 { 2 public static void main(String[] args) { 3 int i = maxArea(new int[]{1, 3, 2, 1, 5}); 4 System.out.println(i); 5 } 6 7 public static int maxArea(int[] height) { 8 int left = 0; 9 int right = height.length - 1; 10 int maxArea = 0; 11 while (left < right) { 12 maxArea = Math.max(maxArea, (right - left) * Math.min(height[left], height[right])); 13 if (height[left] < height[right]) { 14 left++; 15 } else { 16 right--; 17 } 18 } 19 return maxArea; 20 } 21 }
三 判斷是否為回文的整數(第9題)
Determine whether an integer is a palindrome. Do this without extra space.
1. 我的解法(accepted, 不過不是最優空間復雜度,和題意零額外空間不符): 換成char[], 遍歷一半

1 class Solution { 2 public static void main(String[] args) { 3 boolean palindrome = isPalindrome(-121); 4 System.out.println(palindrome); 5 } 6 7 public static boolean isPalindrome(int x) { 8 if (x < 0) { 9 x = -x; 10 } 11 char[] charArray = String.valueOf(x).toCharArray(); 12 int length = charArray.length; 13 for (int i = 0; i < length >> 1; i++) { 14 if (charArray[i] == charArray[length - 1 - i]) { 15 return true; 16 } 17 } 18 return false; 19 } 20 }
2. 按位除法&取余處理,構造出逆序的字串進行比較

1 public class Test1218 { 2 public static void main(String[] args) { 3 boolean palindrome = isPalindrome(-2147447412); 4 System.out.println(palindrome); 5 } 6 7 public static boolean isPalindrome(int x) { 8 if (x < 0 || (x % 10 == 0 && x != 0)) { 9 return false; 10 } 11 12 int revertedNumber = 0; 13 while (x > revertedNumber) { 14 revertedNumber = revertedNumber * 10 + x % 10; 15 x /= 10; 16 } 17 18 return x == revertedNumber || x == revertedNumber / 10; 19 } 20 }
四 兩個有序單鏈表合並成一個有序鏈表(第21題)
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4
1. 我的解法(accepted): 按位置比較插入新鏈表
Time complexity : O(n + m)
Space complexity : O(1)

1 class ListNode { 2 int val; 3 ListNode next; 4 5 ListNode(int x) { 6 val = x; 7 } 8 } 9 10 public class Test1218 { 11 public static void main(String[] args) { 12 13 ListNode node11 = new ListNode(1); 14 ListNode node12 = new ListNode(2); 15 ListNode node13 = new ListNode(4); 16 node11.next = node12; 17 node12.next = node13; 18 19 ListNode n1 = node11; 20 21 ListNode node21 = new ListNode(1); 22 ListNode node22 = new ListNode(3); 23 ListNode node23 = new ListNode(4); 24 node21.next = node22; 25 node22.next = node23; 26 27 ListNode n2 = node21; 28 29 ListNode listNode = mergeTwoLists(n1, n2); 30 while (listNode != null) { 31 System.out.println(listNode.val); 32 listNode = listNode.next; 33 } 34 } 35 36 public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { 37 ListNode head = new ListNode(-1); 38 ListNode cur = head; 39 40 while (l1 != null & l2 != null) { 41 if (l1.val < l2.val) { 42 cur.next = l1; 43 l1 = l1.next; 44 } else { 45 cur.next = l2; 46 l2 = l2.next; 47 } 48 cur = cur.next; 49 } 50 51 cur.next = l1 == null ? l2 : l1; 52 return head.next; 53 } 54 }
2. 遞歸:
灰常簡約和易於書寫,不過有遞歸的棧空間消耗
Time complexity : O(n + m)
Space complexity : O(n + m)

1 class ListNode { 2 int val; 3 ListNode next; 4 5 ListNode(int x) { 6 val = x; 7 } 8 } 9 10 public class Test1218 { 11 public static void main(String[] args) { 12 13 ListNode node11 = new ListNode(1); 14 ListNode node12 = new ListNode(2); 15 ListNode node13 = new ListNode(4); 16 node11.next = node12; 17 node12.next = node13; 18 19 ListNode n1 = node11; 20 21 ListNode node21 = new ListNode(1); 22 ListNode node22 = new ListNode(3); 23 ListNode node23 = new ListNode(4); 24 node21.next = node22; 25 node22.next = node23; 26 27 ListNode n2 = node21; 28 29 ListNode listNode = mergeTwoLists(n1, n2); 30 while (listNode != null) { 31 System.out.println(listNode.val); 32 listNode = listNode.next; 33 } 34 } 35 36 public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { 37 if (l1 == null) { 38 return l2; 39 } 40 if (l2 == null) { 41 return l1; 42 } 43 if (l1.val < l2.val) { 44 l1.next = mergeTwoLists(l1.next, l2); 45 return l1; 46 } else { 47 l2.next = mergeTwoLists(l1, l2.next); 48 return l2; 49 } 50 } 51 }
五 找出數組中三個數相加為0的不同組合(第15題)
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4], A solution set is: [ [-1, 0, 1], [-1, -1, 2] ]
1. 我的解法(Time Limit Exceeded): 兩重循環i, j得到 num[i] num[j] 找到-(num[i] + num[j]), 把這三個數的集合列表進行去重,時間復雜度逼近O(n^3)

1 public class Solution { 2 public static void main(String[] args) { 3 // List<List<Integer>> lists = threeSum(new int[]{-1, 0, 1, 2, -1, -4}); 4 List<List<Integer>> lists = threeSum(new int[]{-1, 0, 1, 2, -1, -4}); 5 6 System.out.println(lists); 7 } 8 9 public static List<List<Integer>> threeSum(int[] nums) { 10 11 HashSet<Integer> hashSet = new HashSet<>(); 12 for (int num : nums) { 13 hashSet.add(-num); 14 } 15 16 List<List<Integer>> duplicatedRet = new ArrayList<>(); 17 List<Integer> list; 18 int length = nums.length; 19 for (int i = 0; i < length; i++) { 20 for (int j = i + 1; j < length; j++) { 21 list = new ArrayList<>(); 22 if (hashSet.contains(nums[i] + nums[j])) { 23 int ele[] = new int[]{nums[i], nums[j], -nums[i] - nums[j]}; 24 if (isIntList(nums, nums[i], nums[j], -nums[i] - nums[j])) { 25 Arrays.sort(ele); 26 for (int e : ele) { 27 list.add(e); 28 } 29 duplicatedRet.add(list); 30 } 31 } 32 } 33 } 34 35 List<List<Integer>> ret = new ArrayList<>(); 36 for (int i = 0; i < duplicatedRet.size(); i++) { 37 boolean flag = false; 38 for (int j = i + 1; j < duplicatedRet.size(); j++) { 39 if (ifSame(duplicatedRet.get(i), duplicatedRet.get(j))) { 40 flag = true; 41 break; 42 } 43 } 44 if (!flag) { 45 ret.add(duplicatedRet.get(i)); 46 } 47 } 48 return ret; 49 } 50 51 private static boolean isIntList(int[] nums, int n1, int n2, int n3) { 52 List<Integer> list = new ArrayList<>(); 53 for (int i : nums) { 54 list.add(i); 55 } 56 return list.remove((Integer) n1) && list.remove((Integer) n2) && list.remove((Integer) n3); 57 } 58 59 private static boolean ifSame(List<Integer> l1, List<Integer> l2) { 60 return Objects.equals(l1.get(0), l2.get(0)) && Objects.equals(l1.get(1), l2.get(1)) && Objects.equals(l1.get(2), l2.get(2)); 61 } 62 }
2. 排序,遍歷每個數i時,看是否后面的兩個數可以相加等於-i, 雙端遍歷,合理剪枝,比如相同的數字略過,遍歷到倒數第三個,遍歷后期的數為正直接break等

1 import java.util.*; 2 class Solution { 3 public static void main(String[] args) { 4 List<List<Integer>> lists = threeSum(new int[]{-1, 0, 1, 2, -1, -4}); 5 System.out.println(lists); 6 } 7 8 public static List<List<Integer>> threeSum(int[] nums) { 9 List<List<Integer>> ret = new ArrayList<>(); 10 int length = nums.length; 11 Arrays.sort(nums); 12 for (int i = 0; i < length - 2; i++) { 13 // 剪枝。已排序,整數作為最小的數,無法構造出三個數相加等於0 14 if (nums[i] > 0) { 15 break; 16 } 17 18 // 剪枝。相等的數無需重復計算 19 if (i > 0 && nums[i] == nums[i - 1]) { 20 continue; 21 } 22 23 int target = -nums[i]; 24 int left = i + 1; 25 int right = length - 1; 26 27 while (left < right) { 28 if (target == nums[left] + nums[right]) { 29 ret.add(Arrays.asList(nums[i], nums[left], nums[right])); 30 // 去重 31 while (left < right && nums[left] == nums[left + 1]) { 32 left++; 33 } 34 while (left < right && nums[right] == nums[right - 1]) { 35 right--; 36 } 37 left++; 38 right--; 39 } else if (target > nums[left] + nums[right]) { 40 left++; 41 } else { 42 right--; 43 } 44 } 45 } 46 return ret; 47 } 48 }
六 top K問題(第347題)
m個數找出出現頻率最大的k個
1. 桶排序解法,比如a出現3次, b出現6次,c出現3次,那么構造出bucket[3] = Arrays.asList(a, c), bucket[6] = Arrays.asList(b)

public class TTest { public static void main(String[] args) { // List<Integer> integers = topKFrequent(new int[]{4, 6, 6, 3, 3, 3, 3}, 2); // List<Integer> integers = topKFrequent(new int[]{1, 1, 1, 2, 2, 2, 3, 3, 3}, 2); // List<Integer> integers = topKFrequent(new int[]{3, 0, 1, 0}, 2); List<Integer> integers = topKFrequent2(new int[]{1}, 1); System.out.println(integers); } /** * 桶排序 */ public static List<Integer> topKFrequent(int[] nums, int k) { Map<Integer, Integer> map = new LinkedHashMap<>(); List<Integer>[] bucket = new List[nums.length]; List<Integer> res = new ArrayList<>(); for (int num : nums) { map.put(num, map.getOrDefault(num, 0) + 1); } for (int key : map.keySet()) { int frequency = map.get(key); if (bucket[frequency] == null) { bucket[frequency] = new ArrayList<>(); } bucket[frequency].add(key); } for (int pos = bucket.length - 1; pos >= 0; pos--) { if (bucket[pos] != null) { for (int i = 0; i < bucket[pos].size() && res.size() < k; i++) res.add(bucket[pos].get(i)); } } return res; } }
1. 堆排序解法,用集合中的PriorityQueue

/** * 堆排序原理,優先隊列 */ public static List<Integer> topKFrequent2(int[] nums, int k) { Map<Integer, Integer> map = new LinkedHashMap<>(); for (int n : nums) { map.put(n, map.getOrDefault(n, 0) + 1); } PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>((l, r) -> r.getValue() - l.getValue()); heap.addAll(map.entrySet()); List<Integer> res = new ArrayList<>(); while (res.size() < k) { res.add(heap.poll().getKey()); } return res; }
七 二叉樹左右對稱(第101題)
返回是否一個二叉樹為左右對稱結構
1. 我的解法(accepted): 比較中序和先序是否完全相等

/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public boolean isSymmetric(TreeNode root) { List<Integer> list1 = new ArrayList(); inOrder(root, list1); List<Integer> list2 = new ArrayList(); inOrder1(root, list2); List<Integer> list3 = new ArrayList(); preOrder(root, list3); List<Integer> list4 = new ArrayList(); preOrder1(root, list4); return isSame(list1, list2) && isSame(list3, list4); } private boolean isSame(List<Integer> list1, List<Integer> list2) { if (list1.size() != list2.size()) { return false; } for (int i = 0; i < list1.size(); i++) { if (list1.get(i) != list2.get(i)) { return false; } } return true; } private void inOrder(TreeNode root, List<Integer> numList) { if (root == null) { return; } inOrder(root.left, numList); numList.add(root.val); inOrder(root.right, numList); } private void inOrder1(TreeNode root, List<Integer> numList) { if (root == null) { return; } inOrder1(root.right, numList); numList.add(root.val); inOrder1(root.left, numList); } private void preOrder(TreeNode root, List<Integer> numList) { if (root == null) { return; } numList.add(root.val); preOrder(root.left, numList); preOrder(root.right, numList); } private void preOrder1(TreeNode root, List<Integer> numList) { if (root == null) { return; } numList.add(root.val); preOrder1(root.right, numList); preOrder1(root.left, numList); } }
2. 遞歸

public boolean isSymmetric(TreeNode root) { return isMirror(root, root); } public boolean isMirror(TreeNode t1, TreeNode t2) { if (t1 == null && t2 == null) return true; if (t1 == null || t2 == null) return false; return (t1.val == t2.val) && isMirror(t1.right, t2.left) && isMirror(t1.left, t2.right); }
3. 非遞歸

public boolean isSymmetric(TreeNode root) { Queue<TreeNode> q = new LinkedList<>(); q.add(root); q.add(root); while (!q.isEmpty()) { TreeNode t1 = q.poll(); TreeNode t2 = q.poll(); if (t1 == null && t2 == null) continue; if (t1 == null || t2 == null) return false; if (t1.val != t2.val) return false; q.add(t1.left); q.add(t2.right); q.add(t1.right); q.add(t2.left); } return true; }