emm
遇到的真实面试题,可惜太菜了,希望下次别再犯错吧
面试题1:栈怎么实现O(1)空间复杂度查询栈中最大值,可以修改栈的存储方式,push,pop操作,但是要保证O(1)时间复杂度,空间复杂度
参考:https://blog.csdn.net/taotaotheripper/article/details/8652665
思路:
- 想到使用辅助栈,用来存储当前栈的最大值,每次查询栈的最大值时,将对应栈的最大值一起弹出既可;
算法描述:
- 建立2个栈,1个当前存储栈,1个辅助最大值栈Sm
- 当push入栈的元素大于当前最大元素时,将该元素压入Sm;
- 当push入栈的元素小于Sm栈顶中最大元素时,将Sm栈顶元素再压入一次;
- 当当前最大元素被弹出时,辅助栈Sm栈顶的元素也对应弹出;
- 查询栈的最大值,也就是Sm栈顶的元素
进一步追问:如何使用常量空间O(1),常量时间O(1)完成这一操作:
思路:使用一个标记量Max记录当前最大值,每次num入栈时,入栈值为(num-MAX),出栈时+max进行相应变换;

前序遍历迭代实现
迭代算法中,每遇到一个结点A,就应该立即访问它,
每颗子树先访问先访问其根节点,对节点左右子树来说,一定是先访问根
在A的两棵子树中,遍历完左子树后,再遍历右子树
在访问完根结点后,需要将右子树入栈;
//模板
栈S;
p = root;
while(p != null || !S.isEmpty()){
while(p != null){
访问p结点;
p的右子树入S;
p = p的左子树;
}
p = S栈顶弹出
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root.right); //先将右子树入栈
res.add(root.val); //访问该节点
root = root.left;
}
root = stack.pop();
}
return res;
}
}
后序遍历迭代实现
后序遍历:左右根,与前序遍历类似
每遇到一个结点 ,立即访问它,然后将左子树入栈,访问右子树—>这样得到的顺序为根右左,倒序后为左根右;
//目标
Stack s;
p = root;
while(p != null || !stack.isEmpty()){
while(p != null){
stack.push(p.left);
访问p结点;
p = p.right;
}
p = s.pop();
}
将结果逆序输出
//Coding
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode prev = null;
while(root != null || !stack.isEmpty()){
while(root != null)
{
stack.push(root);
root = root.left;
}
root = stack.pop();
if(root.right == null || root.right == prev){
res.add(root.val);
prev = root;
root = null;
}else{
stack.push(root);
root = root.right;
}
}
return res;
}
}
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Deque<TreeNode> stack = new ArrayDeque<>();
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root.left);
res.push(root.val); //根右左
root = root.right;
}
root = stack.pop();
}
Collections.reverse(res); //反转为左右根
return res;
}
}
中序遍历迭代实现
思路:每到一个节点 A,因为根的访问在中间,将 A 入栈。然后遍历左子树,接着访问 A,最后遍历右子树。
在访问完 A 后,A 就可以出栈了。因为 A 和其左子树都已经访问完成。
//伪代码
栈S;
p = root;
while(p != null || S != 空){
while(p != null){
p入S;
p = p的左子树;
}
p = S.pop();
访问p;
p = p的右子树;
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
Deque<TreeNode> stack = new ArrayDeque<>();
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val); //先访问左
root = root.right;
}
return res;
}
}
寻找二叉树上从根结点到给定结点的路径
递归方式:
思路:借助栈结构来保存路径上的节点,首先从根结点开始,一直往左找,如果左边找到就返回true;否则,如果左边找不到并且右子树不为空的情况下再继续往右子树找,如果左右子树都找不到,则返回false,方法运行完,栈中保存的就是从根到给定结点的路径
public static boolean searchNode(TreeNode root , Stack<TreeNode> s , TreeNode node)
{
if(root == null)
return false;
s.push(root);
if(root.val == node.val)
return true;
boolean b = false;
//先找左子树
if(root.left != null)
b = searchNode(root.left , s , node);
//左子树没找到,再去右子树找
if(!b && root.right != null)
b = searchNode(root.right, s, node);
//左右都找不到
if(!b)
s.pop();
return b;
}
非递归实现:
public static boolean searchNode(TreeNode root, TreeNode node)
{
if(root == null || node == null)
return;
Stack<TreeNode> s = new Stack<>();
TreeNode p = root;
TreeNode pre = null; //上次出栈的节点
while(p != null || !s.isEmpty())
{
while(p != null)
{
s.push(p);
if(p.val == node.val){
//如果找到了,则打印输出并返回
for(TreeNode t : s){
System.out.print(treeNode.val + " ");
}
return;
}
p = p.left;
}
//左子树为null,开始往栈顶的右子树上找
if(!s.isEmpty())
{
p = s.peek();
//如果栈顶元素的左子树为null,那么久开始向栈顶元素的右子树上去找
while(p.right == null || pre != null && p.right == pre)
{
pre = s.pop();
p = s.peek();
}
//继续遍历p的右子树
p = p.right;
}
}
}
力扣部分题
144.二叉树前序遍历
给你二叉树的根节点
root,返回它节点值的 前序 遍历。
迭代实现:使用栈,
前序遍历:根左右,
入栈方式应该为:右左根
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while(!stack.isEmpty())
{
TreeNode node = stack.pop();
res.add(node.val);
//注意栈先进后出
if(node.right != null)
stack.push(node.right);
if(node.left != null)
stack.push(node.left);
}
return res;
}
}
递归写法
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
dfs(root , res);
return res;
}
private void dfs(TreeNode root, List<Integer> res)
{
if(root == null)
return;
res.add(root.val);
dfs(root.left , res);
dfs(root.right , res);
}
}
145.二叉树后续遍历
给定一个二叉树,返回它的 后序 遍历。
考虑它的迭代实现:
前序遍历:根-左-右
后序遍历:左右根;
修改前序遍历为:根-右-左,则其倒序就为后序遍历
Collections.reverse(List<?> list): 翻转列表元素
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<Integer>();
if(root == null)
return ret;
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node == null)
continue;
ret.add(node.val);
if(node.left != null)
stack.push(node.left);
if(node.right != null)
stack.push(node.right);
}
Collections.reverse(ret);
return ret;
}
}
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
dfs(root , res);
return res;
}
private void dfs(TreeNode root, List<Integer> res)
{
if(root == null)
return;
dfs(root.left , res);
dfs(root.right , res);
res.add(root.val);
}
}
94.二叉树中序遍历
给定一个二叉树的根节点
root,返回它的 中序 遍历。
迭代实现:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
Deque<TreeNode> stack = new ArrayDeque<>();
while(root != null || !stack.isEmpty())
{
while(root != null)
{
stack.push(root);
root = root.left;
}
TreeNode node = stack.pop();
res.add(node.val);
root = node.right;
}
return res;
}
}
递归实现
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
dfs(root , res);
return res;
}
private void dfs(TreeNode root , List<Integer> res)
{
if(root == null)
return;
dfs(root.left , res);
res.add(root.val);
dfs(root.right , res);
}
}
本文探讨了如何在二叉树遍历中实现不同顺序(前序、中序、后序)的迭代方法,并介绍了如何在栈中以O(1)空间复杂度查询栈中最大值。此外,还讲解了如何使用常量空间和时间复杂度修改栈以查询最大值。同时,提供了寻找二叉树上从根节点到给定节点路径的递归和非递归实现。

466

被折叠的 条评论
为什么被折叠?



