剑指offer_面试题6:从尾到头打印链表,面试题7:重建二叉树,面试题8:二叉树的下一个节点,面试题9:用两个栈实现队列

本文深入解析了链表与二叉树相关的经典面试题目,包括从尾到头打印链表、重建二叉树、二叉树的下一个节点等,详细介绍了算法思路与实现代码。
面试题6:从尾到头打印链表

在这里插入图片描述

  • 要求返回的结果是动态数组,而且元素是链表中结点值从尾到头的顺序。
  • 巧借ArrayList.add(0, element)方法在顺序遍历链表时将结点值逆序存入数组中
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> result = new ArrayList<>();
    while (listNode != null) {
        // 采用头插法,使得最后为逆序
        result.add(0, listNode.val);
        // listNode指向下一个结点 
        listNode = listNode.next; 
    }
    return result;
}
面试题7:重建二叉树

在这里插入图片描述
在这里插入图片描述

  • 二叉树的前序遍历,第一个节点为根节点。找到中序遍历中对应的根节点,可以将中序数组分为左右子树。
    在这里插入图片描述
  • 根据中序遍历中得到左右子树,可以得到前序遍历中的左右子树。
    在这里插入图片描述
  • 先构建根节点,然后分别根据左子树的中序、前序数组和右子树的中序、前序数组,递归构建左右子树。
  • 特殊情况: 如果前序序列或中序序列长度为0,直接返回null
  • 代码如下,其中获得左右子树的数组使用System.arraycopy()方法去完成。System.arraycopy()native方法。
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
    if (pre.length == 0 || in.length == 0) {
        return null;
    }
    int index = 0; // 标记中序中的根节点位置
    for (; index < in.length; index++) {
        if (in[index] == pre[0]) {
            break;
        }
    }
    TreeNode root = new TreeNode(pre[0]);
    // 获得左子树的前序、中序数组
    int[] leftPre = new int[index];
    System.arraycopy(pre, 1, leftPre, 0, index);
    int[] leftIn = new int[index];
    System.arraycopy(in, 0, leftIn, 0, index);
    // 根据左子树的前序、中序数组,递归构造左子树
    root.left = reConstructBinaryTree(leftPre, leftIn);
    
    int rightLen = pre.length - index - 1;
    int[] rightPre = new int[rightLen];
    System.arraycopy(pre, index + 1, rightPre, 0, rightLen);
    int[] rightIn = new int[rightLen];
    System.arraycopy(in, index + 1, rightIn, 0, rightLen);
    root.right = reConstructBinaryTree(rightPre, rightIn);
    return root;
}
leetcode:根据中序和后续序列构建二叉树
  • 后序序列中,最后一个值为根节点。根据后序序列中的根节点,可以将中序序列分为左右子树。
  • 根据中序序列中,左右子树的长度,可以将后续序列从左到右分为左右子树。
  • 获得了左右子树的中序、后序序列,便可以递归构建左右子树。
  • 代码如下:
public TreeNode buildTree(int[] inorder, int[] postorder) {
    if (inorder.length == 0 || postorder.length == 0) {
        return null;
    }
    int index = 0;// 标记中序序列中根节点的位置
    int len = inorder.length;
    for (; index < len; index++) {
        if (inorder[index] == postorder[len - 1]) {
            break;
        }
    }
    TreeNode root = new TreeNode(postorder[len - 1]);
    // 获得左子树的中序、后序序列
    int[] leftIn = new int[index];
    System.arraycopy(inorder, 0, leftIn, 0, index);
    int[] leftPost = new int[index];
    System.arraycopy(postorder, 0, leftPost, 0, index);
    root.left = buildTree(leftIn, leftPost);

    int rightLen = len - index - 1;
    int[] rightIn = new int[rightLen];
    System.arraycopy(inorder, index + 1, rightIn, 0, rightLen);
    int[] rightPost = new int[rightLen];
    System.arraycopy(postorder, index, rightPost, 0, rightLen);
    root.right=buildTree(rightIn,rightPost);
    return root;
}
面试题8:二叉树的下一个节点

