数据结构与算法

  • Tree
  • Binary Tree
  • Binary Search Tree 搜索二叉树
  • Serialize and Deserialize Binary Tree 二叉树的序列化和反序列化
  • Tree Traversal 树的遍历
  • 前序 PreOrder Traversal
  • 中序 InOrder Traversal
  • 后序 PostOrder Traversal
  • Red Black Tree 红黑树
  • Binary Tree Right Side View 二叉树的右视角
  • Lowest Common Ancestor of a Binary Tree 二叉树最近公共祖先
  • Balanced Binary Tree 平衡二叉树
  • Heap 堆
  • Top K Largest 第K大元素
  • Find Median from Data Stream 从数据流中找出中位数
  • Hash 散列
  • Deep Dive HashMap 深度解析HashMap
  • Subarray Sum Equals K 子数组的和等于K
  • Clone Graph 图的克隆
  • longest substring without repeat 最长无重复字符串
  • BFS 宽度优先搜索
  • Number of island 小岛屿数量
  • Word Ladder单词梯
  • DFS 深度优先搜索
  • Permutations 排列
  • Subset 子集
  • n queens 皇后n
  • String Manipulation 字符串
  • Reverse Word in a String 逆转字符串
  • Integer to Roman 整数转罗马数字
  • Trie 有序树
  • What is Typeahead?提示词
  • Union Find 并查集
  • 小岛问题
  • Dynamic Programming 动态规划
  • Climbing Stairs 爬楼梯
  • Unique Path 独一的道路
  • Longest Common 最长子串
  • Word Break 单词拆分
  • Palindrome Partitioning 回文分割


Tree

树的特点:

每个节点只有1个父节点(除了根节点没有父节点)。

树是不能有环的。

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法


一般我们所用的树都是说二叉树和二叉搜索树。

Binary Tree

树的结构多种多样,不过我们用的最多的还是二叉树。二叉树包含左子节点和右子节点。并不要求每个节点都有2个子节点。

满二叉树: 所有的叶子节点都在最底层,这样的二叉树称为满二叉树。

完全二叉树: 叶子节点都在最底下的两层,最后一层的叶子节点都靠左排列。除了最后一层,其它层节点个数都达到最大。

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_02


树的存储,有链式存储法和数组存储,常用的还是链式存储。

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_03


A是根节点存储在下标为1的位置,2i为左节点,(2i+1)为右节点

B是左节点存储到下标2的位置,C是右节点存储在下标等于3的位置。

这样就可以通过下标计算把整棵树都串起来。

数组存储的缺点,如果是完全二叉树无疑是最节省内存空间的存储方式,但是如果是非完全二叉树将会浪费很多的内存空间。

Binary Search Tree 搜索二叉树

搜索二叉树也叫二叉查找树,是二叉树最常用的一种类型。

二叉查找树是为了方便快速查找一个数据而生的,不仅可以快速查找,也可以快速插入、删除一个数据。

二叉查找树要求每个节点左节点的值都小于节点的值,而右节点的值都大于节点的值

数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_04


二叉查找树又叫二叉有序树,因为它可以输出一个有序的数组,时间复杂度为O(n)。

Serialize and Deserialize Binary Tree 二叉树的序列化和反序列化

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root == null) {
            return "{}";
        }
        List<TreeNode> list = new ArrayList<>();
        list.add(root);
        // [1,2,3,null,null,4,5,null,null,null,null]
        for (int i = 0; i < list.size(); i++) {
            TreeNode node = list.get(i);
            if (node == null) {
                continue;
            }
            list.add(node.left);
            list.add(node.right);
        }
        // [1,2,3,null,null,4,5]
        while (list.get(list.size() - 1) == null) {
            list.remove(list.size() - 1);
        }
        StringBuilder sb = new StringBuilder("{");
        sb.append(list.get(0).val);
        // {1,2,3,#,#,4,5}
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i) == null) {
                sb.append(",#");
            } else {
                sb.append("," + list.get(i).val);
            }
        }
        sb.append("}");
        return sb.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (data == null || data.equals("{}")) {
            return null;
        }
        // String[] data 1 2 3 # # 4 5
        String[] datas = data.substring(1, data.length() - 1).split(",");
        boolean isLeft = true;
        List<TreeNode> queue = new ArrayList<>();
        TreeNode node = new TreeNode(Integer.parseInt(datas[0]));
        queue.add(node);
        int index = 0;
        for (int i = 1; i < datas.length; i++) {
            if (!datas[i].equals("#")) {
                TreeNode node1 = new TreeNode(Integer.parseInt(datas[i]));
                if (isLeft) {
                    queue.get(index).left = node1;
                } else {
                    queue.get(index).right = node1;
                }
                queue.add(node1);
            }
            
            if (!isLeft) {
                index++;
            }
            isLeft = !isLeft;
        }
        return queue.get(0);
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

Tree Traversal 树的遍历

