记录一些值得多刷的题
搜索旋转排序数组 山谷数组的搜索 在严格从小到大排序的数组中,索引0-i被翻转,导致最小元素位于数组中间
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 class Solution { public int search (int [] nums, int target) { int pivot = findMin(nums); return binarySearch(nums,0 ,pivot-1 ,target)==-1 ?binarySearch(nums,pivot,nums.length-1 ,target):binarySearch(nums,0 ,pivot-1 ,target); } int binarySearch (int [] nums,int start, int end,int target) { int left=start-1 ; int right=end+1 ; while (left+1 != right){ int mid = left+(right-left)/2 ; if (nums[mid]<=target) left=mid; else right=mid; } if (left==start-1 || nums[left]!=target) return -1 ; return left; } int findMin (int [] nums) { int low = 0 ; int high = nums.length-1 ; while (low<high){ int mid = low+(high-low)/2 ; if (nums[mid]>nums[high]) low=mid+1 ; else high=mid; } return low; } }
山峰数组的搜索 顾名思义,就是最大元素在数组中间的数组
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution { public int peakIndexInMountainArray (int [] arr) { int left=-1 ; int right=arr.length; while (left+1 != right){ int mid = left+(right-left)/2 ; if (arr[mid]>arr[mid+1 ]) right = mid; else left=mid; } return right; } }
并查集算法 并查集可以用来干啥:检测于避免环的生成,可用于辅助克鲁斯卡尔算法的实现
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 class UF { private int count; private int [] parent; public UF (int n) { this .count = n; parent = new int [n]; for (int i = 0 ; i < n; i++) { parent[i] = i; } } public void union (int p, int q) { int rootP = find(p); int rootQ = find(q); if (rootP == rootQ) return ; parent[rootQ] = rootP; count--; } public boolean connected (int p, int q) { int rootP = find(p); int rootQ = find(q); return rootP == rootQ; } public int find (int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } public int 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 class MonotonicQueue { LinkedList<Integer> maxq = new LinkedList <>(); public void push (int n) { while (!maxq.isEmpty() && maxq.getLast() < n) { maxq.pollLast(); } maxq.addLast(n); } public int max () { return maxq.getFirst(); } public void pop (int n) { if (n == maxq.getFirst()) { maxq.pollFirst(); } } }
nSum
系列问题2Sum 给定一个数组和一个目标值,要求输出若干个二元组,这些二元组的和都等于target
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 List<List<Integer>> twoSumTarget (int [] nums, int target) { Arrays.sort(nums); int low = 0 ; int high= nums.length-1 ; List<List<Integer>> res = new ArrayList (); while (low<high){ int sum = nums[low] + nums[high]; int left = nums[low] ; int right = nums[high]; if (sum < target) while (low<high && nums[low]==left) low++; else if (sum > target) while (low<high && nums[high]==right) high--; else { List<Integer> ans = Arrays.asList(left,right); res.add(ans); while (low < high && nums[low] == left) low++; while (low < high && nums[high] == right]) high--; } } return res; }
3Sum 其实可以化简为2Sum问题,即对每一个数进行遍历,并解决这一个数的去重问题,其他两个数的去重问题交给另一个方法来解决,4Sum同理
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 { public List<List<Integer>> threeSum (int [] nums, int target) { List<List<Integer>> res = new ArrayList (); Arrays.sort(nums); int n = nums.length; if (n<3 || nums==null ) return res; for (int i = 0 ;i<n;i++){ List<List<Integer>> twoSums = twoSumTarget(nums,i+1 ,target-nums[i]); for (List<Integer> Sums : twoSums){ List<Integer> temp = new ArrayList <>(Sums); temp.add(nums[i]); res.add(temp); } while (i<n-1 &&nums[i]==nums[i+1 ]) i++; } return res; } List<List<Integer>> twoSumTarget (int [] nums,int start, int target) { Arrays.sort(nums); int low = start; int high= nums.length-1 ; List<List<Integer>> res = new ArrayList (); while (low<high){ int sum = nums[low] + nums[high]; int left = nums[low] ; int right = nums[high]; if (sum < target) while (low<high && nums[low]==left) low++; else if (sum > target) while (low<high && nums[high]==right) high--; else { List<Integer> ans = Arrays.asList(left,right); res.add(ans); while (low < high && nums[low] == left) low++; while (low < high && nums[high] == right) high--; } } return res; } }
nSum 统一解法:递归
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 class Solution { public List<List<Integer>> fourSum (int [] nums, int target) { Arrays.sort(nums); return nSumTarget(nums,4 ,0 ,target); } private List<List<Integer>> nSumTarget (int [] nums, int n, int start,int target) { List<List<Integer>> res = new ArrayList (); if (target==-294967296 ||target==294967296 ) return res; int len = nums.length; if (n<2 || len<n) return res; if (n==2 ){ int low = start; int high= len-1 ; while (low<high){ int sum = nums[low] + nums[high]; int left = nums[low];int right = nums[high]; if (sum < target) while (low<high && nums[low]==left) low++; else if (sum > target) while (low<high && nums[high]==right) high--; else { res.add(new ArrayList <Integer>(Arrays.asList(left, right))); while (low < high && nums[low] == left) low++; while (low < high && nums[high] == right) high--; } } } else { for (int i = start;i<len;i++){ List<List<Integer>> sub = nSumTarget(nums,n-1 ,i+1 ,target-nums[i]); for (List<Integer> Sums : sub){ Sums.add(nums[i]); res.add(Sums);} while (i<len-1 &&nums[i]==nums[i+1 ]) i++; } } return res; } }
反转链表系列问题 链表是一个兼具迭代与递归性质的数据结构
整个链表反转 反转整个链表其实就是反转由head到null之间的节点
迭代实现:由三个指针向前推进实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ListNode reverse (ListNode a) { ListNode pre, cur, nxt; pre = null ; cur = a; nxt = a; while (cur != null ) { nxt = cur.next; cur.next = pre; pre = cur; cur = nxt; } return pre; }
反转特定范围内的节点 1 2 3 4 5 6 7 8 9 10 11 12 13 ListNode reverse (ListNode a, ListNode b) { ListNode pre, cur, nxt; pre = null ; cur = a; nxt = a; while (cur != b) { nxt = cur.next; cur.next = pre; pre = cur; cur = nxt; } return pre; }
以k个一组反转链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ListNode reverseKGroup (ListNode head, int k) { if (head == null ) return null ; ListNode a, b; a = b = head; for (int i = 0 ; i < k; i++) { if (b == null ) return head; b = b.next; } ListNode newHead = reverse(a, b); a.next = reverseKGroup(b, k); return newHead; }
排序 归并排序,二叉树的后序遍历 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 class Merge { private static int [] temp; public static void sort (int [] nums) { temp = new int [nums.length]; sort(nums, 0 , nums.length - 1 ); } private static void sort (int [] nums, int lo, int hi) { if (lo == hi) { return ; } int mid = lo + (hi - lo) / 2 ; sort(nums, lo, mid); sort(nums, mid + 1 , hi); merge(nums, lo, mid, hi); } private static void merge (int [] nums, int lo, int mid, int hi) { for (int i = lo; i <= hi; i++) { temp[i] = nums[i]; } int i = lo, j = mid + 1 ; for (int p = lo; p <= hi; p++) { if (i == mid + 1 ) { nums[p] = temp[j++]; } else if (j == hi + 1 ) { nums[p] = temp[i++]; } else if (temp[i] > temp[j]) { nums[p] = temp[j++]; } else { nums[p] = temp[i++]; } } } }
归并排序应用:链表排序 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 class Solution { public ListNode sortList (ListNode head) { if (head==null ) return null ; return divide(head); } ListNode divide (ListNode head) { if (head.next==null ) return head; ListNode slow=head,fast=head,pre=null ; while (fast!=null &&fast.next!=null ){ pre=slow; slow=slow.next; fast=fast.next.next; } pre.next = null ; ListNode segA = divide(head); ListNode segB = divide(slow); return merge(segA,segB); } ListNode merge (ListNode segA, ListNode segB) { ListNode dummy = new ListNode (-1 ); ListNode cur = dummy; while (segA!=null && segB!=null ){ if (segA.val>=segB.val){ cur.next = segB; cur= cur.next; segB=segB.next; } else if (segB.val>segA.val){ cur.next = segA; cur=cur.next; segA=segA.next; } } if (segA != null ) cur.next = segA; if (segB != null ) cur.next = segB; return dummy.next; } }
快速排序,二叉树的前序遍历 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 class Quick { public static void sort (int [] nums) { shuffle(nums); sort(nums, 0 , nums.length - 1 ); } private static void sort (int [] nums, int lo, int hi) { if (lo >= hi) { return ; } int p = partition(nums, lo, hi); sort(nums, lo, p - 1 ); sort(nums, p + 1 , hi); } private static int partition (int [] nums, int lo, int hi) { int pivot = nums[lo]; int i = lo + 1 , j = hi; while (i <= j) { while (i < hi && nums[i] <= pivot) { i++; } while (j > lo && nums[j] > pivot) { j--; } if (i >= j) { break ; } swap(nums, i, j); } swap(nums, lo, j); return j; } private static void shuffle (int [] nums) { Random rand = new Random (); int n = nums.length; for (int i = 0 ; i < n; i++) { int r = i + rand.nextInt(n - i); swap(nums, i, r); } } private static void swap (int [] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } }
BFS框架 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 int BFS (Node start, Node target) { Queue<Node> q; Set<Node> visited; q.add(start); visited.add(start); int step = 0 ; while (q not empty) { int sz = q.size(); for (int i = 0 ; i < sz; i++) { Node cur = q.poll(); if (cur is target) return step; for (Node x : cur.adj()) { if (x not in visited) { q.add(x); visited.add(x); } } } step++; }
BFS应用:二叉树的锯齿形遍历
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 class Solution { public List<List<Integer>> zigzagLevelOrder (TreeNode root) { List<List<Integer>> res = new LinkedList <>(); if (root ==null ) return res; Queue<TreeNode> q = new LinkedList <>(); q.add(root); boolean flag = true ; while (!q.isEmpty()){ int n = q.size(); LinkedList<Integer> level = new LinkedList <>(); for (int i=0 ;i<n;i++){ TreeNode node = q.poll(); if (flag) level.addLast(node.val); else level.addFirst(node.val); if (node.left!=null ) q.add(node.left); if (node.right!=null ) q.add(node.right); } flag = !flag; res.add(level); } return res; } }