算法 & 数据结构

切题四件套

Clarification、Possible solutions、Coding、Test cases

常用数据结构

数组、链表、队列、栈、堆、哈希表、树、二叉查找树、字母树、LRU缓存

常用算法

递归、贪心、分治、广度优先搜索、深度优先搜索、二叉树遍历、动态规划、二分查找、图

时间/空间复杂度

O(1)、O(logn)、O(n)、O(nlogn)、O(n2)、O(n3)、O(2n)、O(n!)

常见算法复杂度

二分查找:O(logn)、二叉树遍历:O(n)、归并排序:O(nlogn)

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

MapputgetremovecontainsKeykeySetentrySetclearisEmptysize
CollectionaddaddAllremoveremoveAllcontainscontainsAllclearisEmptysize
Set
Listget
Stackpushpeekpop
Queueaddpeekpoll
Deque

addFrist

addLast

peekFirst

peekLast

pollFirst

pollLast

String:charAt、toCharArray、Arrays.sort、valueOf

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

LinkedList 实现了 List、Queue、Deque

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

class Solution {
    fun search(nums: IntArray, target: Int): Int {
        if (nums.size == 0) return -1
        var start = 0
        var end = nums.size - 1
        var middle = 0
        while (start <= end) {
            middle = (start + end) / 2
            when {
                nums[middle] == target -> {
                    return middle
                }
                nums[middle] > target -> {
                    end = middle - 1
                }
                else -> {
                    start = middle + 1
                }
            }
        }
        return -1
    }
}

反转链表:只要保证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)

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

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

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

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

四数之和:多了一层循环

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

排序算法总结:各种排序算法总结和比较 - 苍穹逸影 - 博客园

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

不稳定排序算法:选择排序(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;
}

二叉树转换成单链表:

求空间曼哈顿距离最小点

二叉树转换成双向链表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little-sparrow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值