经典的遍历方式分为前序、中序、后序遍历。

前序:对于树中任意节点,先打印自己,再打左节点,后打右节点。

中序:对于树中任意节点,先打印左节点,再打印自己,后打印右节点

后序:对于树中任意节点,先打印左节点,再打印右节点,后打印自己

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_05


对于中序遍历(in-order)二叉查找树,得到的结果是一个有序的数组。

前序 PreOrder Traversal

递归方式:

public class Solution {
    /**
     * @param root: A Tree
     * @return: Preorder in ArrayList which contains node values.
     */
    public List<Integer> preorderTraversal(TreeNode root) {
        // write your code here
        List<Integer> result = new ArrayList<>();
        if(root == null) {
            return result;
        }
       
        List<Integer> left = preorderTraversal(root.left);
        List<Integer> right = preorderTraversal(root.right);
       
        result.add(root.val);
        result.addAll(left);
        result.addAll(right);
        return result;
    }
}

栈的方式:

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_06

public class Solution {
    /**
     * @param root: A Tree
     * @return: Preorder in ArrayList which contains node values.
     */
    public List<Integer> preorderTraversal(TreeNode root) {
        // write your code here
        List<Integer> result = new ArrayList<>();
        if(root == null) {
            return result;
        }
       
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return result;
    }
}

中序 InOrder Traversal

递归方式:

public class Solution {
    /**
     * @param root: A Tree
     * @return: Preorder in ArrayList which contains node values.
     */
    public List<Integer> preorderTraversal(TreeNode root) {
        // write your code here
        List<Integer> result = new ArrayList<>();
        if(root == null) {
            return result;
        }
       
        List<Integer> left = preorderTraversal(root.left);
        List<Integer> right = preorderTraversal(root.right);
       
	result.addAll(left);
        result.add(root.val);
        result.addAll(right);
        return result;
    }
}

栈的方式:

数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_07

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        
        Stack<TreeNode> stack = new Stack<>();
        TreeNode current = root;
        while (current != null || !stack.isEmpty()) {
            while (current != null) {
                stack.push(current);
                current = current.left;
            }
            current = stack.pop();
            result.add(current.val);
            current = current.right;
        }
        return result;
    }
}

后序 PostOrder Traversal

递归方式:

public class Solution {
    /**
     * @param root: A Tree
     * @return: Preorder in ArrayList which contains node values.
     */
    public List<Integer> preorderTraversal(TreeNode root) {
        // write your code here
        List<Integer> result = new ArrayList<>();
        if(root == null) {
            return result;
        }
       
        List<Integer> left = preorderTraversal(root.left);
        List<Integer> right = preorderTraversal(root.right);
       
	result.addAll(left);
        result.addAll(right);
	result.add(root.val);
        return result;
    }
}

栈的方式1:

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_08

public class Solution {
    /**
     * @param root: A Tree
     * @return: Postorder in ArrayList which contains node values.
     */
    public List<Integer> postorderTraversal(TreeNode root) {
        // write your code here
        List<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.peek();
            if (node.right == null && node.left == null) {
                result.add(stack.pop().val);
            }
            if (node.right != null) {
                stack.push(node.right);
                node.right = null;
            }
            if (node.left != null) {
                stack.push(node.left);
                node.left = null;
            }
        }
        return result;
    }
}

栈的方式2:

public ArrayList<Integer> postorderTraversal(TreeNode root) {
    ArrayList<Integer> result = new ArrayList<Integer>();
    Stack<TreeNode> stack = new Stack<TreeNode>();
    TreeNode prev = null; // previously traversed node
    TreeNode curr = root;

    if (root == null) {
        return result;
    }

    stack.push(root);
    while (!stack.empty()) {
        curr = stack.peek();
        if (prev == null || prev.left == curr || prev.right == curr) { // traverse down the tree
            if (curr.left != null) {
                stack.push(curr.left);
            } else if (curr.right != null) {
                stack.push(curr.right);
            }
        } else if (curr.left == prev) { // traverse up the tree from the left
            if (curr.right != null) {
                stack.push(curr.right);
            }
        } else { // traverse up the tree from the right
            result.add(curr.val);
            stack.pop();
        }
        prev = curr;
    }

    return result;
}

Red Black Tree 红黑树

AVL树(平衡二叉查找树)有两个特点
1、可以是空树
2、假如不是空树,任何一个节点的左子树和右子树都是平衡二叉树,并且高度之差绝对值不超过1。
我们所说的红黑树严格意义上并不是一个平衡二叉查找树,而是一个近似的平衡二叉查找树。在工程中很多用到平衡二叉查找树的地方都会用到红黑树,例如HashMap,1.8之前使用链表来解决哈希冲突,1.8开始用红黑树来解决链表过长的问题。(链表超过8个元素,开始转化为红黑树)。
所以红黑树出镜率甚至高于平衡二叉查找树,有时候会默认平衡二叉查找树就是红黑树。

