剑指offer二

72次阅读

共计 3512 个字符,预计需要花费 9 分钟才能阅读完成。

今天来学习以下几道《剑指 offer》里有关数据结构的算法题

  • 从尾到头打印链表
  • 用两个栈实现队列
  • 重建二叉树
  • 二叉树的下一个节点

从尾到头打印链表

输入个链表的头结点,从尾到头反过来打印出每个结点的值。

很容易可以想到使用 或者 递归 来实现

代码实现

public class Test {
    
    public static class ListNode{
        int val;
        ListNode next;
    }
 //—--------- 使用栈 ----------
  public static void printListReverseUsStack(ListNode root){Stack<ListNode> stack=new Stack<>();
        while(root!=null){stack.push(root);
            root=root.next;            
        }
        ListNode temp;
        while(!stack.isEmpty()){temp=stack.pop();
            System.out.print(temp.val+" ");
        }
    }
 //—--------- 使用递归 -------------
  public static void printListReverseUsRecursion(ListNode root){if (root!=null) {if (root.next!=null) {printListReverseUsDiGui(root.next);
            }            
        }
        System.out.print(root.val+" ");        
    }
}

用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能。

解题思路

(1)插入:将元素压入到 stack1 中。
(2)删除:当 stack2 不为空时,弹出 stack2 的栈顶元素。当 stack2 为空时,将 stack1 的元素逐个弹出并压入到 stack2 中,然后弹出栈顶元素。

代码实现

public class CQueue<T>{private Stack<T> stack1=new Stack<>();
    private Stack<T> stack2=new Stack<>();

    // 尾部添加
    public void appendTail(T t){stack1.push(t);
    }
    // 删除头部
    public T deleteHead(){if (stack2.isEmpty()){while (!stack1.isEmpty()){stack2.push(stack1.pop());
            }
        }
        if (stack2.isEmpty()) throw new RuntimeException("No Data!");

        return stack2.pop();}
}

重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

解题思路

(1)前序遍历的第一个数字就是根结点的值。
(2)扫描中序遍历中根结点的位置,根结点左边的值为左子树结点的值,右边的为右子树结点的值。
(3)既然我们已经分别找到了左、右子树的前序遍历和中序遍历,接下来可以递归地构建左、右子树。

代码实现

public class BinaryTree {
   
   public static class BinaryTreeNode {
        int value;
        BinaryTreeNode left;
        BinaryTreeNode right;
    }

     //preorder 和 inorder 分别为前序遍历和后序遍历
      public static BinaryTreeNode construct(int[] preorder, int[] inorder) {
        // 输入的合法性判断,两个数组都不能为空,并且都有数据,而且数据的数目相同
        if (preorder == null || inorder == null || preorder.length != inorder.length || inorder.length < 1) {return null;}

        return construct(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }

    /**
     * @param preorder 前序遍历
     * @param ps       前序遍历的开始位置
     * @param pe       前序遍历的结束位置
     * @param inorder  中序遍历
     * @param is       中序遍历的开始位置
     * @param ie       中序遍历的结束位置
     * @return 树的根结点
     */
    public static BinaryTreeNode construct(int[] preorder, int ps, int pe, int[] inorder, int is, int ie) {

        // 开始位置大于结束位置说明已经没有需要处理的元素了
        if (ps > pe) {return null;}
        // 取前序遍历的第一个数字,就是当前的根结点
        int value = preorder;
        int index = is;
        // 在中序遍历的数组中找根结点的位置
        while (index <= ie && inorder[index] != value) {index++;}

        // 如果在整个中序遍历的数组中没有找到,说明输入的参数是不合法的,抛出异常
        if (index > ie) {throw new RuntimeException("Invalid input");
        }

        // 创建当前的根结点,并且为结点赋值
        BinaryTreeNode node = new BinaryTreeNode();
        node.value = value;

        // 递归构建当前根结点的左子树,左子树的元素个数:index-is 个
        // 左子树对应的前序遍历的位置在
        // 左子树对应的中序遍历的位置在[is, index-1]
        node.left = construct(preorder, ps + 1, ps + index - is, inorder, is, index - 1);
        // 递归构建当前根结点的右子树,右子树的元素个数:ie-index 个
        // 右子树对应的前序遍历的位置在
        // 右子树对应的中序遍历的位置在[index+1, ie]
        node.right = construct(preorder, ps + index - is + 1, pe, inorder, index + 1, ie);

        return node;
    }
}

二叉树的下一个节点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

解题思路

(1)如果一个结点有右子树,那么它的下一个结点就是它的右子树的最左子结点。例如,图中结点 b 的下一个结点是 h。
(2)接着我们分析一下结点没有右子树的情形。如果结点是它父结点的左子结点,那么它的下一个结点就是它的父结点。例如,结点 d 的下一个结点是 b。
(3)如果一个结点既没有右子树,并且它还是父结点的右子结点,这种情形就比较复杂。我们可以沿着指向父结点的指针一直向上遍历,直到找到一个是它父结点的左子结点的结点。如果这样的结点存在,那么这个结点的父结点就是我们要找的下一个结点。例如结点 i 的下一个结点是 a。

代码实现

public class Solution {
   
    public class BinaryTreeNode {
    int val;
    TreeLinkNode left;
    TreeLinkNode right;
    TreeLinkNode parent;
}

    public TreeLinkNode getNext(BinaryTreeNode pNode){
        
        BinaryTreeNode curNode;
        // 第一步:判断是否有右孩子
        if(pNode.right != null){
            curNode = pNode.right;
            while(curNode.left != null) curNode = curNode.left;
            return curNode;
        }
        // 第二步:判断是否是其父节点的左孩子
        if(pNode.parent == null) return null;
        if(pNode == pNode.parent.left){return pNode.parent;}
        // 第三步:向上找其父节点,直到父节点是其父节点的的左子节点
        curNode = pNode.parent;
        while(curNode.parent != null){if(curNode == curNode.parent.left){return curNode.parent;}
            // 继续向上找父节点
            curNode = curNode.parent;
        }
        return null;
    }
}

正文完
 0