算法与数据结构*

学习方法:chunk it up、deliberate practicing、feedback

切题四件套:Clarification、Possible solutions、Coding、Test cases

数组 链表 队列 哈希表 二叉查找树 字母树 LRU缓存
递归 贪心

分治

广度优先搜索 深度优先搜索 二叉树遍历 动态规划 二分查找

时间/空间复杂度:O(1)、O(logn)、O(n)、O(nlogn)、O(n2)、O(n3)、O(2n)、O(n!)

常见算法复杂度:Binary Search:O(logn)、Binary Tree Traversal:O(n)、Merge Sort:O(nlogn)

做题之前一定要想时间/空间复杂度

Map put get remove containsKey keySet entrySet clear isEmpty size
Collection add addAll remove removeAll contains containsAll clear isEmpty size
Set                  
List   get              
Stack push peek pop            
Queue add peek poll            
Deque addFrist addLast peekFirst peekLast pollFirst pollLast      

String:charAt、toCharArray、Arrays.sort、valueOf

涉及到获取操作一定要想到判空

LinkedList实现了List、Queue、Deque

链表求和:由于不知道第一个节点的值,需要冗余一个节点

反转链表:只要保证cur不为空

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode nex = null;
        while(cur != null) {
            nex = cur.next;
            cur.next = pre;
            pre = cur;
            cur = nex;
        }
        return pre;
    }
}

两两交换链表:需要记录四个节点

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode ret = head.next;
        ListNode pre = null;
        ListNode cur0 = head;
        ListNode cur1 = head.next;
        ListNode next = null;
        while(cur0 != null && cur1 != null) {
            next = cur1.next;
            if(pre != null) pre.next = cur1;
            cur1.next = cur0;
            cur0.next = next;
            pre = cur0;
            cur0 = next;
            cur1 = (cur0 != null) ? cur0.next : null;
        }
        return ret;
    }
}

环形链表:HashSet、快慢指针

public class Solution {
    public boolean hasCycle(ListNode head) {
        // if(head == null) return false;
        // ListNode cur = head;
        // HashSet<ListNode> hashSet = new HashSet<>();
        // while(cur != null) {
        //     if(hashSet.contains(cur)) return true;
        //     hashSet.add(cur);
        //     cur = cur.next;
        // }
        // return false;
        ListNode low = head, fast = head;
        while(fast != null) {
            low = low.next;
            fast = fast.next;
            if(fast == null) return false;
            fast = fast.next;
            if(fast == low) return true;
        }
        return false;
    }
}

K个一组翻转链表:

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if(k <= 1 || head == null) return head;
        ListNode res = head;
        ListNode cur = head;
        ListNode pre = null;
        Stack<ListNode> stack = new Stack<>();
        while(cur != null) {
            for(int i = 0; i < k; i++) {
                if(cur != null) {
                    stack.push(cur);
                    cur = cur.next;
                } else {
                    return res;
                }
            }
            if(stack.size() == k && res == head) {
                res = stack.peek();
            }
            for(int i = 0; i < k; i++) {
                if(pre != null) {
                    pre.next = stack.peek();
                }
                pre = stack.pop();
            }
            if(pre != null) {
                pre.next = cur;
            }
        }
        return res;
    }
}

统一规律:在循环内部赋值,尽量减少循环判断的参数信息

有效的括号:需要后面的括号作为Key、需要实时判断栈是否为空

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put(')', '(');
        map.put('}', '{');
        map.put(']', '[');
        for (int i = 0; i < s.length(); i++) {
            if(!map.containsKey(s.charAt(i))) {
                stack.push(s.charAt(i));
            } else {
                //忘记判断栈是否为空了
                if(stack.isEmpty()) return false;
                if(!stack.pop().equals(map.get(s.charAt(i)))) return false;
            }
        }
        return stack.isEmpty();
    }
}

两个队列实现栈、两个栈实现队列

优先队列(PriorityQueue):Heap、Binary Search Tree

小顶堆:父亲比左右孩子都要小

