1 平衡二叉树
- 题目。给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 - 思路。递归求解即可。左子树和右子树都是平衡二叉树并且左右子树的高度差小于等于1。
- 代码。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
return isBalanced(root.left) && isBalanced(root.right) && Math.abs(depth(root.left) - depth(root.right)) <= 1;
}
private int depth(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(depth(root.left), depth(root.right)) + 1;
}
}
2 有序链表转换二叉搜索树
- 题目。给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree - 思路。可以将有序链表的值存储在一个数组中,这样可以直接使用数组下标取到节点值。
- 代码。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
/**
* 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 TreeNode sortedListToBST(ListNode head) {
if (head == null) {
return null;
}
List<Integer> list = get(head);
Integer[] val = list.toArray(new Integer[list.size()]);
int mid = val.length / 2;
TreeNode root = new TreeNode(val[mid]);
root.left = f(val, 0, mid - 1);
root.right = f(val, mid + 1, val.length - 1);
return root;
}
private TreeNode f(Integer[] val, int i, int j) {
if (i > j) {
return null;
}
if (i == j) {
return new TreeNode(val[i]);
}
int mid = i + (j - i) / 2;
TreeNode r = new TreeNode(val[mid]);
r.left = f(val, i, mid - 1);
r.right = f(val, mid + 1, j);
return r;
}
private List<Integer> get(ListNode head) {
List<Integer> list = new ArrayList<>();
ListNode cur = head;
while (cur != null) {
list.add(cur.val);
cur = cur.next;
}
return list;
}
}
3 回文子串
- 题目。给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings - 思路。遇到回文串的题目,自然而然可以想到Manacher算法,该算法可以用O(N)的时间复杂度求解出一个字符串的最长回文子串。另外,求解字符串中的回文串时,可以考虑从某个字符开始,向左右两个方向扩的思路。
- 代码。
class Solution {
public int countSubstrings(String s) {
int n = s.length(), ans = 0;
for (int i = 0; i < 2 * n - 1; ++i) {
int l = i / 2, r = i / 2 + i % 2;
while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
--l;
++r;
++ans;
}
}
return ans;
}
}
4 扫雷游戏
- 题目。让我们一起来玩扫雷游戏!
给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。
示例 1:
输入:
[[‘E’, ‘E’, ‘E’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘M’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘E’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘E’, ‘E’, ‘E’]]
Click : [3,0]
输出:
[[‘B’, ‘1’, ‘E’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘M’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘1’, ‘1’, ‘B’],
[‘B’, ‘B’, ‘B’, ‘B’, ‘B’]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minesweeper - 思路。这道题需要首先读懂题意。可以从给定的初始点开始进行bfs,每次向看下周围有多少雷,如果当前位置周围有雷则标记当前位置有几个雷;如果当前位置没有雷,那么将当前位置周围的E位置加入到队列继续求解。在代码实现时,我们可以通过二位数组表示方向,使用以为数组表示坐标。
- 代码。
class Solution {
public char[][] updateBoard(char[][] board, int[] click) {
if (board == null || board.length == 0 || board[0] == null || board[0].length == 0 || click == null || click.length != 2) {
return board;
}
int r = board.length;
int c = board[0].length;
int[][] dir = new int[][]{{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
boolean[][] visited = new boolean[r][c];
Queue<int[]> queue = new LinkedList<>();
queue.add(click);
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0];
int y = cur[1];
if (visited[x][y]) {
continue;
}
visited[x][y] = true;
if (board[x][y] == 'M') {
board[x][y] = 'X';
return board;
}
int mineNumber = 0;
for (int[] d : dir) {
int p = x + d[0];
int q = y + d[1];
if (p >= 0 && p < r && q >= 0 && q < c) {
if (board[p][q] == 'M') {
mineNumber++;
}
}
}
// 当前位置x y周围没有雷
if (mineNumber == 0) {
board[x][y] = 'B';
// 将x y周围没有处理过的位置,也就是周围的E加入队列
for (int[] d : dir) {
int p = x + d[0];
int q = y + d[1];
if (p >= 0 && p < r && q >= 0 && q < c && board[p][q] == 'E') {
queue.add(new int[]{p, q});
}
}
} else {
board[x][y] = (char)(mineNumber + '0');
}
}
return board;
}
}
5 二叉树的最小深度
-
题目。给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],3
/
9 20
/
15 7
返回它的最小深度 2.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree
- 思路。注意到这里的最小深度定义的是到叶子节点的最小深度,所以递归求解时如果到了null,我们将深度置为Integer.MAX_VALUE而不是0,然后再取左右子树最小深度。
- 代码。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
if (root != null && root.left == null && root.right == null) {
return 1;
}
int leftMin = minDepth(root.left);
leftMin = leftMin == 0 ? Integer.MAX_VALUE : leftMin;
int rightMin = minDepth(root.right);
rightMin = rightMin == 0 ? Integer.MAX_VALUE : rightMin;
return 1 + Math.min(rightMin, leftMin);
}
}
6 24点游戏
-
题目。你有 4 张写有 1 到 9 数字的牌。你需要判断是否能通过 *,/,+,-,(,) 的运算得到 24。
示例 1:
输入: [4, 1, 8, 7]
输出: True
解释: (8-4) * (7-1) = 24
示例 2:
输入: [1, 2, 1, 2]
输出: False
注意:
除法运算符 / 表示实数除法,而不是整数除法。例如 4 / (1 - 2/3) = 12 。
每个运算符对两个数进行运算。特别是我们不能用 - 作为一元运算符。例如,[1, 1, 1, 1] 作为输入时,表达式 -1 - 1 - 1 - 1 是不允许的。
你不能将数字连接在一起。例如,输入为 [1, 2, 1, 2] 时,不能写成 12 + 12 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/24-game -
思路。先从4个数中选两个做排列,然后从加减乘除中选择一个符号,得到一个数。共有A(4,2)*4=48种可能性
然后从剩下的三个数中选择两个做排列,然后从加减乘除中选择一个符号,得到一个数。共有A(3,2)4=24种可能性
剩下的两个数做排列,看得到的数是不是24即可。共有A(2,2)4=8种可能性。综上所述,一共有48248=9216种可能性。需要考虑的是这里的除法是浮点数运算,需要考虑误差。
另外加法和乘法都满足交换律,因此如果选择的运算操作是加法或乘法,则对于选出的 22 个数字不需要考虑不同的顺序,在遇到第二种顺序时可以不进行运算,直接跳过。 -
代码。
class Solution {
private static final int TARGET = 24;
private static final double EPSILON = 1e-6;
public boolean judgePoint24(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
List<Double> list = new ArrayList<>(4);
for (int num : nums) {
list.add((double) num);
}
return canReachTarget(list);
}
private boolean canReachTarget(List<Double> list) {
if (list == null || list.size() == 0) {
return false;
}
// list中只有一个数了,看是不是等于24
if (list.size() == 1) {
if (Math.abs(list.get(0) - TARGET) <= EPSILON) {
return true;
} else {
return false;
}
}
int size = list.size();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (i == j) {
continue;
}
// 从list中取两个数
double p = list.get(i);
double q = list.get(j);
List<Double> newList = new ArrayList<>();
for (int k = 0; k < size; k++) {
if (k != i && k != j) {
newList.add(list.get(k));
}
}
// 从4个符号中取一个
double newNumber;
for (int k = 0; k < 4; k++) {
// 认为0代表加法
if (k == 0) {
newNumber = p + q;
} else if (k == 1) {
// 认为1代表减法
newNumber = p - q;
} else if (k == 2) {
// 认为2代表乘法
newNumber = p * q;
} else {
// 认为3代表除法
if (q != 0) {
newNumber = p / q;
} else {
return false;
}
}
newList.add(newNumber);
if (canReachTarget(newList)) {
return true;
} else {
newList.remove(newNumber);
}
}
}
}
return false;
}
}
7 数字范围按位与
- 题目。给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。
示例 1:
输入: [5,7]
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bitwise-and-of-numbers-range - 思路。该题可转化为找m和n对应的二进制字符串的最长公共前缀,这个最长公共前缀和后面的0组成答案。
- 代码。
class Solution {
public int rangeBitwiseAnd(int m, int n) {
int shift = 0;;
while (m < n) {
m = m >> 1;
n = n >> 1;
shift++;
}
return m << shift;
}
}