红黑树定义:

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_09


红黑树规定,插入的时候叶子节点必须是红色,这个时候就会发生红黑树的平衡调整。

红黑树的平衡调整其实就是一个迭代的过程,调整的手段就是通过旋转(左旋或右旋)和改变颜色。

Binary Tree Right Side View 二叉树的右视角

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_10

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
public List<Integer> rightSideView(TreeNode root) {
      List<Integer> result = new ArrayList<>();
      if (root == null) {
        return result;  
      }
      Queue<TreeNode> queue = new LinkedList<>();
      queue.offer(root);
      boolean findRight = false;
      while (!queue.isEmpty()) {
        int size = queue.size();
        findRight = false;
        for (int i = 0; i < size; i++) {
            TreeNode node = queue.poll();
            if (!findRight) {
              result.add(node.val);
              findRight = true;
            }
            // 4 5
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (node.left != null) {
                queue.offer(node.left);
            }
        }
      }
      return result;
    }
}

Lowest Common Ancestor of a Binary Tree 二叉树最近公共祖先

如图:

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_11


代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) {
          // write your code here
            if(root == null || root == A || root == B) {
                return root;
            }
            TreeNode left = lowestCommonAncestor(root.left, A, B);
            TreeNode right = lowestCommonAncestor(root.right, A, B);
            if (left != null && right != null) {
                return root;
            }
            if (left != null) {
                return left;
            }
            if (right != null) {
                return right;
            }
        
            return null;
        
    }
}

Balanced Binary Tree 平衡二叉树

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构与算法分析视频教程_12

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
     public boolean isBalanced(TreeNode root) {
        // write your code here
         if(root == null) {
            return true;
         }
         return maxTree(root) != -1;
    }
    
    public int maxTree(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int left = maxTree(node.left);
        int right = maxTree(node.right);
        if (left == -1 || right == -1 || Math.abs(left - right) > 1) {
            return -1;
        } else {
            return Math.max(left, right) + 1;
        }
    }
    
}

Heap 堆

堆,就是用数组实现的二叉树

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_13


堆和普通树之间的区别是什么?其实堆并不能取代二叉搜索树。它们的区别是在于节点的顺序,在二叉搜索树中左子节点<父节点,右子节点>父节点;在堆中子节点一定比父节点要小。

在堆中其实是解决了TopK的问题,找出第一第二的数据。

Top K Largest 第K大元素

找出第几大元素

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_14


方法1:(使用Queue来处理)

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
             public int compare(Integer num1, Integer num2) {
                 return num2 - num1;
             }
         });

         for (int i : nums) {
             maxHeap.add(i);
         }

         for (int i = 0; i < k - 1; i++) {
             maxHeap.poll();
         }
         return maxHeap.poll();
    }
}

方法2:(使用快排来解决)最优方式

class Solution {
    public int findKthLargest(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k < 1 || k > nums.length) {
            return -1;
        }
        return partition(nums, 0, nums.length - 1, nums.length - k);
    }
    
    public int partition(int[] nums, int start, int end, int k) {
        if (start >= end) {
            return nums[k];
        }
        
        int left = start;
        int right = end;
        int pivot = nums[(start + end) / 2];
        while (left <= right) {
            while(left <= right && nums[left] < pivot) {
                left++;
            }
            while (left <= right && nums[right] > pivot) {
                right--;
            }
            if (left <= right) {
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
                left++;
                right--;
            }
        }
        if (k <= right) {
            return partition(nums, start, right, k);
        }
        if (k >= left) {
            return partition(nums, left, end, k);
        }
        
        return nums[k];
    }
}

Find Median from Data Stream 从数据流中找出中位数

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_15


数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_16

public class Solution {
    /**
     * @param nums: A list of integers.
     * @return: the median of numbers
     */
    public int[] medianII(int[] nums) {
        int count = nums.length;
       PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(count, new Comparator<Integer>(){
            public int compare(Integer num1, Integer num2) {
                return num2 - num1;
            }
        });
        PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(count);
        int[] answer = new int[count];
        int number = nums[0];
        answer[0] = number;
        for (int i = 1; i < count; i++) {
            if (nums[i] > number) {
                minHeap.add(nums[i]);
            } else {
                maxHeap.add(nums[i]);
            }
            if (Math.abs(maxHeap.size() - minHeap.size()) > 1) {
                if (minHeap.size() > maxHeap.size()) {
                    maxHeap.add(number);
                    number = minHeap.poll();
                } else {
                    minHeap.add(number);
                    number = maxHeap.poll();
                }
            } else {
                if (maxHeap.size() - minHeap.size() == 1 && maxHeap.peek() < number) {
                    minHeap.add(number);
                    number = maxHeap.poll();
                }
            }
            answer[i] = number;
        }
        return answer;
    }
    
}

//maxHeap   number   minHeap

Hash 散列

散列其实是安全领域所用的一种算法,常见的MD5、MD4、sha1等。