二叉堆 find-min:O(1) delete-min:O(logn) insert:O(logn) merge:O(n)
Fibonacci堆 find-min:O(1) delete-min:O(logn) insert:O(1) merge:O(1)

返回数据流中第K大的元素:小顶堆O(Nlogk)、排序O(Nklogk)

```python class KthLargest {
private Queue<Integer> queue = null;
private int size = 0;

public KthLargest(int k, int[] nums) {
    queue = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
        @Override
        public int compare(Integer i, Integer j) {
            return i.compareTo(j);
        }
    });
    size = k;
    for(int i = 0; i < nums.length; i++) {
        add(nums[i]);
    }
}

public int add(int val) {
    if(queue.size() < size) {
        queue.add(val);
    } else {
        if(!queue.isEmpty() && val > queue.peek()) {
            queue.poll();
            queue.add(val);
        }
    }
    return queue.peek();
}

}


<p>PriorityQueue默认是小顶堆,我们需要手动实现Comparator来实现大顶堆</p>

```python
priorityQueue = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
    @Override
    public int compare(Integer i1, Integer i2) {
        return i2.compareTo(i1);
    }
});

滑动窗口最大值:Deque(双端队列)

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length <= 0 || k <= 0 || nums.length < k) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int res[] = new int[nums.length - k + 1];
        for (int i = 0; i < nums.length; i++) {
            if (!deque.isEmpty()) {
                if (i >= k && deque.peekFirst() <= i - k) deque.pollFirst();
                while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) deque.pollLast();
            }
            deque.addLast(i);
            if (i >= k - 1) res[i - k + 1] = nums[deque.peekFirst()];
        }
        return res;
    }
}

有效的字母异或位:排序O(nlogn)、字母计数O(n)

class Solution {
    public boolean isAnagram(String s, String t) {
        if(s == null && t == null) return true;
        if(s == null || t == null) return false;
        char[] ss = s.toCharArray();
        char[] ts = t.toCharArray();
        Arrays.sort(ss);
        Arrays.sort(ts);
        return String.valueOf(ss).equals(String.valueOf(ts));
    }
}

Integer和Integer之间不能用等号进行判断,Integer和int之间可以

二数之和:暴力循环、HashMap

三数之和:排序之后可以很好的排除相同元素

    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums); 
        List<List<Integer>> res = new LinkedList<>();
        for (int i = 0; i < nums.length-2; ++i) {
            if (i == 0 || (i > 0 && nums[i] != nums[i-1])) {
                int lo = i+1, hi = nums.length-1, sum = 0 - nums[i];
                while (lo < hi) {
                    if (nums[lo] + nums[hi] == sum) {
                        res.add(Arrays.asList(nums[i], nums[lo], nums[hi]));
                        while (lo < hi && nums[lo] == nums[lo+1]) lo++; 
                        while (lo < hi && nums[hi] == nums[hi-1]) hi--; 
                        lo++; hi--; 
                    }else if (nums[lo] + nums[hi] < sum) lo++;
                    else hi--;
                }
            }
        }
        return res;
    }

四数之和:多了一层循环

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        if(nums.length < 3) return new LinkedList<>();
        List<List<Integer>> result = new LinkedList<>();
        Arrays.sort(nums);
        for(int i = 0; i < nums.length - 2; i++) {
            if(i != 0 && nums[i-1] == nums[i]) continue;
            int j = i + 1, k = nums.length - 1;
            while(j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if(sum == 0) result.add(Arrays.asList(nums[i], nums[j], nums[k]));
                if(sum <= 0) do j++; while(j < k && nums[j] == nums[j - 1]);
                if(sum >= 0) do k--; while(j < k && nums[k] == nums[k + 1]);
            }
        }
        return result;
    }
}

二叉搜索树:指一棵空树或者具有下列性质的二叉树(左子树上所有节点小于根节点、右子树上所有节点大于根节点、左右子树也分别为二叉搜索树)

平衡二叉搜索树:Red-Black Tree,最坏情况也是O(logN)

验证二叉搜索树:中序遍历、双层递归

