数据结构与算法
- 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个父节点(除了根节点没有父节点)。
树是不能有环的。
一般我们所用的树都是说二叉树和二叉搜索树。
Binary Tree
树的结构多种多样,不过我们用的最多的还是二叉树。二叉树包含左子节点和右子节点。并不要求每个节点都有2个子节点。
满二叉树: 所有的叶子节点都在最底层,这样的二叉树称为满二叉树。
完全二叉树: 叶子节点都在最底下的两层,最后一层的叶子节点都靠左排列。除了最后一层,其它层节点个数都达到最大。
树的存储,有链式存储法和数组存储,常用的还是链式存储。
A是根节点存储在下标为1的位置,2i为左节点,(2i+1)为右节点
B是左节点存储到下标2的位置,C是右节点存储在下标等于3的位置。
这样就可以通过下标计算把整棵树都串起来。
数组存储的缺点,如果是完全二叉树无疑是最节省内存空间的存储方式,但是如果是非完全二叉树将会浪费很多的内存空间。
Binary Search Tree 搜索二叉树
搜索二叉树也叫二叉查找树,是二叉树最常用的一种类型。
二叉查找树是为了方便快速查找一个数据而生的,不仅可以快速查找,也可以快速插入、删除一个数据。
二叉查找树要求每个节点左节点的值都小于节点的值,而右节点的值都大于节点的值。
二叉查找树又叫二叉有序树,因为它可以输出一个有序的数组,时间复杂度为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 树的遍历
经典的遍历方式分为前序、中序、后序遍历。
前序:对于树中任意节点,先打印自己,再打左节点,后打右节点。
中序:对于树中任意节点,先打印左节点,再打印自己,后打印右节点
后序:对于树中任意节点,先打印左节点,再打印右节点,后打印自己
对于中序遍历(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;
}
}
栈的方式:
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;
}
}
栈的方式:
/**
* 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:
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个元素,开始转化为红黑树)。
所以红黑树出镜率甚至高于平衡二叉查找树,有时候会默认平衡二叉查找树就是红黑树。
红黑树定义:
红黑树规定,插入的时候叶子节点必须是红色,这个时候就会发生红黑树的平衡调整。
红黑树的平衡调整其实就是一个迭代的过程,调整的手段就是通过旋转(左旋或右旋)和改变颜色。
Binary Tree Right Side View 二叉树的右视角
/**
* 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 二叉树最近公共祖先
如图:
代码如下:
/**
* 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 平衡二叉树
/**
* 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 堆
堆,就是用数组实现的二叉树。
堆和普通树之间的区别是什么?其实堆并不能取代二叉搜索树。它们的区别是在于节点的顺序,在二叉搜索树中左子节点<父节点,右子节点>父节点;在堆中子节点一定比父节点要小。
在堆中其实是解决了TopK的问题,找出第一第二的数据。
Top K Largest 第K大元素
找出第几大元素
方法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 从数据流中找出中位数
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
前缀和数组:
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 图的克隆
/*
// 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 宽度优先搜索
宽度优先搜索是性价比最高的一种算法,比较容易理解。深度优先就是递归,而宽度优先走的是层,一层一层的来。
无向图(A->CDF->BG->E) 像队列一样,一层一层的遍历。
有向图(A->B->CFE->GD)也是一层层的宽度优先遍历。
Number of island 小岛屿数量
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单词梯
这道题难度比较大
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 排列
写出一组数字的全排列
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 子集
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 逆转字符串
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;
}
}
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?提示词
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 并查集
// 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);
}
}
}
小岛问题
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 爬楼梯
//滚动数组
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 独一的道路
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];
}
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 最长子串
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 单词拆分
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 回文分割
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);
}
}
}
}