Deep Dive HashMap 深度解析HashMap

Subarray Sum Equals K 子数组的和等于K

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构与算法分析视频教程_17


前缀和数组:

数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_18

class Solution {
    public int subarraySum(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 1; i < nums.length; i++) {
            nums[i] += nums[i - 1];
        }
        int ans = 0;
        int temp = 0;
        map.put(0, 1);
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i] - k)) {
                ans += map.get(nums[i] - k);
            }
            temp = map.containsKey(nums[i]) ? map.get(nums[i]) + 1 : 1;
            map.put(nums[i], temp);
        }
        return ans;
    }
}

Clone Graph 图的克隆

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_19

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> neighbors;
    
    public Node() {
        val = 0;
        neighbors = new ArrayList<Node>();
    }
    
    public Node(int _val) {
        val = _val;
        neighbors = new ArrayList<Node>();
    }
    
    public Node(int _val, ArrayList<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
}
*/

class Solution {
    public Node cloneGraph(Node node) {
        if (node == null) {
            return null;
        }
        ArrayList<Node> nodes = getNodes(node);
        Map<Node, Node> mapping = new HashMap<>();
        // copy node
        for (Node n: nodes) {
            mapping.put(n, new Node(n.val));
        }
        //copy neighbors
        for (Node n: nodes) {
            Node newNode = mapping.get(n);
            for (Node neighbor: n.neighbors) {
                Node newNeighbor = mapping.get(neighbor);
                newNode.neighbors.add(newNeighbor);
            }
        }
        return mapping.get(node);
    }
    
    public ArrayList<Node> getNodes(Node node) {
        Queue<Node> queue = new LinkedList<Node>();
        HashSet<Node> set = new HashSet<Node>();
        // -> 1
        queue.offer(node);
        // -> 1
        set.add(node);
        while (!queue.isEmpty()) {
            //round 1: head = 1, queue is empty, set - > 1
            //round 2: head = 2, queue -> 4, set -> 1,2,4
            // round 3 head = 4 queue -> 3 , set -> 1,2,3,4
            //round 4 head = 3 queue is empty, set -> 1,2,3,4
            Node head = queue.poll();
            // round 1 neighbors = 2, 4
            // after round 1 set 1,2,4   queue 2,4
            // round 2 neighbors 1,3
            // after loop set -> 1,2,3,4 queue 4, 3
            //round 3 neighbors-> 1,3 queue 3
            // round 4 neighbots -> 2,4 , queue is 
            for (Node neighbor: head.neighbors) {
                if (!set.contains(neighbor)) {
                    set.add(neighbor);
                    queue.offer(neighbor);
                }
            }
        }
        return new ArrayList<Node>(set);
    }
}

longest substring without repeat 最长无重复字符串

Solution1:

public class Solution {
    /**
     * @param s: a string
     * @return: an integer
     */
    public int lengthOfLongestSubstring(String s) {
        // write your code here
        Set<Character> set = new HashSet<>();
        if (s == null || s.length() == 0) {
            return 0;
        }
        int max = 0;
        int right = 0;
        for (int i = 0; i < s.length(); i++) {
            while (right < s.length() && !set.contains(s.charAt(right))) {
                set.add(s.charAt(right++));
                max = Math.max(max, right - i);
            }
            set.remove(s.charAt(i));
        }
        return max;
    }
}

Solution2:

public class Solution {
    /**
     * @param s: a string
     * @return: an integer
     */
    public int lengthOfLongestSubstring(String s) {
        // write your code here
        if (s == null || s.length() == 0) {
       	return 0;
       }

       int i = 0;
       int j = 0;
       int ans = 0;
       int[] map = new int[256];
       for (i = 0; i < s.length(); i++) {
       	while (j < s.length() && map[s.charAt(j)] == 0) {
       		map[s.charAt(j)] = 1;
       		ans = Math.max(ans, j - i + 1);
       		j++;
       	}
       	map[s.charAt(i)] = 0;
       } 
       return ans;
    }
}

BFS 宽度优先搜索

宽度优先搜索是性价比最高的一种算法,比较容易理解。深度优先就是递归,而宽度优先走的是层,一层一层的来。

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_20


无向图(A->CDF->BG->E) 像队列一样,一层一层的遍历。

有向图(A->B->CFE->GD)也是一层层的宽度优先遍历。

Number of island 小岛屿数量

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_21