二叉搜索树的最近公共祖先:根据值来判断

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || (p == null && q == null))  {
            return root;
        } else if(p == null) {
            return q;
        } else if(q == null) {
            return p;
        } else if (root.val > p.val && root.val > q.val) {
            return lowestCommonAncestor(root.left, p, q);
        } else if (root.val < p.val && root.val < q.val) {
            return lowestCommonAncestor(root.right, p, q);
        } else {
            return root;
        }
    }
}

二叉树的最近公共祖先:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || p == null || q == null) return null;
        if(root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        return (left != null && right != null) ? root : left != null ? left : right;
    }
}

遍历二叉树(前序、中序、后序)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> preOrderTraversal(TreeNode root) {
        //traversal
        List<Integer> list = new LinkedList<Integer>();
        if(root == null) return list;
        list.add(root.val);
        list.addAll(preorderTraversal(root.left));
        list.addAll(preorderTraversal(root.right));
        return list;
        
        //non-traversal
        List<Integer> list = new LinkedList<Integer>();
        if (root == null) return list;
        Stack<TreeNode> stack = new Stack<>();
        stack.add(root);
        while (!stack.isEmpty()) {
            TreeNode cur = stack.pop();
            list.add(cur.val);
            //这里不要糊涂,应该是右孩子先进栈
            if (cur.right != null) stack.push(cur.right);
            if (cur.left != null) stack.push(cur.left);
        }
        return list;
    }
    public List<Integer> inOrderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> list = new LinkedList<>();
        if(root == null) return list;
        TreeNode cur = root;
        //如果cur不为空,一直往左压栈,访问栈顶,右孩子入栈
        while(!stack.isEmpty() || cur != null) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            list.add(cur.val);
            cur = cur.right;
        }
        return list;
    }
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode lastVisit = null;
        while(!stack.isEmpty() || cur != null) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.peek();
            if (cur.right == null || cur.right == lastVisit) {
                list.add(cur.val);
                lastVisit = stack.pop();
                cur = null;
            } else {
                cur = cur.right;
            }
        }
        return list;
    }
}

递归分治:Pow(x, n),需要注意的是负数转正数需要考虑边界值(Integer.MIN_VALUE)

class Solution {
    public double myPow(double x, int n) {
        double pow = 1;
        if(n < 0) {
            if(n == Integer.MIN_VALUE){ 
                x = 1 / x;
                n = -(n + 1);
                pow = x;
            } else {
                x = 1 / x;
                n = -n;
            }
        }
        while(n != 0) {
            if((n & 1) == 1) {
                pow = pow * x;
            }
            x *= x;
            n >>= 1;
        }
        return pow;
        
        // if(n < 0) return (n != Integer.MIN_VALUE) ? 1 / myPow(x, -n) : 1 / (x * myPow(x, -(n+1)));
        // if(n == 0) return 1;
        // double tmp = myPow(x, n/2);
        // return (n % 2 == 0) ? tmp * tmp : x * tmp * tmp;
    }
}

求众数:排序、HashMap

class Solution {
    public int majorityElement(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++) {
            if(map.containsKey(nums[i])) {
                map.put(nums[i], map.get(nums[i]) + 1);
            } else {
                map.put(nums[i], 1);
            }
        }
        int max = 0;
        int res = 0;
        for(Integer i : map.keySet()) {
            if(map.get(i) > max) {
                max = map.get(i);
                res = i;
            }
        }
        return res;
    }
}

二叉树的层次遍历:BFS(广度优先搜索):队列实现、DFS(深度优先搜索):需要传递层次信息

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new LinkedList<>();
        levelOrder(root, 0, list);
        return list;
    }
    private void levelOrder(TreeNode root, int level, List<List<Integer>> list) {
        if(root == null) return;
        if(level > list.size() - 1) list.add(new LinkedList<Integer>());
        list.get(level).add(root.val);
        levelOrder(root.left, level + 1, list);
        levelOrder(root.right, level + 1, list);
    }
}
 
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list = new LinkedList<>();
        if(root == null) return list;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> list_item = new LinkedList<>();
            for(int i = 0; i < size; i++) {
                TreeNode tmp = queue.poll();
                list_item.add(tmp.val);
                if(tmp.left != null) queue.add(tmp.left);
                if(tmp.right != null) queue.add(tmp.right);
            }
            list.add(list_item);
        }
        return list;
    }
}

