面试题28、29、30、31

面试题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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值