class Solution {
    public int numIslands(char[][] grid) {
        if(grid == null || grid.length == 0) {
            return 0;
        }
        if (grid[0] == null || grid[0].length == 0) {
            return 0;
        }
        int row = grid.length;
        int column = grid[0].length;
        boolean[][] visited = new boolean[row][column];
        int number = 0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    bfs(grid, i, j, visited);
                    number++;
                }
            }
        }
        return number;
    }
    
    public void bfs(char[][] grid, int i, int j, boolean[][] visited) {
        int[] kx ={1, -1, 0, 0};
        int[] ky ={0, 0, 1, -1};
        visited[i][j] = true;
        Queue<Integer> xQueue = new LinkedList<>();
        Queue<Integer> yQueue = new LinkedList<>();
        xQueue.offer(i);
        yQueue.offer(j);
        while (!xQueue.isEmpty()) {
            int currentX = xQueue.poll();
            int currentY = yQueue.poll();
            for (int k = 0; k < 4; k++) {
                int newX = currentX + kx[k];
                int newY = currentY + ky[k];
                if (newX >= 0 && newY >= 0 && newX < grid.length && newY < grid[0].length && !visited[newX][newY]) {
                    if (grid[newX][newY] == '1') {
                        xQueue.offer(newX);
                        yQueue.offer(newY);
                        visited[newX][newY] = true;
                    }
                }
            }
        }
    }
}

Word Ladder单词梯

这道题难度比较大

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_22

public class Solution {
    /*
     * @param start: a string
     * @param end: a string
     * @param dict: a set of string
     * @return: An integer
     */
    public int ladderLength(String start, String end, Set<String> dict) {
        // write your code here
        int steps = 1;
        if (dict == null) {
            return 0;
        }
        dict.add(end);
        Queue<String> queue = new LinkedList<>();
        queue.offer(start);
        Set<String> duplicate = new HashSet<>();
        duplicate.add(start);
        while (!queue.isEmpty()) {
            int size = queue.size();
            steps++;
            for (int i = 0; i < size; i++) {
                String word = queue.poll();
                List<String> nextWords = getNext(word, dict);
                for (String next: nextWords) {
                    if (duplicate.contains(next)) {
                        continue;
                    }
                    if (next.equals(end)) {
                        return steps;
                    }
                    duplicate.add(next);
                    queue.offer(next);
                }
            }
        }
        return -1;
    }
    
    public List<String> getNext(String word, Set<String> dict) {
        List<String> next = new ArrayList<>();
        for (char i = 'a'; i <= 'z'; i++) {
            for (int j = 0; j < word.length(); j++) {
                String potentialNext = changedWord(word, i, j);
                if (dict.contains(potentialNext)) {
                    next.add(potentialNext);
                }
            }
        }
        return next;
    }
    
    public String changedWord(String word, char c, int i) {
        char[] words = word.toCharArray();
        words[i] = c;
        return new String(words);
    }
    
}

DFS 深度优先搜索

Permutations 排列

写出一组数字的全排列

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_23

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (nums == null) {
            return result;
        }
        if (nums.length == 0) {
            result.add(new ArrayList<Integer>());
            return result;
        }
        List<Integer> list = new ArrayList<>();
        helper(result, nums, list);
        return result;
    }
    
    public void helper(List<List<Integer>> result, int[] nums, List<Integer> list) {
        if (list.size() == nums.length) {
            result.add(new ArrayList<Integer>(list));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (list.contains(nums[i])) {
                continue;
            }
            list.add(nums[i]);
            helper(result, nums, list);
            list.remove(list.size() - 1);
        }
    }
}

Subset 子集

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_24

public class Solution {
    /**
     * @param nums: A set of numbers
     * @return: A list of lists
     */
    public List<List<Integer>> subsets(int[] nums) {
        // write your code here
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (nums == null || nums.length == 0) {
            result.add(new ArrayList<>());
            return result;
        }
        List<Integer> list = new ArrayList<>();
        Arrays.sort(nums);
        dfs(nums, result, list, 0);
        return result;
    }
    
    public void dfs(int[] nums, List<List<Integer>> result, List<Integer> list, int pos) {
        
        /*
            []
            1
                1, 2
                    1, 2, 3
                1, 3
            2
                2, 3
            3
        */
            
        result.add(new ArrayList<Integer>(list));
        for (int i = pos; i < nums.length; i++) {
            list.add(nums[i]);
            dfs(nums, result, list, i + 1);
            list.remove(list.size() - 1);
        }
    }
}

n queens 皇后n

import java.util.ArrayList;
import java.util.List;

public class NQueens {
	public static void main(String[] args) {
		List<List<String>> chessboard = new NQueens().solveNQueens(4);
		for (List<String> list: chessboard) {
			for (String s: list) {
				System.out.println(s + " ");
			}
			System.out.println();
		}
	}
    List<List<String>> solveNQueens(int n) {
      List<List<String>> results = new ArrayList<>();
      if (n <= 0) {
    	  return results;
      }
      //....
      search(results, new ArrayList<Integer>(), n);
      return results;
    }
    
    //cols.get(0,1).... 0,1 就是colIndex
    public void search(List<List<String>> results, List<Integer> cols, int n) {
    	if (cols.size() == n) {
    		results.add(drawChessboard(cols));
    		return;
    	}
    	for (int colIndex = 0; colIndex < n; colIndex++) {
    		if (!isValid(cols, colIndex)) {
    			continue;
    		}
    		cols.add(colIndex);
    		search(results, cols, n);
    		cols.remove(cols.size() - 1);
    	}
    }
    
