【剑指offer】之【树】

重建二叉树

题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

解法一:

使用递归:

  • 递归出口就是数组中只有一个元素就是叶子节点
  • 递归条件是根据先序遍历找到根元素,然后根据中序遍历找到左右子树。
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length == 0){
            return null;
        }
        //定义根节点
        int rootval = pre[0];
        if(pre.length == 1){
            return new TreeNode(rootval);
        }
        int rootIndex = 0;
        TreeNode root = new TreeNode(rootval);
        for(int i=0;i< in.length;i++){
            if(in[i] == rootval){
                rootIndex = i;
            }
        }
        root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, rootIndex + 1), Arrays.copyOfRange(in, 0, rootIndex));
        root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, rootIndex + 1, pre.length), Arrays.copyOfRange(in, rootIndex + 1, in.length));

        return root;
    }
}

二叉树的下一个结点

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

解法一:

  • 该节点如果有右子树,那么找这个节点的右子树的最左子树
  • 没有右子树的话
    • 如果该节点是其父节点的左节点,那么其父节点就是下一个节点
    • 如果该节点是其父节点的右节点,那么找某个节点是其父节点的左节点,那么该某个节点的父节点就是下一个节点
 public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        TreeLinkNode cur = null;
        if(pNode == null){
            return cur;
        }
        
         cur = pNode;
        if(cur.right != null){
            TreeLinkNode tem = pNode.right;
            while(tem.left != null){
                tem = tem.left;
            }
            return tem;
        }else{
            if(cur.next != null && cur.next.left == cur){
                return cur.next;
            }
            else{
                if(cur.next != null && cur.next.right == cur){
                       while(cur.next != null){
                           if(cur.next.left == cur){
                               return cur.next;
                           }
                           cur = cur.next;
                       }
                }
            }
            return null;
        }
    }

对称的二叉树

题目描述
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

解法一:

  • 就是求其镜像二叉树
  • 肯定是使用递归啦
  • 比较left.right == right.left
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {

        if(pRoot == null){
            return true;
        }
        return help(pRoot.left, pRoot.right);
    }
    public boolean help(TreeNode t1, TreeNode t2){
            if(t1 == null && t2 == null){
                return true;
            }
            if(t1 == null || t2 == null){
                return false;
            }
            return t1.val == t2.val && help(t1.left, t2.right);
        }
}

按之字形顺序打印二叉树

题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

解法一:

  • 使用两个栈解决,一个负责奇数层数,一个负责偶数层数
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList();
        if(pRoot == null){
            return res;
        }
        //奇数
        Stack<TreeNode> s1 = new  Stack();
        //偶数
        Stack<TreeNode> s2 = new Stack();
        boolean flag = true;
        s1.push(pRoot);
        while(!s1.isEmpty() || !s2.isEmpty()){
            if(flag){
                //开始走s1
                ArrayList<Integer> list = new ArrayList();
                while(!s1.isEmpty()){
                    TreeNode cur = s1.pop();
                    list.add(cur.val);    
                    if(cur.left != null){
                        s2.push(cur.left);
                    }
                    if(cur.right != null){
                        s2.push(cur.right);
                    }
                }
                res.add(list);                    
                flag = !flag;
            }else{
                //开始走s2
                ArrayList<Integer> list = new ArrayList();
                
                while(!s2.isEmpty()){
                    TreeNode cur = s2.pop();
                    list.add(cur.val);    
                    if(cur.right != null){
                        s1.push(cur.right);
                    }
                    if(cur.left != null){
                        s1.push(cur.left);
                    }
                }
                res.add(list);                    
                flag = !flag;      
            }
        }  
        return res;
    }

解法二:

  • 用两个list 一个放每层的元素,一个放值。
  • 偶数层用Collections.reverse( new ArrayList<Integer>() );方法反转一下。
  • 在数据量比较大的效率比较慢