二叉树的最大/最小深度:广度优先搜索、深度优先搜索(递归,可以看成中序遍历)

括号生成:(深度优先 + 剪枝算法)

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> list = new LinkedList<>();
        generate(list, 0, 0, n, "");
        return list;
    }
    //要点在于参数是值传递,可以回溯
    private void generate(List<String> list, int left, int right, int n, String result) {
        if (left == n && right == n) list.add(result);
        if (left < n) generate(list, left + 1, right, n, result + "(");
        if (right < n && right < left) generate(list, left, right + 1, n, result + ")");
    }
}

剪枝:递归前面加上判断条件

N皇后问题(DFS+剪枝):

class Solution {
    private Set<Integer> cols = new HashSet<>();
    private Set<Integer> pie = new HashSet<>();
    private Set<Integer> na = new HashSet<>();
    private List<List<String>> results = new LinkedList<>();
    
    public List<List<String>> solveNQueens(int n) {
        DFS(0, n, new LinkedList<String>());
        return results;
    }
    
    private void DFS(int row, int n, List<String> res) {
        if(row >= n) {
            List<String> result = new LinkedList<>();
            result.addAll(res);
            results.add(result);
            return;
        }
        for(int col = 0; col < n; col++) {
            if(cols.contains(col) || pie.contains(row + col) || na.contains(row - col)) continue;
            StringBuilder sb = new StringBuilder();
            for(int s = 0; s < n; s++) {
                if(s != col) sb.append("."); else sb.append("Q");
            }
            res.add(sb.toString());
            cols.add(col);
            pie.add(row + col);
            na.add(row - col);
            
            DFS(row + 1, n, res);
            
            res.remove(sb.toString());
            cols.remove(col);
            pie.remove(row + col);
            na.remove(row - col);
        }
    }
}

有效的数独(利用9个数的特性):

class Solution {
    public boolean isValidSudoku(char[][] board) {
        boolean[][] row = new boolean[9][10];
        boolean[][] col = new boolean[9][10];
        boolean[][] block = new boolean[9][10];
        for(int i = 0; i < 9; i++) {
            for(int j = 0; j < 9; j++) {
                if(board[i][j] != '.') {
                    int num = board[i][j] - '0';
                    if(row[i][num] || col[j][num] || block[i / 3 * 3 + j / 3][num]) {
                        return false;
                    } else {
                        row[i][num] = true;
                        col[j][num] = true;
                        block[i / 3 * 3 + j / 3][num] = true;
                    }
                }
            }
        }
        return true;
    }
}

解数独(DFS+剪枝):

class Solution {
    public void solveSudoku(char[][] board) {
        DFS(board, 0, 0);
    }
    
    private boolean DFS(char[][] board, int i, int j) {
        if(i == (9 - 1) && j == (9 - 1) && board[i][j] != '.') return true;
        while(true) {
            if(board[i][j] == '.') {
                break;
            } else {
                if(j < 9 - 1) {
                    j++;
                } else {
                    if(i < 9 - 1) {
                        i++;
                        j = 0;
                    } else {
                        return true;
                    }
                }
            }
        }
        for(char num = '1'; num <= '9'; num++) {
            if(isValid(board, i, j, num)) {
                board[i][j] = num;
                if(DFS(board, i, j)) {
                    return true;
                } else {
                    board[i][j] = '.';
                }
            }
        }
        return false;
    }
    
    private boolean isValid(char[][] board, int row, int col, char num) {
        for(int i = 0; i < 9; i++) {
            if(board[row][i] != '.' && board[row][i] == num) return false;
            if(board[i][col] != '.' && board[i][col] == num) return false;
            if(board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] != '.' &&
              board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == num) return false;
        }
        return true;
    }
}

二分查找:(单调递增或递减、存在上下界、能够通过索引访问)

X的平方根(多层的二分查找):