    private boolean isValid(List<Integer> cols, int column) {
    	int row = cols.size();
    	for (int rowIndex = 0; rowIndex < cols.size(); rowIndex++) {
    		//同一竖线
    		if (cols.get(rowIndex) == column) {
    			return false;
    		}
    		//左到右斜线
    		if (rowIndex - cols.get(rowIndex) == row - column) {
    			return false;
    		}
    		//右到左斜线
    		if (rowIndex + cols.get(rowIndex) == row + column) {
    			return false;
    		}
    	}
    	return true;
    }
    public List<String> drawChessboard(List<Integer> cols) {
    	List<String> chessboard = new ArrayList<>();
    	for (int i = 0; i < cols.size(); i++) {
    		StringBuilder sb = new StringBuilder();
    		for (int j = 0; j < cols.size(); j++) {
    			sb.append(j == cols.get(i) ? 'Q' : '.');
    		}
    		chessboard.add(sb.toString());
    	}
    	return chessboard;
    }
}

String Manipulation 字符串

Reverse Word in a String 逆转字符串

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_25

class Solution {
    public String reverseWords(String s) {
        if (s == null || s.trim().length() == 0) {
            return "";
        }
        String[] words = s.split(" ");
        StringBuilder answer = new StringBuilder();
        for (int i = 0; i < words.length; ++i) {
            answer.append(String.valueOf(' ') + reverseString(words[i]));
        }
        return answer.toString().trim();
    }
    
    public String reverseString(String s) {
        String ans = "";
        for (int i = s.length() - 1; i>= 0; i--) {
            ans += String.valueOf(s.charAt(i));
        }
        return ans;
    }
}

数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_26

class Solution {
    public String reverseWords(String s) {
           // write your code here
        if (s == null || s.trim().length() == 0) {
            return "";
        }
        String[] words = s.trim().split(" ");
        StringBuilder sb = new StringBuilder();
        for (int i = words.length - 1; i >= 0; i--) {
            if(words[i].trim().equals("")) {
                continue;
            }
            sb.append(words[i] + " ");
        }
        return sb.toString().trim();
    }
}

Integer to Roman 整数转罗马数字

class Solution {
    public String intToRoman(int n) {
        String[] M = {"","M","MM","MMM"};
        String[] C = {"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"};
        String[] X = {"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"};
        String[] I = {"", "I","II","III","IV","V","VI","VII","VIII","IX"};
        return M[n/1000] + C[(n/100) % 10] + X[(n/10) % 10] + I[n % 10];
    }
}

Trie 有序树

What is Typeahead?提示词

数据结构与算法分析视频教程 数据结构算法解析(第2版)_算法_27

class TrieNode {
    //a - z
    char c;
    HashMap<Character, TrieNode> children = new HashMap<>();
    boolean hasWord;
    public TrieNode() {
    
    }
    public TrieNode(char c) {
        this.c = c;
    }
    
}
public class Trie {

    private TrieNode root;
    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    public void insert(String word) {
        TrieNode current = root;
        HashMap<Character, TrieNode> children = current.children;
        char[] wordArray = word.toCharArray();
        for (int i = 0; i < wordArray.length; i++) {
            char wc = wordArray[i];
            if (!children.containsKey(wc)) {
                TrieNode newNode = new TrieNode(wc);
                children.put(wc, newNode);
                current = newNode;
            } else {
                current = children.get(wc);
            }
            children = current.children;
            if (i == wordArray.length - 1) {
                current.hasWord = true;
            }
        }
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        HashMap<Character, TrieNode> children = root.children;
        TrieNode current = null;
        char[] wordArray = word.toCharArray();
        for (int i = 0; i < wordArray.length; i++) {
            if (children.containsKey(wordArray[i])) {
                current = children.get(wordArray[i]);
                children = current.children;
            } else {
                return false;
            }
        }
        if (current.hasWord){
            return true;    
        } else {
            return false;
        }
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        HashMap<Character, TrieNode> children = root.children;
        TrieNode current = null;
        char[] wordChar = prefix.toCharArray();
        for (int i = 0; i < wordChar.length; i++) {
            if (children.containsKey(wordChar[i])) {
                current = children.get(wordChar[i]);
                children = current.children;
            } else {
                return false;
            }
        }
        return true;
    }
}

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * boolean param_2 = obj.search(word);
 * boolean param_3 = obj.startsWith(prefix);
 */

Union Find 并查集

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构_28

// Find
HashMap<Integer, Intger> father = new HashMap<>();
pubic int find(int x) {
	int parent = father.get(x);
	while (parent != father.get(parent)) {
		parent = father.get(parent);
	}
	return parent;
}
//Time Complexity O(n) 

