面试题28.对称的二叉树
递归
对于二叉树定义,一定有:
- 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) {
if(root == null) return false;
//从两个子节点开始判断
return recur(root.left, root.right);
}
public boolean recur(TreeNode L, TreeNode R) {
//如果左右子节点都为空,说明当前节点是叶子节点,返回true
if(L == null && R == null) return true;
//如果当前节点只有一个子节点,或者有两个子节点,但两个子节点的值不相同,直接返回false
if(L == null || R == null || L.val != R.val) return false;
//然后比较左子节点的左子节点和右子节点的右子节点比较
//左子节点的右子节点和右子节点的左子节点比较
return recur(L.left, R.right) && recur(L.right, R.left);
}
}
- 时间复杂度 O(N) :其中 N 为二叉树的节点数量,每次执行 recur() 可以判断一对节点是否对称,因此最多调用 N/2 次 recur() 方法。
- 空间复杂度 O(N) :最差情况下(见下图),二叉树退化为链表,系统使用 O(N) 大小的栈空间。
迭代
class Solution {
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) return true;
//左右子节点同时入队
queue.add(root.left);
queue.add(root.right);
//如果队列不为空就继续循环
while(!queue.isEmpty()) {
//每两个出队
TreeNode left = queue.poll(), right = queue.poll();
//如果都为空继续循环
if(left == null && right == null) continue;
//如果一个为空一个不为空,说明不是对称的,直接返回false
if(left == null || right == null) return false;
//如果这两个值不相同,也不是对称的,直接返回false
if(left.val != right.val) return false;
//这里要记住入队的顺序,他会每两个两个的出队。
//左子节点的左子节点和右子节点的右子节点同时
//入队,因为他俩会同时比较。
//左子节点的右子节点和右子节点的左子节点同时入队,
//因为他俩会同时比较
queue.add(left.left);
queue.add(right.right);
queue.add(left.right);
queue.add(right.left);
}
return true;
}
}
————————————————————————————————————————
面试题29.顺时针打印矩阵
顺时针打印矩阵的顺序是“从左到右、从上到下、从右到左、从下到上”循环,因此,考虑设定矩阵的“左、右、上、下”四个边界,模拟以上矩阵遍历顺序。
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int l = 0, r = matrix[0].length - 1;
int t = 0, b = matrix.length - 1;
int x = 0;
int[] res = new int[(r + 1) * (b + 1)];
while(true) {
for(int i = l; i <= r; i++) res[x++] = matrix[t][i];
if(++t > b) break; //已经打印了一层上边界,故判断此时的上边界是否超过下边界
for(int i = t; i <= b; i++) res[x++] = matrix[i][r];
if(l > --r) break;
for(int i = r; i >= l; i--) res[x++] = matrix[b][i];
if(t > --b) break;
for(int i = b; i >= t; i--) res[x++] = matrix[i][l];
if(++l > r) break;
}
return res;
}
}
- 时间复杂度 O(MN) : M, N 分别为矩阵行数和列数。
- 空间复杂度 O(1) : 四个边界 l , r , t , b 使用常数大小的额外空间( res 为必须使用的空间)。
————————————————————————————————————————
面试题30.包含min函数的栈
本题需要将 min() 函数复杂度降为 O(1),可通过建立辅助栈实现
-
数据栈A:栈 A 用于存储所有元素,保证入栈 push() 函数、出栈 pop() 函数、获取栈顶 top() 函数的正常逻辑。
-
辅助栈B:栈 B 中存储栈 A 中所有非严格降序的元素,即栈 A 中的最小元素始终对应栈 B 的栈顶元素,即 min() 函数只需返回栈 B 的栈顶元素即可
-
push(x) 函数:重点为保持栈 B 的元素是非严格降序的
- 1.将 x 压入栈 A(即 A.add(x))
- 2.若栈 B为空或 x 小于等于栈 B 的栈顶元素,则将 x 压入栈 B (即 B.add(x))
-
pop() 函数:重点为保持栈 A、B 的元素一致性
- 1.执行栈 A 出栈(即 A.pop()),将出栈元素记为 y
- 2.若 y 等于栈 B 的栈顶元素,则执行栈 B 出栈(即 B.pop())
-
top() 函数:直接返回栈 A 的栈顶元素即可,即返回 A.peek()
-
min() 函数:直接返回栈 B 的栈顶元素即可,即返回 B.peek()
class MinStack {
Stack<Integer> A, B;
public MinStack() {
A = new Stack<>();
B = new Stack<>();
}
public void push(int x) {
A.add(x);
if(B.empty() || B.peek() >= x) {
B.add(x);
}
}
public void pop() {
if(A.pop().equals(B.peek())) {
B.pop();
}
}
public int top() {
return A.peek();
}
public int min() {
return B.peek();
}
}
- 时间复杂度 O(1) : push(), pop(), top(), min() 四个函数的时间复杂度均为常数级别。
- 空间复杂度 O(N) : 当共有 N 个待入栈元素时,辅助栈 B 最差情况下存储 N 个元素,使用 O(N) 额外空间。
————————————————————————————————————————
面试题31.栈的压入、弹出序列
借用一个辅助栈 stack,模拟 压入 / 弹出操作的排列。根据是否模拟成功,即可得到结果。
-
入栈操作:按照压栈序列的顺序执行
-
出栈操作:每次入栈后,循环判断 栈顶元素 == 弹出序列的当前元素 是否成立,将符合弹出顺序的栈顶元素全部弹出。
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack = new Stack<>();
int i = 0;
for(int num : pushed) {
stack.push(num); //num 入栈
while(!stack.isEmpty() && stack.peek() == popped[i]) {
stack.pop();
i++;
}
}
return stack.isEmpty();
}
}
- 时间复杂度 O(N) : 其中 N 为列表 pushed 的长度;每个元素最多入栈与出栈一次,即最多共 2N 次出入栈操作。
- 空间复杂度 O(N) : 辅助栈 stack 最多同时存储 N 个元素。
不用额外空间的做法
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
int phIndex = 0;
int ppIndex = 0;
for(int num : pushed) {
//将遍历的值放入数组,模拟入栈
pushed[phIndex] = num;
//若此时两个值相等,则模拟出栈
while(phIndex >= 0 && pushed[phIndex] == popped[ppIndex]) {
phIndex--;
ppIndex++;
}
phIndex++; //指向数组中为空的位置,相当于栈顶指针
}
//相当于判断栈中是否还有元素,若有,则说明匹配不成功
return phIndex == 0;
}
}