class Solution {
    public int mySqrt(int x) {
        double res = mySqrt(x, 1);
        return (int)res;
    }
    private double mySqrt(int x, int precise) {
        double left = 0;
        double right = x;
        double incre = 1;
        for(int i = 0; i < precise; i++) {
            while(left < right) {
                double mid = (right + left) / 2;
                mid = (double)((int)(mid * (1 / incre))) / (1 / incre);
                double tmp = mid * mid;
                if(tmp > x) right = mid - incre;
                if(tmp < x) left = left + incre;
                if(tmp == x) return mid;
            }
            left = left * left > x ? left - incre : left;
            right = left + incre;
            incre /= 10;
        }
        return left;
    }
}

Trie树(字典树):又称单次查找树或键树

class Trie {
    class TrieNode {
        public boolean isWord = false;
        public TrieNode[] children = new TrieNode[26];
        public TrieNode() {
            
        }
    }
    TrieNode root = null;
    public Trie() {
        root = new TrieNode();
    }
    public void insert(String word) {
 
    }
    public void search(String word) {
 
    }
    public boolean search(String word) {
 
    }
}

单词搜索:(DFS+位置记录+单词前缀剪枝)

位运算 & | ^ ~ >> << >>>

X = X & (X - 1):清零最低位的1

判断一个数是否是2的幂:

class Solution {
    public boolean isPowerOfTwo(int n) {
        return n <= 0 ? false : (n & (n - 1)) == 0;
    }
}

判断数组中各个数1的位数:

class Solution {
    public int[] countBits(int num) {
        int[] res = new int[num + 1];
        for(int i = 1; i < num + 1; i++) {
            res[i] = res[i & (i - 1)] + 1;
        }
        return res;
    }
}

螺旋矩阵:

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> list = new LinkedList<>();
        int i = 0, j = 0, m = matrix.length, n = 0;
        if(m > 0) n = matrix[0].length;
        spiralOrder(matrix, 0, 0, m - 1, n - 1, list);
        return list;
    }
    
    private void spiralOrder(int[][] matrix, int i, int j, int m, int n, List<Integer> list) {
        if(i > m || j > n) {
            return;
        } else if(i == m && j == n) {
            list.add(matrix[i][j]);
        } else if(i == m) {
            for(int s = j; s <= n; s++) {
                list.add(matrix[i][s]);
            }
        } else if(j == n) {
            for(int s = i; s <= m; s++) {
                list.add(matrix[s][j]);
            }
        } else {
            for(int s = j; s < n; s++) {
                list.add(matrix[i][s]);
            }
            for(int s = i; s < m; s++) {
                list.add(matrix[s][n]);
            }
            for(int s = n; s > j; s--) {
                list.add(matrix[m][s]);
            }
            for(int s = m; s > i; s--) {
                list.add(matrix[s][j]);
            }
        }
        spiralOrder(matrix, i+1, j+1, m-1, n-1, list);
    }
}

动态规划(Dynamic Programming)

  1. 递归(自上向下)+ 记忆化 -> 递推(自下而上)
  2. 状态的定义
  3. 状态转移方程
  4. 最优子结构

爬楼梯:最简单的动态规划(状态定义简单、DP方程简单)

三角形的最小路径和:(状态定义简单、DP方程简单)

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int height = triangle.size();
        int[] memory = new int[height + 1];
        for(int i = height - 1; i >= 0; i--) {
            for(int j = 0; j <= i; j++) {
                memory[j] = Math.min(memory[j], memory[j + 1]) + triangle.get(i).get(j);
            }
        }
        return memory[0];
    }
}

乘积最大子序列:(状态定义困难、DP方程简单、存在截断问题,需要循环搜索最优解)

class Solution {
    public int maxProduct(int[] nums) {
        int max = Integer.MIN_VALUE, imax = 1, imin = 1; //一个保存最大的,一个保存最小的。
        for(int i=0; i<nums.length; i++){
            if(nums[i] < 0){ int tmp = imax; imax = imin; imin = tmp;}
            //和nums[i]比较就是截断的过程
            imax = Math.max(imax*nums[i], nums[i]);
            imin = Math.min(imin*nums[i], nums[i]);
            max = Math.max(max, imax);
        }
        return max;
    }
}