// Union
// union(E, B)
HashMap<Integer, Integer> father = new HashMap<>();
public void union(int x, int y) {
	int fa_x = find(x);
	int fa_y = find(y);
	if (fa_x != fa_y) {
		father.put(fa_x, fa_y);
	}
}
// Time Complexity O(n)


class UnionFind {
	HashMap<Integer, Integer> father = new HashMap<>();
	UnionFind(int n) {
		for (int i = 0; i < n; i++) {
			father.put(i, i);
		}
	}

	int find2(int x) {
		int parent = father.get(x);
		while (parent != father.get(parent)) {
			parent = father.get(parent);
		}
		int temp = -1;
		int fa = father.get(x);
		while (fa != father.get(fa)) {
			temp = father.get(fa);
			father.put(fa, parent);
			fa = temp;
		}
		return parent;
	}

	void union(int x, int y) {
		int fa_x = find2(x);
		int fa_y = find2(y);
		if (fa_x != fa_y) {
			father.put(fa_x, fa_y);
		}
	}
}

小岛问题

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_29

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

class Point {
	int x;
	int y;
	Point() {
		x = 0;
		y = 0;
	}
	Point(int a, int b) {
		x = a;
		y = b;
	}
}

public class NumberOfIsland2 {
	public static void main(String[] args) {
		int n = 4;
		int m = 5;
		Point[] operators = new Point[4];
		Point point1 = new Point(1, 1);
		Point point2 = new Point(0, 1);
		Point point3 = new Point(3, 3);
		Point point4 = new Point(3, 4);
		operators[0] = point1;
		operators[1] = point2;
		operators[2] = point3;
		operators[3] = point4;
		List<Integer> list = new NumberOfIsland2().numIslands(n, m, operators);
		for (int number: list) {
			System.out.println(number);
		}
	}
	class UnionFind {
		private Map<Integer, Integer> father = new HashMap<>();
		public UnionFind(int row, int column) {
			for (int i = 0; i < row; i ++) {
				for (int j = 0; j < column; j++) {
					int id = convertedId(i, j, column);
					father.put(id,  id);
				}
			}
		}
		
		public int find(int x) {
			int parent = father.get(x);
			while (parent != father.get(parent)) {
				parent = father.get(parent);
			}
			int temp = -1;
			int fa = father.get(x);
			while (fa != father.get(fa)) {
				temp = father.get(fa);
				father.put(fa,  parent);
				fa = temp;
			}
			return parent;
		}
		
		public void union(int x, int y) {
			int parent_x = father.get(x);
			int parent_y = father.get(y);
			if (parent_x != parent_y) {
				father.put(parent_x, parent_y);
			}
		}
		public int convertedId(int x, int y, int row) {
			return x * row + y;
		}
	}
	public List<Integer> numIslands(int n, int m, Point[] operators) {
		List<Integer> result = new ArrayList<>();
		if (operators == null || operators.length == 0) {
			return result;
		}
		UnionFind unionFind = new UnionFind(n, m);
		boolean[][] island = new boolean[n][m];
		int[] dx = {0, 0, 1, -1};
		int[] dy = {1, -1, 0, 0};
		int count = 0;
		for (Point point: operators) {
			int x = point.x;
			int y = point.y;
			if (!island[x][y]) {
				island[x][y] = true;
				count++;
				int id = unionFind.convertedId(x, y, m);
				for (int i = 0; i < 4; i++) {
					int current_x = x + dx[i];
					int current_y = y + dy[i];
					if (current_x >= 0 && current_x < n && current_y >= 0 && current_y < m && island[current_x][current_y]) {
						int nid = unionFind.convertedId(current_x, current_y, m);
						int father = unionFind.find(id);
						int nfather = unionFind.find(nid);
						if (father != nfather) {
							count--;
							unionFind.union(father, nfather);
						}
					}
				}
				result.add(count);
			}
		}
		return result;
	}
}

Dynamic Programming 动态规划

Climbing Stairs 爬楼梯

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构与算法分析视频教程_30

//滚动数组
class Solution {
    public int climbStairs(int n) {
        // n = 1 -> 1 (ways -> 1)
        // n = 2 -> 2(ways, 11, 2)
        // n = 3 -> 3(ways 111, 12, 21)
        // n = 4 -> 5(ways 1111,112,121,211,22)
        // n = 5 -> 8(ways 11111,1112,1121,1211,2111,221,212,122)
        if (n <= 3) {
            return n;
        } 
        int[] steps = new int[3];
        steps[0] = 1;
        steps[1] = 2;
        for (int i = 2; i < n; i++) {
            steps[i % 3] = steps[(i - 1) % 3] + steps[(i - 2) % 3];
        }
        return steps[(n - 1) % 3];
    }
}
     
     /*  DFS
      public int climbStairs(int n) {
        // n == 1 -> 1
        // n == 2 -> 2
        // n == 0 -> 0
        if (n < 3) {
            return n;
        }
        return climbStairs(n - 1) + climbStairs(n - 2);
    }
     */

    /* DP
    steps 
    0 1 2 3 4 5 ... 50
      1 2 3 5 8     ...
      public int climbStairs(int n) {
        // n == 1 -> 1
        // n == 2 -> 2
        // n == 0 -> 0
        if (n < 3) {
            return n;
        }
        int[] steps = new int[n + 1];
        steps[1] = 1;
        steps[2] = 2;
        for (int i = 3; i <= n; i++) {
            steps[i] = steps[i - 1] + steps[i - 2];
        }
        return steps[n];
    }
    */
    
