本文整理了常见算法题型与LeetCode题解,涵盖数组、字符串、哈希表、双指针、链表等数据结构,以及栈、队列、回溯、动态规划等核心算法。适合算法初学者及面试备考者,帮助掌握高频题目解题技巧,提升编码效率。
数组
数组是存放在连续内存空间上的相同类型数据的集合。
注意:
数组下标都是从0开始的。
数组内存空间的地址是连续的
正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。数组的元素是不能删的,只能覆盖。
第一题:704. 二分查找 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 public static int search (int [] nums, int target) { for (int i = 0 ; i < nums.length; i++) { if (nums[i] == target){ return i; } } return -1 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public int search (int [] nums, int target) { if (target < nums[0 ] || target > nums[nums.length - 1 ]) { return -1 ; } int left = 0 ; int right = nums.length - 1 ; int mid = (left + right) / 2 ; while (left <= right) { if (target > nums[mid]) { left = mid + 1 ; mid = (left + right) / 2 ; } if (target < nums[mid]) { right = mid - 1 ; mid = (left + right) / 2 ; } if (target == nums[mid]) { return mid; } } return -1 ; }
第二题:27. 移除元素 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static int removeElement2 (int [] nums, int val) { int size = nums.length; for (int i = 0 ; i < size; i++) { if (nums[i] == val) { for (int j = i + 1 ; j < size; j++) { nums[j - 1 ] = nums[j]; } i--; size--; } } return size; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static int removeElement (int [] nums, int val) { int slowIndex = 0 ; int fastIndex = 0 ; for (fastIndex = 0 ; fastIndex < nums.length; fastIndex++) { if (nums[fastIndex] != val) { nums[slowIndex] = nums[fastIndex]; slowIndex++; } } return slowIndex; }
第三题:283. 移动零 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void moveZeroes2 (int [] nums) { if (nums == null ) { return ; } int i = 0 ; int j = 0 ; while (i < nums.length) { if (nums[i] != 0 ) { int tmp = nums[j]; nums[j] = nums[i]; nums[i] = tmp; j++; } i++; } }
第四题:844. 比较含退格的字符串 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public boolean backspaceCompare (String s, String t) { StringBuilder ssb = new StringBuilder (); StringBuilder tsb = new StringBuilder (); for (char c : s.toCharArray()) { if (c != '#' ) { ssb.append(c); } else if (ssb.length() > 0 ) { ssb.deleteCharAt(ssb.length() - 1 ); } } for (char c : t.toCharArray()) { if (c != '#' ) { tsb.append(c); } else if (tsb.length() > 0 ) { tsb.deleteCharAt(tsb.length() - 1 ); } } return ssb.toString().equals(tsb.toString()); }
第五题:977. 有序数组的平方 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 public int [] sortedSquares(int [] nums) { int [] arr = new int [nums.length]; for (int i = 0 ; i < nums.length; i++) { arr[i] = nums[i] * nums[i]; } Arrays.sort(arr); return arr; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int [] sortedSquares(int [] nums) { int right = nums.length - 1 ; int left = 0 ; int [] result = new int [nums.length]; int index = result.length - 1 ; while (left <= right) { if (nums[left] * nums[left] > nums[right] * nums[right]) { result[index--] = nums[left] * nums[left]; ++left; } else { result[index--] = nums[right] * nums[right]; --right; } } return result; }
第六题:209. 长度最小的子数组 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public int minSubArrayLen (int target, int [] nums) { int n = nums.length; if (n == 0 ) { return 0 ; } int ans = Integer.MAX_VALUE; for (int i = 0 ; i < nums.length; i++) { int sum = 0 ; for (int j = i; j < nums.length; j++) { sum = sum + nums[j]; if (sum >= target){ ans = Math.min(ans,j - i + 1 ); break ; } } } return ans == Integer.MAX_VALUE ? 0 : ans; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static int minSubArrayLen2 (int s, int [] nums) { int left = 0 ; int sum = 0 ; int result = Integer.MAX_VALUE; int subLength = 0 ; for (int right = 0 ; right < nums.length; right++) { sum += nums[right]; while (sum >= s) { subLength = right - left + 1 ; result = Math.min(result, subLength); sum -= nums[left++]; } } return result == Integer.MAX_VALUE ? 0 : result; }
相关题目推荐
字符串 第一题:344. 反转字符串 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 public void reverseString (char [] s) { int j = s.length - 1 ; for (int i = 0 ; i < s.length / 2 ; i++) { char temp = s[i]; s[i] = s[j]; s[j] = temp; j--; } }
第二题:541. 反转字符串 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static String reverseStr (String s, int k) { char [] chs = s.toCharArray(); for (int i = 0 ; i < chs.length; i += 2 * k) { int start = i; int end = Math.min(chs.length - 1 ,start + k - 1 ); while (start <end){ char temp = chs[start]; chs[start] = chs[end]; chs[end] = temp; start++; end--; } } return String.valueOf(chs); }
第三题:54. 替换数字(第八期模拟笔试) (kamacoder.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.util.Scanner;public class Main { public static void main (String[] args) { Scanner sc = new Scanner (System.in); String str = sc.next(); StringBuilder sb = new StringBuilder (str); for (int i = 0 ; i < sb.length(); i++) { if (sb.charAt(i) >= '0' && sb.charAt(i) <= '9' ){ sb.deleteCharAt(i); sb.insert(i,"number" ); i = i + 5 ; } } System.out.println(String.valueOf(sb).toString()); } }
第四题:151. 反转字符串中的单词 - 力扣(LeetCode) (困难)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public static String reverseWords (String s) { StringBuilder sb = removeSpace(s); String str = sb.toString(); String[] split = str.split(" " ); int j = split.length - 1 ; for (int i = 0 ; i < split.length / 2 ; i++, j--) { String temp = split[i]; split[i] = split[j]; split[j] = temp; } StringBuilder s1 = new StringBuilder (); for (int i = 0 ; i < split.length; i++) { if (i == split.length - 1 ){ s1.append(split[i]); }else { s1.append(split[i]).append(" " ); } } String result = s1.toString(); return result; } private static StringBuilder removeSpace (String s) { int start = 0 ; int end = s.length() - 1 ; while (s.charAt(start) == ' ' ){ start++; } while (s.charAt(end) == ' ' ){ end--; } StringBuilder sb = new StringBuilder (); while (start <= end){ char c = s.charAt(start); if (c != ' ' || sb.charAt(sb.length() - 1 ) != ' ' ){ sb.append(c); } start++; } return sb; }
第五题:459. 重复的子字符串 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 public boolean repeatedSubstringPattern (String s) { StringBuilder sb = new StringBuilder (); sb.append(s).append(s); String substring = sb.substring(1 , sb.length() - 1 ); boolean contains = substring.contains(s); return contains; }
哈希表
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法 。牺牲了空间换取了时间
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
第一题:242. 有效的字母异位词 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static boolean isAnagram2 (String s, String t) { if (s.length() != t.length()){ return false ; } char [] ch_s = s.toCharArray(); char [] ch_t = t.toCharArray(); Arrays.sort(ch_s); Arrays.sort(ch_t); for (int i = 0 ; i < ch_s.length; i++) { if (ch_s[i] != ch_t[i]){ return false ; } } return true ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public boolean isAnagram (String s, String t) { if (s.length() != t.length()){ return false ; } int [] arr = new int [26 ]; char [] ch_s = s.toCharArray(); char [] ch_t = t.toCharArray(); for (int i = 0 ; i < ch_s.length; i++) { arr[ch_s[i] - 'a' ]++; } for (int i = 0 ; i < ch_t.length; i++) { arr[ch_t[i] - 'a' ]--; } for (int i = 0 ; i < arr.length; i++) { if (arr[i] != 0 ){ return false ; } } return true ; }
第二题:349. 两个数组的交集 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public int [] intersection(int [] nums1, int [] nums2) { if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0 ) { return new int [0 ]; } Set<Integer> set1 = new HashSet <>(); Set<Integer> resSet = new HashSet <>(); for (int i : nums1) { set1.add(i); } for (int i : nums2) { if (set1.contains(i)) { resSet.add(i); } } int [] array = new int [resSet.size()]; int j = 0 ; for (int i : resSet) { array[j++] = i; } return array; }
第三题:202. 快乐数 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static boolean isHappy (int n) { Set<Integer> record = new HashSet <>(); while (n != 1 && !record.contains(n)) { record.add(n); n = getNextNumber(n); } return n == 1 ; } private static int getNextNumber (int n) { int res = 0 ; while (n > 0 ) { int temp = n % 10 ; res += temp * temp; n = n / 10 ; } return res; }
第四题:1. 两数之和 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static int [] twoSum(int [] nums, int target) { if (nums == null || nums.length == 0 ) { return res; } int [] res = new int [2 ]; int sum = 0 ; for (int i = 0 ; i < nums.length; i++) { for (int j = i + 1 ; j < nums.length; j++) { sum = nums[i] + nums[j]; if (sum == target) { res[0 ] = i; res[1 ] = j; return res; } } } return res; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public int [] twoSum(int [] nums, int target) { int [] res = new int [2 ]; if (nums == null || nums.length == 0 ) { return res; } Map<Integer, Integer> map = new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { int temp = target - nums[i]; if (map.containsKey(temp)) { res[1 ] = i; res[0 ] = map.get(temp); break ; } map.put(nums[i], i); } return res; }
第五题:454. 四数相加 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static int fourSumCount1 (int [] nums1, int [] nums2, int [] nums3, int [] nums4) { int count = 0 ; for (int i = 0 ; i < nums1.length; i++) { for (int j = 0 ; j < nums2.length; j++) { for (int k = 0 ; k < nums3.length; k++) { for (int g = 0 ; g < nums4.length; g++) { if (nums1[i] + nums2[j] + nums3[k] + nums4[g] == 0 ) { count++; } } } } } return count; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static int fourSumCount2 (int [] nums1, int [] nums2, int [] nums3, int [] nums4) { int res = 0 ; Map<Integer, Integer> map = new HashMap <Integer, Integer>(); for (int i : nums1) { for (int j : nums2) { int sum = i + j; map.put(sum, map.getOrDefault(sum, 0 ) + 1 ); } } for (int i : nums3) { for (int j : nums4) { int sum = i + j; res += map.getOrDefault(-sum, 0 ); } } return res; }
第六题:383. 赎金信 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static boolean canConstruct (String ransomNote, String magazine) { char [] ch_r = ransomNote.toCharArray(); char [] ch_m = magazine.toCharArray(); for (int i = 0 ; i < ch_r.length; i++) { for (int j = 0 ; j < ch_m.length; j++) { if (ch_r[i] == ch_m[j]){ ch_r[i] = '0' ; ch_m[j] = '1' ; break ; } } } for (int i = 0 ; i < ch_r.length; i++) { if (ch_r[i] != '0' ){ return false ; } } return true ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public boolean canConstruct (String ransomNote, String magazine) { if (ransomNote.length() > magazine.length()) { return false ; } int [] record = new int [26 ]; char [] ch_r = ransomNote.toCharArray(); char [] ch_m = magazine.toCharArray(); for (int i = 0 ; i < ch_m.length; i++) { record[ch_m[i] - 'a' ]++; } for (int i = 0 ; i < ch_r.length; i++) { record[ch_r[i] - 'a' ]--; } for (int i = 0 ; i < record.length; i++) { if (record[i] < 0 ){ return false ; } } return true ; }
第七题:15. 三数之和 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static List<List<Integer>> threeSum (int [] nums) { HashSet<List<Integer>> set = new HashSet <>(); for (int i = 0 ; i < nums.length - 2 ; i++) { for (int j = i + 1 ; j < nums.length - 1 ; j++) { for (int k = j + 1 ; k < nums.length; k++) { if (nums[i] + nums[j] + nums[k] == 0 ) { List<Integer> list = Arrays.asList(nums[i], nums[j], nums[k]); Collections.sort(list); set.add(list); } } } } return new ArrayList <>(set); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public static List<List<Integer>> threeSum (int [] nums) { List<List<Integer>> result = new ArrayList <>(); Arrays.sort(nums); for (int i = 0 ; i < nums.length; i++) { if (nums[i] > 0 ) { return result; } if (i > 0 && nums[i] == nums[i - 1 ]) { continue ; } int left = i + 1 ; int right = nums.length - 1 ; while (right > left) { int sum = nums[i] + nums[left] + nums[right]; if (sum > 0 ) { right--; } if (sum < 0 ) { left++; } if (sum == 0 ){ result.add(Arrays.asList(nums[i], nums[left], nums[right])); while (right > left && nums[right] == nums[right - 1 ]) right--; while (right > left && nums[left] == nums[left + 1 ]) left++; right--; left++; } } } return result; }
第八题:18. 四数之和 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public List<List<Integer>> fourSum (int [] nums, int target) { List<List<Integer>> result = new ArrayList <>(); Arrays.sort(nums); for (int i = 0 ; i < nums.length; i++) { if (nums[i] > 0 && nums[i] > target) { return result; } if (i > 0 && nums[i - 1 ] == nums[i]) { continue ; } for (int j = i + 1 ; j < nums.length; j++) { if (j > i + 1 && nums[j - 1 ] == nums[j]) { continue ; } int left = j + 1 ; int right = nums.length - 1 ; while (right > left) { long sum = (long ) nums[i] + nums[j] + nums[left] + nums[right]; if (sum > target) { right--; } if (sum < target) { left++; } if (sum == target) { result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); while (right > left && nums[right] == nums[right - 1 ]) right--; while (right > left && nums[left] == nums[left + 1 ]) left++; left++; right--; } } } } return result; }
双指针 第一题:27. 移除元素 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int removeElement (int [] nums, int val) { int i = 0 ; int slow = 0 ; for (i = 0 ; i < nums.length; i++) { if (nums[i] != val) { nums[slow] = nums[i]; slow++; } } return slow; }
第二题:344. 反转字符串 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 public void reverseString (char [] s) { int j = s.length - 1 ; for (int i = 0 ; i < s.length / 2 ; i++) { char temp = s[i]; s[i] = s[j]; s[j] = temp; j--; } }
第三题:151. 反转字符串中的单词 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public String reverseWords (String s) { StringBuilder sb = removeSpace(s); String str = sb.toString(); String[] split = str.split(" " ); int j = split.length - 1 ; for (int i = 0 ; i < split.length / 2 ; i++, j--) { String temp = split[i]; split[i] = split[j]; split[j] = temp; } StringBuilder s1 = new StringBuilder (); for (int i = 0 ; i < split.length; i++) { if (i == split.length - 1 ){ s1.append(split[i]); }else { s1.append(split[i]).append(" " ); } } String result = s1.toString(); return result; } private static StringBuilder removeSpace (String s) { int start = 0 ; int end = s.length() - 1 ; while (s.charAt(start) == ' ' ){ start++; } while (s.charAt(end) == ' ' ){ end--; } StringBuilder sb = new StringBuilder (); while (start <= end){ char c = s.charAt(start); if (c != ' ' || sb.charAt(sb.length() - 1 ) != ' ' ){ sb.append(c); } start++; } return sb; }
第四题:206. 反转链表 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 public ListNode reverseList (ListNode head) { ListNode prev = null ; ListNode cur = head; ListNode temp = null ; while (cur != null ) { temp = cur.next; cur.next = prev; prev = cur; cur = temp; } return prev; }
第五题:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public ListNode removeNthFromEnd (ListNode head, int n) { ListNode dummyNode = new ListNode (0 ); dummyNode.next = head; ListNode fastIndex = dummyNode; ListNode slowIndex = dummyNode; for (int i = 0 ; i < n + 1 ; i++){ fastIndex = fastIndex.next; } while (fastIndex != null ){ fastIndex = fastIndex.next; slowIndex = slowIndex.next; } slowIndex.next = slowIndex.next.next; return dummyNode.next; }
第六题:面试题 02.07. 链表相交 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public ListNode getIntersectionNode (ListNode headA, ListNode headB) { ListNode curA = headA; ListNode curB = headB; int lenA = 0 , lenB = 0 ; while (curA != null ) { lenA++; curA = curA.next; } while (curB != null ) { lenB++; curB = curB.next; } curA = headA; curB = headB; if (lenB > lenA) { int tmpLen = lenA; lenA = lenB; lenB = tmpLen; ListNode tmpNode = curA; curA = curB; curB = tmpNode; } int gap = lenA - lenB; while (gap-- > 0 ) { curA = curA.next; } while (curA != null ) { if (curA == curB) { return curA; } curA = curA.next; curB = curB.next; } return null ; }
第七题:142. 环形链表 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public ListNode detectCycle (ListNode head) { ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; if (slow == fast) { ListNode index1 = fast; ListNode index2 = head; while (index1 != index2) { index1 = index1.next; index2 = index2.next; } return index1; } } return null ; }
第八题:15. 三数之和 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public List<List<Integer>> threeSum (int [] nums) { List<List<Integer>> result = new ArrayList <>(); Arrays.sort(nums); for (int i = 0 ; i < nums.length; i++) { if (nums[i] > 0 ) { return result; } if (i > 0 && nums[i] == nums[i - 1 ]) { continue ; } int left = i + 1 ; int right = nums.length - 1 ; while (right > left) { int sum = nums[i] + nums[left] + nums[right]; if (sum > 0 ) { right--; } if (sum < 0 ) { left++; } if (sum == 0 ) { result.add(Arrays.asList(nums[i], nums[left], nums[right])); while (right > left && nums[right] == nums[right - 1 ]) right--; while (right > left && nums[left] == nums[left + 1 ]) left++; right--; left++; } } } return result; }
第九题:18. 四数之和 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public List<List<Integer>> fourSum (int [] nums, int target) { List<List<Integer>> result = new ArrayList <>(); Arrays.sort(nums); for (int i = 0 ; i < nums.length; i++) { if (nums[i] > 0 && nums[i] > target) { return result; } if (i > 0 && nums[i - 1 ] == nums[i]) { continue ; } for (int j = i + 1 ; j < nums.length; j++) { if (j > i + 1 && nums[j - 1 ] == nums[j]) { continue ; } int left = j + 1 ; int right = nums.length - 1 ; while (right > left) { long sum = (long ) nums[i] + nums[j] + nums[left] + nums[right]; if (sum > target) { right--; } if (sum < target) { left++; } if (sum == target) { result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); while (right > left && nums[right] == nums[right - 1 ]) right--; while (right > left && nums[left] == nums[left + 1 ]) left++; left++; right--; } } } } return result; }
链表
链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
链表分为单链表和双链表:
单链表:每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针)。单链表中的指针域只能指向节点的下一个节点。
双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
循环链表:顾名思义,就是链表首尾相连。
链表与数组的比较:
定义数组时长度是固定的,链表的长度不固定。
数组适合频繁的查询,链表适合频繁增删。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private class ListNode { int val; ListNode next; public ListNode () { } public ListNode (int val) { this .val = val; } public ListNode (int val, ListNode next) { this .val = val; this .next = next; } }
第一题:203. 移除链表元素 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public ListNode removeElements (ListNode head, int val) { if (head == null ) { return head; } ListNode dummy = new ListNode (-1 , head); ListNode pre = dummy; ListNode cur = head; while (cur != null ) { if (cur.val == val) { pre.next = cur.next; } else { pre = cur; } cur = cur.next; } return dummy.next; }
第二题:707. 设计链表 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 class MyLinkedList { class ListNode { int val; ListNode next; ListNode() { } ListNode(int val) { this .val = val; } } int size; ListNode head; public MyLinkedList () { size = 0 ; head = new ListNode (0 ); } public int get (int index) { if (index < 0 || index > size - 1 ) { return -1 ; } ListNode currentNode = head; for (int i = 0 ; i <= index; i++) { currentNode = currentNode.next; } return currentNode.val; } public void addAtHead (int val) { addAtIndex(0 , val); } public void addAtTail (int val) { addAtIndex(size, val); } public void addAtIndex (int index, int val) { if (index > size) { return ; } if (index < 0 ) { index = 0 ; } size++; ListNode pred = head; for (int i = 0 ; i < index; i++) { pred = pred.next; } ListNode toAdd = new ListNode (val); toAdd.next = pred.next; pred.next = toAdd; } public void deleteAtIndex (int index) { if (index < 0 || index >= size) { return ; } size--; if (index == 0 ) { head = head.next; return ; } ListNode pred = head; for (int i = 0 ; i < index; i++) { pred = pred.next; } pred.next = pred.next.next; } }
第三题:206. 反转链表 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 public ListNode reverseList (ListNode head) { ListNode prev = null ; ListNode cur = head; ListNode temp = null ; while (cur != null ) { temp = cur.next; cur.next = prev; prev = cur; cur = temp; } return prev; }
第四题:24. 两两交换链表中的节点 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public ListNode swapPairs (ListNode head) { ListNode dumyhead = new ListNode (-1 ); dumyhead.next = head; ListNode cur = dumyhead; ListNode temp; ListNode firstnode; ListNode secondnode; while (cur.next != null && cur.next.next != null ) { temp = cur.next.next.next; firstnode = cur.next; secondnode = cur.next.next; cur.next = secondnode; secondnode.next = firstnode; firstnode.next = temp; cur = firstnode; } return dumyhead.next; }
第五题:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public ListNode removeNthFromEnd (ListNode head, int n) { ListNode dummyNode = new ListNode (0 ); dummyNode.next = head; ListNode fastIndex = dummyNode; ListNode slowIndex = dummyNode; for (int i = 0 ; i < n + 1 ; i++) { fastIndex = fastIndex.next; } while (fastIndex != null ) { fastIndex = fastIndex.next; slowIndex = slowIndex.next; } slowIndex.next = slowIndex.next.next; return dummyNode.next; }
第六题:面试题 02.07. 链表相交 - 力扣(LeetCode) (困难)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public ListNode getIntersectionNode (ListNode headA, ListNode headB) { ListNode curA = headA; ListNode curB = headB; int lenA = 0 , lenB = 0 ; while (curA != null ) { lenA++; curA = curA.next; } while (curB != null ) { lenB++; curB = curB.next; } curA = headA; curB = headB; if (lenB > lenA) { int tmpLen = lenA; lenA = lenB; lenB = tmpLen; ListNode tmpNode = curA; curA = curB; curB = tmpNode; } int gap = lenA - lenB; while (gap-- > 0 ) { curA = curA.next; } while (curA != null ) { if (curA == curB) { return curA; } curA = curA.next; curB = curB.next; } return null ; }
第七题:142. 环形链表 II - 力扣(LeetCode) (困难)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public ListNode detectCycle (ListNode head) { ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null ) { slow = slow.next; fast = fast.next.next; if (slow == fast) { ListNode index1 = fast; ListNode index2 = head; while (index1 != index2) { index1 = index1.next; index2 = index2.next; } return index1; } } return null ; }
栈和队列
队列是先进先出,栈是先进后出。
Stack
方法摘要:
push()
:把项压入堆栈顶部
pop()
:移除堆栈顶部的对象,并作为此函数的值返回该对象
empty()
:测试堆栈是否为空。
peek()
:查看堆栈顶部的对象,但不从堆栈中移除它。
search()
:返回对象在堆栈中的位置,以 1 为基数。返回值 -1 表示此对象不在堆栈中
Queue
方法摘要:
add()
:将指定的元素插入此队列。成功时返回 true,如果当前没有可用的空间,则抛出异常。
offer()
:将指定的元素插入此队列。如果该元素已添加到此队列,则返回 true;否则返回 false
remove()
:获取并移除此队列的头。此方法与 poll 唯一的不同在于:此队列为空时将抛出一个异常。
poll()
:获取并移除此队列的头,如果此队列为空,则返回 null。
peek()
:获取但不移除此队列的头;如果此队列为空,则返回 null。
element()
:获取,但是不移除此队列的头。此方法与 peek 唯一的不同在于:此队列为空时将抛出一个异常。
第一题:232. 用栈实现队列 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import java.util.Stack;public class MyQueue { Stack<Integer> stackIn; Stack<Integer> stackOut; public MyQueue () { stackIn = new Stack <>(); stackOut = new Stack <>(); } public void push (int x) { stackIn.push(x); } public int pop () { dumpstackIn(); return stackOut.pop(); } public int peek () { dumpstackIn(); return stackOut.peek(); } public boolean empty () { return stackIn.isEmpty() && stackOut.isEmpty(); } private void dumpstackIn () { if (!stackOut.isEmpty()) return ; while (!stackIn.isEmpty()) { stackOut.push(stackIn.pop()); } } }
第二题:225. 用队列实现栈 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import java.util.LinkedList;import java.util.Queue;public class MyStack { Queue<Integer> queue; public MyStack () { queue = new LinkedList <>(); } public void push (int x) { queue.add(x); } public int pop () { rePosition(); return queue.poll(); } public int top () { rePosition(); int result = queue.poll(); queue.add(result); return result; } public boolean empty () { return queue.isEmpty(); } public void rePosition () { int size = queue.size(); size--; while (size-- > 0 ) queue.add(queue.poll()); } }
第三题:20. 有效的括号 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static boolean isValid (String s) { if (s.length() % 2 != 0 ) { return false ; } Deque<Character> deque = new LinkedList <>(); char ch; for (int i = 0 ; i < s.length(); i++) { ch = s.charAt(i); if (ch == '(' ) { deque.push(')' ); } else if (ch == '{' ) { deque.push('}' ); } else if (ch == '[' ) { deque.push(']' ); } else if (deque.isEmpty() || deque.peek() != ch) { return false ; } else { deque.pop(); } } return deque.isEmpty(); }
第四题:1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public static String removeDuplicates1 (String S) { ArrayDeque<Character> deque = new ArrayDeque <>(); char ch; for (int i = 0 ; i < S.length(); i++) { ch = S.charAt(i); if (deque.isEmpty() || deque.peek() != ch) { deque.push(ch); } else { deque.pop(); } } String str = "" ; while (!deque.isEmpty()) { str = deque.pop() + str; } return str; } public static String removeDuplicates2 (String s) { StringBuffer res = new StringBuffer (); int top = -1 ; for (int i = 0 ; i < s.length(); i++) { char c = s.charAt(i); if (top >= 0 && res.charAt(top) == c) { res.deleteCharAt(top); top--; } else { res.append(c); top++; } } return res.toString(); } public static String removeDuplicates3 (String s) { char [] ch = s.toCharArray(); int fast = 0 ; int slow = 0 ; while (fast < s.length()) { ch[slow] = ch[fast]; if (slow > 0 && ch[slow] == ch[slow - 1 ]) { slow--; } else { slow++; } fast++; } return new String (ch, 0 , slow); }
第五题:150. 逆波兰表达式求值 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static int evalRPN (String[] tokens) { Deque<Integer> stack = new LinkedList (); for (String s : tokens) { if ("+" .equals(s)) { stack.push(stack.pop() + stack.pop()); } else if ("-" .equals(s)) { stack.push(-stack.pop() + stack.pop()); } else if ("*" .equals(s)) { stack.push(stack.pop() * stack.pop()); } else if ("/" .equals(s)) { int temp1 = stack.pop(); int temp2 = stack.pop(); stack.push(temp2 / temp1); } else { stack.push(Integer.valueOf(s)); } } return stack.pop(); }
第六题:239. 滑动窗口最大值 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static int [] maxSlidingWindow(int [] nums, int k) { if (k == 1 ) { return nums; } int [] res = new int [nums.length - k + 1 ]; int max = Integer.MIN_VALUE; int index = 0 ; for (int i = 0 ; i < nums.length; i++) { for (int j = i; j < k + i; j++) { while (j < k + i && j < nums.length) { if (nums[j] > max) { max = nums[j]; j++; } else { j++; } } res[index] = max; max = Integer.MIN_VALUE; index++; if (index == nums.length - k + 1 ) { return res; } } } return res; }
第七题:347. 前 K 个高频元素 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public int [] topKFrequent(int [] nums, int k) { Map<Integer, Integer> map = new HashMap <>(); for (int num : nums) { map.put(num, map.getOrDefault(num, 0 ) + 1 ); } List<Integer> list = new ArrayList <>(); Set<Integer> keySet = map.keySet(); for (Integer key : keySet) { Integer value = map.get(key); list.add(value); } list.sort(new Comparator <Integer>() { @Override public int compare (Integer o1, Integer o2) { return o2 - o1; } }); int [] res = new int [k]; for (int i = 0 ; i < k;) { Integer value = list.get(i); for (Integer key : keySet) { Integer val = map.get(key); if (Objects.equals(value, val)) { res[i] = key; i++; } } } return res; }
二叉树
二叉树的分类
满二叉树:如果一棵二叉树只有度为 0 的结点和度为 2 的结点,并且度为 0 的结点在同一层上,则这棵二叉树为满二叉树。
完全二叉树
二叉搜索树
平衡二叉搜索树
回溯(递归)算法
提示:拿到一道题,先想暴力解法,发现有点不能确定几层循环后,想起回溯,回溯题目都可以抽象成一个树形结构
回溯算法的三部曲:
回溯函数模板返回值以及参数
回溯函数终止条件
回溯搜索的遍历过程
回溯法,一般可以解决如下几种问题:
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
回溯算法的模板:
1 2 3 4 5 6 7 8 9 10 11 12 void backtracking (参数) { if (终止条件) { 存放结果; return ; } for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); 回溯,撤销处理结果 } }
组合第一题:77. 组合 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { List<List<Integer>> result = new ArrayList <>(); LinkedList<Integer> path = new LinkedList <>(); public List<List<Integer>> combine (int n, int k) { backtracking(n, k, 1 ); return result; } public void backtracking (int n, int k, int startIndex) { if (path.size() == k) { result.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i <= n; i++) { path.add(i); backtracking(n, k, i + 1 ); path.removeLast(); } } }
组合第二题:216. 组合总和 III - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { LinkedList<Integer> path = new LinkedList <>(); List<List<Integer>> ans = new ArrayList <>(); public List<List<Integer>> combinationSum3 (int k, int n) { build(k, n, 1 , 0 ); return ans; } private void build (int k, int n, int startIndex, int sum) { if (sum > n) return ; if (path.size() > k) return ; if (sum == n && path.size() == k) { ans.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i <= 9 ; i++) { path.add(i); sum += i; build(k, n, i + 1 , sum); sum -= i; path.removeLast(); } } }
组合第三题:17. 电话号码的字母组合 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { List<String> list = new ArrayList <>(); public List<String> letterCombinations (String digits) { if (digits == null || digits.length() == 0 ) { return list; } String[] numString = { "" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" }; backTracking(digits, numString, 0 ); return list; } StringBuilder temp = new StringBuilder (); public void backTracking (String digits, String[] numString, int num) { if (num == digits.length()) { list.add(temp.toString()); return ; } String str = numString[digits.charAt(num) - '0' ]; for (int i = 0 ; i < str.length(); i++) { temp.append(str.charAt(i)); backTracking(digits, numString, num + 1 ); temp.deleteCharAt(temp.length() - 1 ); } } }
组合第四题:39. 组合总和 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public List<List<Integer>> combinationSum (int [] candidates, int target) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(candidates); backtracking(res, new ArrayList <>(), candidates, target, 0 , 0 ); return res; } public void backtracking (List<List<Integer>> res, List<Integer> path, int [] candidates, int target, int sum, int idx) { if (sum == target) { res.add(new ArrayList <>(path)); return ; } for (int i = idx; i < candidates.length; i++) { if (sum + candidates[i] > target) break ; path.add(candidates[i]); backtracking(res, path, candidates, target, sum + candidates[i], i); path.remove(path.size() - 1 ); } } }
组合第五题:40. 组合总和 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution { LinkedList<Integer> path = new LinkedList <>(); List<List<Integer>> ans = new ArrayList <>(); boolean [] used; int sum = 0 ; public List<List<Integer>> combinationSum2 (int [] candidates, int target) { used = new boolean [candidates.length]; Arrays.fill(used, false ); Arrays.sort(candidates); backTracking(candidates, target, 0 ); return ans; } private void backTracking (int [] candidates, int target, int startIndex) { if (sum == target) { ans.add(new ArrayList (path)); } for (int i = startIndex; i < candidates.length; i++) { if (sum + candidates[i] > target) { break ; } if (i > 0 && candidates[i] == candidates[i - 1 ] && !used[i - 1 ]) { continue ; } used[i] = true ; sum += candidates[i]; path.add(candidates[i]); backTracking(candidates, target, i + 1 ); used[i] = false ; sum -= candidates[i]; path.removeLast(); } } }
切割第一题:131. 分割回文串 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution { List<List<String>> lists = new ArrayList <>(); Deque<String> deque = new LinkedList <>(); public List<List<String>> partition (String s) { backTracking(s, 0 ); return lists; } private void backTracking (String s, int startIndex) { if (startIndex >= s.length()) { lists.add(new ArrayList (deque)); return ; } for (int i = startIndex; i < s.length(); i++) { if (isPalindrome(s, startIndex, i)) { String str = s.substring(startIndex, i + 1 ); deque.addLast(str); } else { continue ; } backTracking(s, i + 1 ); deque.removeLast(); } } private boolean isPalindrome (String s, int startIndex, int end) { for (int i = startIndex, j = end; i < j; i++, j--) { if (s.charAt(i) != s.charAt(j)) { return false ; } } return true ; } }
切割第二题:93. 复原 IP 地址 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Solution { List<String> result = new ArrayList <>(); public List<String> restoreIpAddresses (String s) { if (s.length() > 12 ) return result; backTrack(s, 0 , 0 ); return result; } private void backTrack (String s, int startIndex, int pointNum) { if (pointNum == 3 ) { if (isValid(s, startIndex, s.length() - 1 )) { result.add(s); } return ; } for (int i = startIndex; i < s.length(); i++) { if (isValid(s, startIndex, i)) { s = s.substring(0 , i + 1 ) + "." + s.substring(i + 1 ); pointNum++; backTrack(s, i + 2 , pointNum); pointNum--; s = s.substring(0 , i + 1 ) + s.substring(i + 2 ); } else { break ; } } } private Boolean isValid (String s, int start, int end) { if (start > end) { return false ; } if (s.charAt(start) == '0' && start != end) { return false ; } int num = 0 ; for (int i = start; i <= end; i++) { if (s.charAt(i) > '9' || s.charAt(i) < '0' ) { return false ; } num = num * 10 + (s.charAt(i) - '0' ); if (num > 255 ) { return false ; } } return true ; } }
子集第一题:78. 子集 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { List<List<Integer>> result = new ArrayList <>(); LinkedList<Integer> path = new LinkedList <>(); public List<List<Integer>> subsets (int [] nums) { subsetsHelper(nums, 0 ); return result; } private void subsetsHelper (int [] nums, int startIndex) { result.add(new ArrayList <>(path)); if (startIndex >= nums.length) { return ; } for (int i = startIndex; i < nums.length; i++) { path.add(nums[i]); subsetsHelper(nums, i + 1 ); path.removeLast(); } } }
子集第二题:90. 子集 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { List<List<Integer>> result = new ArrayList <>(); LinkedList<Integer> path = new LinkedList <>(); boolean [] used; public List<List<Integer>> subsetsWithDup (int [] nums) { if (nums.length == 0 ) { result.add(path); return result; } Arrays.sort(nums); used = new boolean [nums.length]; subsetsWithDupHelper(nums, 0 ); return result; } private void subsetsWithDupHelper (int [] nums, int startIndex) { result.add(new ArrayList <>(path)); if (startIndex >= nums.length) { return ; } for (int i = startIndex; i < nums.length; i++) { if (i > 0 && nums[i] == nums[i - 1 ] && !used[i - 1 ]) { continue ; } path.add(nums[i]); used[i] = true ; subsetsWithDupHelper(nums, i + 1 ); path.removeLast(); used[i] = false ; } } }
排列第一题:491. 非递减子序列 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { List<List<Integer>> result = new ArrayList <>(); List<Integer> path = new ArrayList <>(); public List<List<Integer>> findSubsequences (int [] nums) { backTracking(nums, 0 ); return result; } private void backTracking (int [] nums, int startIndex) { if (path.size() >= 2 ) result.add(new ArrayList <>(path)); HashSet<Integer> hs = new HashSet <>(); for (int i = startIndex; i < nums.length; i++) { if (!path.isEmpty() && path.get(path.size() - 1 ) > nums[i] || hs.contains(nums[i])) continue ; hs.add(nums[i]); path.add(nums[i]); backTracking(nums, i + 1 ); path.remove(path.size() - 1 ); } } }
排列第二题:46. 全排列 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution { List<List<Integer>> result = new ArrayList <>(); LinkedList<Integer> path = new LinkedList <>(); boolean [] used; public List<List<Integer>> permute (int [] nums) { if (nums.length == 0 ) { return result; } used = new boolean [nums.length]; permuteHelper(nums); return result; } private void permuteHelper (int [] nums) { if (path.size() == nums.length) { result.add(new ArrayList <>(path)); return ; } for (int i = 0 ; i < nums.length; i++) { if (used[i]) { continue ; } used[i] = true ; path.add(nums[i]); permuteHelper(nums); path.removeLast(); used[i] = false ; } } }
排列第三题:47. 全排列 II - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { List<List<Integer>> result = new ArrayList <>(); List<Integer> path = new ArrayList <>(); public List<List<Integer>> permuteUnique (int [] nums) { boolean [] used = new boolean [nums.length]; Arrays.fill(used, false ); Arrays.sort(nums); backTrack(nums, used); return result; } private void backTrack (int [] nums, boolean [] used) { if (path.size() == nums.length) { result.add(new ArrayList <>(path)); return ; } for (int i = 0 ; i < nums.length; i++) { if (i > 0 && nums[i] == nums[i - 1 ] && used[i - 1 ] == false ) { continue ; } if (used[i] == false ) { used[i] = true ; path.add(nums[i]); backTrack(nums, used); path.remove(path.size() - 1 ); used[i] = false ; } } } }
棋盘第一题:51. N 皇后 - 力扣(LeetCode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class Solution { List<List<String>> res = new ArrayList <>(); public List<List<String>> solveNQueens (int n) { char [][] chessboard = new char [n][n]; for (char [] c : chessboard) { Arrays.fill(c, '.' ); } backTrack(n, 0 , chessboard); return res; } public void backTrack (int n, int row, char [][] chessboard) { if (row == n) { res.add(Array2List(chessboard)); return ; } for (int col = 0 ; col < n; ++col) { if (isValid(row, col, n, chessboard)) { chessboard[row][col] = 'Q' ; backTrack(n, row + 1 , chessboard); chessboard[row][col] = '.' ; } } } public List Array2List (char [][] chessboard) { List<String> list = new ArrayList <>(); for (char [] c : chessboard) { list.add(String.copyValueOf(c)); } return list; } public boolean isValid (int row, int col, int n, char [][] chessboard) { for (int i = 0 ; i < row; ++i) { if (chessboard[i][col] == 'Q' ) { return false ; } } for (int i = row - 1 , j = col - 1 ; i >= 0 && j >= 0 ; i--, j--) { if (chessboard[i][j] == 'Q' ) { return false ; } } for (int i = row - 1 , j = col + 1 ; i >= 0 && j <= n - 1 ; i--, j++) { if (chessboard[i][j] == 'Q' ) { return false ; } } return true ; } }
贪心算法
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
动态规划
动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心 ,贪心没有状态推导,而是从局部直接选最优的,
对于动态规划问题五步曲:
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组
01背包暴力思路:每个物品只有取或者不取,用回溯算法枚举所有情况,筛选出装满背包的最大价值是多少。
01背包动态规划解法:
dp[i][j]
表示从下标为[0-i]
的物品里任意取,放进容量为j的背包,价值总和最大是多少。
递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])