public ArrayList<ArrayList<Integer>> Print_01(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList();
        ArrayList<TreeNode> list = new ArrayList();
        if(pRoot == null){
            return res;
        }
        list.add(pRoot);
        boolean flag = true;
        while(!list.isEmpty()){
            ArrayList<TreeNode> nextNode = new ArrayList();
            ArrayList<Integer> cur = new ArrayList();
            for(TreeNode a : list){
                cur.add(a.val);
                if(a.left != null){
                    nextNode.add(a.left);
                }
                if(a.right != null){
                    nextNode.add(a.right);
                }
            }
            if(!flag){
                Collections.reverse(cur);
            }
            res.add(cur);
            flag = !flag;
            list = nextNode;
        }
        return res;
    }

把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

解法一:

  • 二叉树的层次遍历。使用两个list 一个存储值 一个存储下一层的所有的节点,
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList();
        ArrayList<TreeNode> list = new ArrayList();
        if(pRoot == null){
            return res;
        }
        list.add(pRoot);
        while(!list.isEmpty()){
            ArrayList<Integer> cur = new ArrayList();
            ArrayList<TreeNode> next = new ArrayList();
            for(TreeNode n : list){
                cur.add(n.val);
                if(n.left != null){
                    next.add(n.left);
                }
                if(n.right != null){
                    next.add(n.right);
                }
            }
            list = next;
            res.add(cur);
        }
        return res;
    }
}

序列化二叉树

题目描述
请实现两个函数,分别用来序列化和反序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

解法一:

  • 都用的是递归操作
  • 用一个队列 保存着所有的String 值
String Serialize(TreeNode root) {
        if(root == null){
            return "#!";
        }
        String s = root.val + "!";
        s += Serialize(root.left);
        s += Serialize(root.right);
        return s;
        
    }
    TreeNode Deserialize(String str) {
        String[] aa = str.split("!");
        Queue<String> queue = new LinkedList();
        for(int i = 0; i < aa.length; i++){
            queue.add(aa[i]);
        }
        return generate(queue);
  }
    public TreeNode generate(Queue<String> queue){

        String a = queue.poll();    
        if(a.equals("#")){
            return null;
        }

        TreeNode cur = new TreeNode(Integer.valueOf(a));
        cur.left = generate(queue);
        cur.right = generate(queue);
        return cur;

        
    }

二叉搜索树的第k个结点

题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

解法一:

  • 其实考的就是中序遍历 返回中序遍历的第k个的值就好了。
public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k)
    {
       if(pRoot == null || k <= 0){
           return null;
       } 
       Stack<TreeNode> stack = new Stack();
       ArrayList<TreeNode> res = new ArrayList();
        TreeNode cur = pRoot;
        while(!stack.isEmpty() || cur != null){
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }else{
                cur = stack.pop();
                res.add(cur);
                cur = cur.right;
            }            
       }
        if(k > res.size()){
            return null;
        }
        TreeNode tem = res.get(k - 1);
        return tem;
    }


}

数据流中的中位数

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

解法一:

  • 选择两个优先级队列 一个大顶堆一个小顶堆,
  • 数量为偶数的时候放入大顶堆中,然后把堆顶元素弹出给小顶堆,
  • 数量为奇数的时候放入小顶堆中,然后把堆顶元素弹出给大顶堆
  • 这样保证大顶堆的元素最大的都比小顶堆的元素最小的小,也就是小的放入大顶堆中,大的当如小顶堆中。
public class Solution {

    //小顶堆
    private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
     
    //大顶堆
    private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    int count = 0;
    public void Insert(Integer num) {
        if((count & 1) == 0){
            maxHeap.offer(num);
            int cur = maxHeap.poll();
            minHeap.offer(cur);
        }else{
            minHeap.offer(num);
            int cur = minHeap.poll();
            maxHeap.offer(cur);
        }
        count++;
    }

    public Double GetMedian() {
        if((count & 1) == 0){
            return new Double(minHeap.peek()+maxHeap.peek())/2;
        }else{
            return new Double(minHeap.peek());
        }
    }


}
发布了118 篇原创文章 · 获赞 5 · 访问量 8731

猜你喜欢

转载自blog.csdn.net/weixin_43672855/article/details/105228891