在这里插入图片描述
在这里插入图片描述

  • 有三种情况:
  1. 目标节点有右子树,则下一个将要访问的节点是右子树中最左子节点。如节点b,下一次将要访问的是其右子树中的节点h
  2. 如果目标节点是其父节点的左孩子且自身没有右子树,则它的父节点就是下一个将要访问的节点。如节点d,下一个将要访问的是节点b
  3. 如果目标节点是其父节点的右孩子且没有右子树,则需要向上查找其父节点。如果该父节点是爷爷的左孩子,则下一次将要访问的就是爷爷节点。如节点i,下一次将要访问的是节点a
  4. 其他情况都返回null,如根节点没有右子树,则下一个将要访问的节点为null
  • 其中第2和第3种情况可以合并为一种情况:当目标节点没有右子树时,向上查找其父节点并不断更新指向目标节点的引用,直到当前节点是其父节点的左孩子
  • 代码如下:
public TreeLinkNode GetNext(TreeLinkNode pNode) {
    // 右子树不为空,查找右子树的最左节点
    if (pNode.right != null) {
        TreeLinkNode cur = pNode.right;
        while (cur.left != null) {
            cur = cur.left;
        }
        return cur;
    }else {
    // 右子树为空,如果存在下一个访问的节点,则该节点一定是自己父节点的左孩子
        while (pNode.next!=null){
            TreeLinkNode parent=pNode.next;
            if (parent.left==pNode){
                return parent;
            }
            pNode=parent;
        }
    }
    return null;
}
面试题9:用两个栈实现队列

在这里插入图片描述

  1. 删除元素,总是从stack2中删除。 如果stack2不为空,直接执行pop()操作;如果stack2为空,需要先将stack1中的元素压入stack1中,在对stack2执行pop()操作。
  2. 添加元素,总是向stack1中添加。
    在这里插入图片描述
  • 代码如下:
import java.util.Stack;

public class MyQueue {
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;

    /**
     * Initialize your data structure here.
     */
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

    /**
     * Push element x to the back of queue.
     */
    public void push(int x) {
        stack1.push(x);
    }

    /**
     * Removes the element from in front of queue and returns that element.
     */
    public int pop() {
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

    /**
     * Get the front element.
     */
    public int peek() {
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        return stack2.peek();
    }

    /**
     * Returns whether the queue is empty.
     */
    public boolean empty() {
        if (stack2.isEmpty() && stack1.isEmpty()) {
            return true;
        }
        return false;
    }
}
相似题:leetcode:Implement Stack using Queues
  • 使用两个队列模拟栈:
  1. 针对push操作: 如果两个队列都为空,直接添加到queue1中;如果queue1非空,将元素添加到queue1的末尾;否则,添加到queue2的末尾。
  2. 针对pop操作: 如果queue1非空,删除queue1的末尾元素,并将queue1中的元素移动到queue2中;如果queue2非空,删除queue2中末尾的元素,将queue2中的元素移动到queue1中。
  3. 针对top操作: 如果queue1非空,则获取queue1的末尾元素;如果queue2非空,则获取queue2的末尾元素。
  4. 针对isEmpty操作: 直接判断queue1和queue2是否均为空。
    在这里插入图片描述
  • 代码如下:
import java.util.LinkedList;

public class MyStack {
    private LinkedList<Integer> queue1;
    private LinkedList<Integer> queue2;

    /**
     * Initialize your data structure here.
     */
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    /**
     * Push element x onto stack.
     */
    public void push(int x) {
        if (queue1.isEmpty() && queue2.isEmpty()) {
            queue1.add(x);
        } else if (!queue1.isEmpty()) {
            queue1.addLast(x);
        } else {
            queue2.addLast(x);
        }
    }

    /**
     * Removes the element on top of the stack and returns that element.
     */
    public int pop() {
        int val = 0;
        if (!queue1.isEmpty()) {
            val = queue1.removeLast();
            while (!queue1.isEmpty()) {
                queue2.addLast(queue1.removeFirst());
            }
        } else {
            val = queue2.removeLast();
            while (!queue2.isEmpty()) {
                queue1.addLast(queue2.removeFirst());
            }
        }
        return val;
    }

    /**
     * Get the top element.
     */
    public int top() {
        if (!queue1.isEmpty()) {
            return queue1.getLast();
        } else {
            return queue2.getLast();
        }
    }

    /**
     * Returns whether the stack is empty.
     */
    public boolean empty() {
        if (queue2.isEmpty() && queue1.isEmpty()) {
            return true;
        }
        return false;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值