文章目录
一、25 合并两个排序的链表
1.算法描述
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
2.算法题解
根据链表 l1、l2 是递增的,因此可以使用双指针遍历链表,根据 val1、val2 的大小关系确定节点添加顺序,两指针交替前进,直至遍历完成。
由于初始状态合并链表中无节点,因此需要初始化一个辅助节点 root 作为合并链表的伪头结点。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode root = new ListNode(0), cur = root;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
cur.next = l1;
l1 = l1.next;
} else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 != null ? l1 : l2;
return root.next;
}
}
二、26 树的子结构
1.算法描述
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
2.算法题解
若树 B 是树 A 的子结构,则子结构的根节点为树 A 的任意一个节点,因此判断树 B 是否是树 A 的子结构需要进行一下步骤:
- 遍历树 A 的每个节点 node(函数 isSubStructure)
- 判断树 A 中以 node 为根节点的子树是否包含树 B(函数 check)
isSubStructure(A, B) 函数:
特例处理:当 树 A 为空或树 B 为空时,直接返回 false。
若树 B 是树 A 的子结构,则必满足以下三种情况之一,因此用或 || 连接:
- 以节点 A 为根节点的子树包含树 B,对应 check(A, B)
- 树 B 是树 A 左子树的子结构,对应 isSubStructure(A.left, B)
- 树 B 是树 A 右子树的子结构,对应 isSubStructure(A.right, B)
- 最后两个步骤实质上是在对树 A 做先序遍历
check(A, B) 函数:
- 当节点 B 为空时,说明树 B 已匹配完成,因此返回 true
- 当节点 A 为空时,说明已经越过树 A 叶子节点,即匹配失败返回 false
- 当节点 A 和 B 的值不同时,说明匹配失败,返回 false
- 当节点 A 等于节点 B 时,判断 A 和 B 的左子节点和右子节点是否相等,即 check(A.left, B.left) 和 check(A.right, B.right)
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
return (A != null && B != null) && (check(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
}
public boolean check(TreeNode A, TreeNode B) {
if (B == null) {
return true;
}
if (A == null || A.val != B.val) {
return false;
}
return check(A.left, B.left) && check(A.right, B.right);
}
}
三、27 二叉树的镜像
1.算法描述
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
2.算法题解
递归法:根据二叉树镜像的定义,考虑递归遍历(dfs)二叉树,交换每个节点的左 / 右子节点,即可生成二叉树的镜像。
- 初始化节点 tmp ,用于暂存 root 的左子节点。
- 递归右子节点 mirrorTree(root.right),并将返回值作为 root 的左子节点。
- 递归左子节点 mirrorTree(tmp),并将返回值作为 root 的右子节点。
- 返回 root 节点。
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);
return root;
}
}
辅助栈:利用栈遍历(bfs)树的所有节点 node,并交换每个 node 的左 / 右子节点。
- 特例处理:当 root 为空时,直接返回 null
- 初始化栈:加入根节点 root
- 循环交换:当栈 stack 为空时跳出
- 出栈:记为 node
- 添加子节点:将 node 的左和右子节点入栈
- 交换节点:交换 node 的左 / 右子节点
- 返回值: 返回根节点 root
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) {
return null;
}
Stack<TreeNode> stack = new Stack() {{
add(root);
}};
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
if (node.left != null) {
stack.add(node.left);
}
if (node.right != null) {
stack.add(node.right);
}
TreeNode tmp = node.left;
node.left = node.right;
node.right = tmp;
}
return root;
}
}
四、28 对称的二叉树
1.算法描述
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
2.算法题解
对称二叉树定义:对于树中任意两个对称节点 L 和 R 一定有:
- L.val = R.val :即此两对称节点值相等。
- L.left.val = R.right.val 即 L 的左子节点和 R 的右子节点对称
- L.right.val = R.left.val 即 L 的右子节点和 R 的左子节点对称
class Solution {
public boolean isSymmetric(TreeNode root) {
return root == null ? true : check(root.left, root.right);
}
public boolean check(TreeNode L, TreeNode R) {
if (L == null && R == null) {
return true;
}
if (L == null || R == null || L.val != R.val) {
return false;
}
return check(L.left, R.right) && check(L.right, R.left);
}
}
五、29 顺时针打印矩阵
1.算法描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
2.算法题解
根据题意可知,顺时针打印的顺序是:从左到右,从上到下,从右到左,从下到上
- 当 matrix 为空时,直接返回空数组。
- 创建返回数组,并定义返回数组的下标 index = 0
- 定义边界上下左右边界 top = 0, bottom = matrix.length - 1, left = 0, right = matrix[0].length - 1
- 按照 “从左向右、从上向下、从右向左、从下向上” 四个方向循环,每个方向打印中做以下三件事:
- 根据边界打印,即将元素按顺序添加至列表 res 尾部
- 边界向内收缩 1 代表已被打印
- 判断是否打印完毕,边界是否相遇,若打印完毕则跳出
打印方向 | 打印边界 | 边界收缩 | 是否打印完毕 |
---|---|---|---|
从左到右 | 左边界 left,右边界 right | 上边界 top 加 1 | top > bottom |
从上到下 | 上边界 top,下边界 bottom | 右边界 right 减 1 | left > right |
从右到左 | 右边界 right,左边界 left | 下边界 bottom 加 1 | top > bottom |
从下到上 | 下边界 bottom,上边界 top | 左边界 left 加 1 | left > right |
class Solution {
public int[] spiralOrder(int[][] matrix) {
if (matrix.length == 0) {
return new int[0];
}
int index = 0;
int left = 0, right = matrix[0].length - 1, top = 0, bottom = matrix.length - 1;
int[] result = new int[matrix[0].length * matrix.length];
while (true) {
for (int i = left; i <= right; i++) {
result[index++] = matrix[top][i]; // left ---> right
}
if (++top > bottom) {
break;
}
for (int i = top; i <= bottom; i++) {
result[index++] = matrix[i][right]; // top ---> bottom
}
if (left > --right) {
break;
}
for (int i = right; i >= left; i--) {
result[index++] = matrix[bottom][i]; // right ---> left
}
if (top > --bottom) {
break;
}
for (int i = bottom; i >= top; i--) {
result[index++] = matrix[i][left]; // bottom ---> top
}
if (++left > right) {
break;
}
}
return result;
}
}