最长上升子序列:复杂度O(n2)

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length <= 0) return 0;
        int[] lengths = new int[nums.length];
        lengths[0] = 1;
        for(int i = 1; i < nums.length; i++) {
            int max = 0;
            for(int j = 0; j < i; j++) {
                max = nums[j] < nums[i] ? (lengths[j] > max ? lengths[j] : max) : max;
            }
            lengths[i] = max + 1;
        }
        int length = 0;
        for(int i = 0; i < lengths.length; i++) {
            length = lengths[i] > length ? lengths[i] : length;
        }
        return length;
    }
}

零钱兑换(变相的爬楼梯):

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] nums = new int[amount + 1];
        for(int i = 0; i <= amount; i++) nums[i] = Integer.MAX_VALUE;
        nums[0] = 0;
        for(int i = 1; i <= amount; i++) {
            for(int j = 0; j < coins.length; j++) {
                if(i - coins[j] < 0 || nums[i - coins[j]] == Integer.MAX_VALUE) continue;
                if(nums[i - coins[j]] + 1 < nums[i]) nums[i] = nums[i - coins[j]] + 1;
            }
        }
        return nums[amount] == Integer.MAX_VALUE ? -1 : nums[amount];
    }
}

股票买卖问题(只能买卖一次):

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length < 2) return 0;
        int min = prices[0];
        int profit = 0;
        for(int i = 1; i < prices.length; i++) {
            if(prices[i] - min > profit) profit = prices[i] - min;
            if(prices[i] < min) min = prices[i];
        }
        return profit;
    }
}

股票买卖问题(无数次):代码略

股票买卖问题(带冷却时间):

股票买卖问题(带手续费):

股票买卖问题(最多K笔交易):

for (int i = 0; i < n; i++) { //天数
    for (int k = 0; k < K; k++) { //交易次数
        dp[i][0][k] = MAX{ dp[i - 1][0][k], dp[i - 1][1][k - 1] + a[i] };
        dp[i][1][k] = MAX{ dp[i - 1][1][k], dp[i - 1][0][k] - a[i] };
    }
}
 
//最终结果在 dp[n - 1][0...k][0] 中

编辑距离(状态定义困难、DP转移方程一般):DP[i][j](word1前i个字符替换word2前j个字符的最少步数)

class Solution {
    public int minDistance(String word1, String word2) {
        if(word1 == null && word2 == null) return 0;
        if(word1 == null || word2 == null) return Integer.MAX_VALUE;
        int length1 = word1.length();
        int length2 = word2.length();
        int[][] matrix = new int[length1 + 1][length2 + 1];
        for(int i = 0; i <= length1; i++) matrix[i][0] = i;
        for(int i = 0; i <= length2; i++) matrix[0][i] = i;
        for(int i = 1; i <= length1; i++) {
            for(int j = 1; j <= length2; j++) {
                if(word1.charAt(i - 1) == word2.charAt(j - 1)) {
                    matrix[i][j] = matrix[i - 1][j - 1];
                } else {
                    matrix[i][j] = Math.min(Math.min(matrix[i - 1][j], matrix[i][j - 1]), matrix[i - 1][j - 1]) + 1;
                }
            }
        }
        return matrix[length1][length2];
    }
}

LRU Cache(Least Recently Used):最近使用的替掉最少使用的,通过LinkedHashMap实现(能保证插入顺序,get访问效率也高),重写removeEldestEntry方法来实现自动删除最老的元素

LFU(Least Frequence Used):最近使用的替掉频率最低的