/*
steps 
0 1 2
5 8 3
https://leetcode.com/problems/fibonacci-number/
https://leetcode.com/problems/house-robber/
*/

Unique Path 独一的道路

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_31

public int uniquePaths(int m, int n) {
         if(m == 0 || n == 0) {
             return 1;
         }
        int[][] path = new int[m][n];
        for (int i  = 0; i < m; i++) {
            path[i][0] = 1;
        }
        for (int i = 0; i < n; i++) {
            path[0][i] = 1;
        }
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                path[i][j] = path[i - 1][j] + path[i][j - 1];
            }
        }
        return path[m - 1][n - 1];
    }

数据结构与算法分析视频教程 数据结构算法解析(第2版)_List_32

public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if (obstacleGrid == null || obstacleGrid.length == 0) {
            return 1;
        }
        if (obstacleGrid[0] == null || obstacleGrid[0].length == 0) {
            return 1;
        }
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] result = new int[m][n];
        for (int i = 0; i < m; i++) {
            if (obstacleGrid[i][0] != 1) {
                result[i][0] = 1;
            } else {
                break;
            }
        }
        for (int j = 0; j < n; j++) {
            if (obstacleGrid[0][j] != 1) {
                result[0][j] = 1;
            } else {
                break;
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (obstacleGrid[i][j] == 1) {
                    result[i][j] = 0;
                } else {
                    result[i][j] = result[i - 1][j] + result[i][j - 1];
                }
            }
        }
        return result[m - 1][n - 1];
    }

Longest Common 最长子串

数据结构与算法分析视频教程 数据结构算法解析(第2版)_数据结构与算法分析视频教程_33

class Solution {
    public int longestCommonSubsequence(String A, String B) {
        if (A == null || A.length() == 0 || B == null || B.length() == 0) {
            return 0;
        }
        int aLength = A.length();
        int bLength = B.length();
        int[][] longest = new int[aLength + 1][bLength + 1];
        for (int i = 1; i <= A.length(); i++) {
            for (int j = 1; j <= B.length; j++) {
                if (A.charAt(i- 1) == B.charAt(j - 1)) {
                    longest[i][j] = longest[i - 1][j - 1] + 1;
                } else {
                    longest[i][j] = Math.max(longest[i - 1][j], longest[i][j - 1]);
                }
            }
        }
        return longest[aLength][bLength];
    }
}

Word Break 单词拆分

数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_34

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> dict = new HashSet<>();
        for (String word: wordDict) {
            dict.add(word);
        }
        boolean[] canSegment = new boolean[s.length() + 1];
        canSegment[0] = true;
        int largetlengthWord = getLargest(dict);
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j <= largetlengthWord && j <= i; j++) {
                if (!canSegment[i - j]) {
                    continue;
                }
                if (dict.contains(s.substring(i - j, i))) {
                    canSegment[i] = true;
                }
            }
        }
        return canSegment[s.length()];
    }
    
    public int getLargest(Set<String> dict) {
        int max = 0;
        for (String word: dict) {
            max = Math.max(max, word.length());
        }
        return max;
    }
}


//               l e e t c o d e
//cansegment// 1 0 0 0 1       1
    //index//  0 1 2 3 4 5 6 7 8

Palindrome Partitioning 回文分割

数据结构与算法分析视频教程 数据结构算法解析(第2版)_二叉树_35

class Solution {
    public int minCut(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        int[] minCut = new int[s.length() + 1];
        for (int i = 0; i <= s.length(); i++) {
            minCut[i] = i - 1;
        }
        boolean[][] pali = new boolean[s.length()][s.length()];
        assignPali(pali, s);
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (pali[j][i - 1]) {
                    minCut[i] = Math.min(minCut[j] + 1, minCut[i]);
                }
            }
        }
        return minCut[s.length()];
    }
    
    public void assignPali(boolean[][] pali, String s) {
        int length = s.length();
        for (int i = 0; i < length; i++) {
            pali[i][i] = true;
        }
        for (int i = 0; i < length - 1; i++) {
            pali[i][i + 1] = (s.charAt(i) == s.charAt(i + 1));
        }
        for (int i = 2; i < length; i++) {
            for (int j = 0; j + i < length; j++) {
                pali[j][i + j] = pali[j + 1][i + j - 1] && s.charAt(j) == s.charAt(i + j);
            }
        }
    }
}