KMP算法(strStr)

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle == null || needle.equals("")) return 0;
        if(haystack == null) return -1;
        if(needle.length() > haystack.length()) return -1;
        int[] next = getNext(needle);
        int i = 0;
        int j = 0;
        char[] chars1 = haystack.toCharArray();
        char[] chars2 = needle.toCharArray();
        while(i < chars1.length && j < chars2.length) {
            if(j == -1 || chars1[i] == chars2[j]) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if(j == chars2.length) 
            return i - j;
        else
            return -1;
    }
    
    int[] getNext(String needle) {
        char[] chars = needle.toCharArray();
        int[] next = new int[chars.length];
        next[0] = -1;
        int m = -1;
        int n = 0;
        while(n < chars.length - 1) {
            if(m == -1 || chars[m] == chars[n]) {
                m++;
                n++;
                next[n] = m;
            } else {
                m = next[m];
            }
        }
        return next;
    }
}

区间合并(需要排序,选用的快排):

class Solution {
    public List<Interval> merge(List<Interval> intervals) {
        if(intervals == null || intervals.size() < 2) return intervals;
        int length = intervals.size();
        int[][] res = new int[length][2];
        for(int i = 0; i < length; i++) {
            res[i][0] = intervals.get(i).start;
            res[i][1] = intervals.get(i).end;
        }
        sort(res, 0, res.length - 1);
        List<Interval> list = new LinkedList<>();
        int[][] tmp = new int[length][2];
        tmp[0] = res[0];
        int tmp_length = 1;
        for(int i = 1; i < length; i++) {
            if(res[i][1] <= tmp[tmp_length - 1][1]) {
                continue;
            } else if(res[i][0] <= tmp[tmp_length - 1][1] && res[i][1] >= tmp[tmp_length - 1][1]) {
                tmp[tmp_length - 1][1] = res[i][1];
            } else {
                tmp_length++;
                tmp[tmp_length - 1][0] = res[i][0];
                tmp[tmp_length - 1][1] = res[i][1];
            }
        }
        for(int i = 0; i < tmp_length; i++) {
            list.add(new Interval(tmp[i][0], tmp[i][1]));
        }
        return list;
    }
    
    private void sort(int[][] intervals, int i, int j) {
        if(i < j) {
            int base0 = intervals[i][0];
            int base1 = intervals[i][1];
            while(i < j) {
                while(i < j && intervals[j][0] >= base0) j--;
                intervals[i][0] = intervals[j][0];
                intervals[i][1] = intervals[j][1];
                while(i < j && intervals[i][0] <= base0) i++;
                intervals[j][0] = intervals[i][0];
                intervals[j][1] = intervals[i][1];
            }
            intervals[i][0] = base0;
            intervals[i][1] = base1;
            sort(intervals, 0, i - 1);
            sort(intervals, i + 1, j);
        }
    }
}

求K个无序数组的中位数:小顶堆、快速排序

求字符串的Huffman编码:频率排序,生成父节点之后重新计算频率,可以通过小顶堆来实现

求两个有序数组所有数中第K大的数、求两个有序数组所有数的中位数:二分查找,对应第二个数组截成两段,比较临界值

数组的最大区间和:动态规划,类似于最大乘积子序列

单向链表最后第K个节点:双指针实现,不需要回溯

判断二叉树是否是完全二叉树(不是满二叉树):层次遍历 + 逐层判断

判断二叉树是否是满二叉树:2的N次方 - 1

求两个链表的交叉节点:计算两个链表的长度差,然后长的链表先后移、通过HashSet来判断

对数组中的元素进行重新排列,负数放到前面,不改变相对顺序:一遍快排

求数组中逆序对的个数:归并排序 //TODO

统计字符串中出现最多的字符和次数:HashMap、String.toCharArray

排序算法总结:https://www.cnblogs.com/zhaoshuai1215/p/3448154.html

稳定排序算法:插入排序、冒泡排序、归并排序

不稳定排序算法:选择排序(5 8 5 2 9)、希尔排序、快速排序、堆排序

斜45度打印数组:数字游戏,需要补全

有序数组中任选两个数之和等于目标值,给出所有组合:HashSet

和至少为K的最短子数组:双层(起点循环)+ 动态规划

对称树判断:

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);
}
 
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;
}

二叉树转换成单链表:

求空间曼哈顿距离最小点

二叉树转换成双向链表

猜你喜欢

转载自blog.csdn.net/ljyljyok/article/details/106743789