• 树常见的算法操作


    树是数据结构中很重要的一部分,也是各大公司面试常考部分。

    继树的各种遍历算法之后,今天又整理一下树的常见算法操作。

    本文包括:

    1.求节点的最近公共祖先

    2.树的序列化与反序列化

    3.已知先序遍历和中序遍历构造二叉树

    4.已知中序遍历和后序遍历构造二叉树

    1.求节点最近的公共祖先

    此题不同的要求有不同的解法

    如果已知树中的每一个结点有指向父节点的指针:

    思路:从给定节点遍历到根节点,当父节点相等时返回。

    解法1

    private ArrayList<TreeNode> getPath2Root(TreeNode node) {
            ArrayList<TreeNode> list = new ArrayList<TreeNode>();
            while (node != null) {
                list.add(node);
                node = node.parent;
            }
            return list;
        }
        public TreeNode lowestCommonAncestor(TreeNode node1, TreeNode node2) {
            ArrayList<TreeNode> list1 = getPath2Root(node1);
            ArrayList<TreeNode> list2 = getPath2Root(node2);
            
            int i, j;
            for (i = list1.size() - 1, j = list2.size() - 1; i >= 0 && j >= 0; i--, j--) {
                if (list1.get(i) != list2.get(j)) {
                    return list1.get(i).parent;
                }
            }
            return list1.get(i+1);
        }

    解法2:

    private TreeNode getRoot(node) {
            while (node.parent != null) {
                node = node.parent;
            }
            return node;
        }
        
        private TreeNode getAncestor(TreeNode root, TreeNode node1, TreeNode node2) {
            if (root == null || root == node1 || root == node2) {
                return root;
            }
            
            TreeNode left = getAncestor(root.left, node1, node2);
            TreeNode right = getAncestor(root.right, node1, node2);
    
            if (left != null && right != null) {
                return root;
            } 
            if (left != null) {
                return left;
            }
            if (right != null) {
                return right;
            }
            return null;
        }
        
        public TreeNode lowestCommonAncestor(TreeNode node1, TreeNode node2) {
            if (node1 == null || node2 == null) {
                return null;
            }
            TreeNode root = getRoot(node1);
            return getAncestor(root, node1, node2);
        }

    如果树中的节点不带有指向父节点的指针:

    思路利用先序遍历和中序遍历的特点,利用先序遍历分辨根节点,根据根节点和给定节点AB的位置关系来确定公共祖先,根据中序遍历结果,两个节点中间的节点即为所求祖先,若中间没有节点,则中序遍历中下标较小的即为祖先。

     TreeNode node =null;
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) {
            // write your code here
            
            TreeNode result =null;
            ArrayList<TreeNode> inorder = new ArrayList<TreeNode>(); 
            ArrayList<TreeNode> preorder = new ArrayList<TreeNode>();
            inorderto(root,inorder);
            preorderto(root,preorder);
            //得到AB节点在中序遍历中的下标
            int aValue = inorder.indexOf(A);
            int bValue = inorder.indexOf(B);
            int lastindex = 0 ;
            for(int i=0;i<preorder.size();){
                int rootValue = inorder.indexOf(preorder.get(i));
                if((aValue<=rootValue && bValue>=rootValue)||(aValue>=rootValue && bValue<=rootValue)){
                    result = preorder.get(i);
                    break;
                }
           
                else 
                    i++;
            }
            return result;
        }
        public void inorderto(TreeNode root,ArrayList<TreeNode> list){
            if(root==null)
                return;
            else{
                inorderto(root.left,list);
                list.add(root);
                inorderto(root.right,list);
            }
        }
        public void preorderto(TreeNode root,ArrayList<TreeNode> list){
            if(root==null)
                return;
            else{
                list.add(root);
                preorderto(root.left,list);
                preorderto(root.right,list);
            }    
        }

    2.树的序列化与反序列化

    将树的节点存入文件,再从文件读出构造出树称为树的序列化与反序列化。

    思路:利用先序遍历(其他遍历亦可),将树的节点值存入字符串,若节点为空则存入#,每个节点之间用','隔开用来区分数字。

    读出时亦用先序遍历的方法。

     public String serialize(TreeNode root) {
            // write your code here
            String str = "";
            Stack<TreeNode> stack = new Stack<TreeNode>();
            if(root!=null)
                stack.push(root);
            while(!stack.isEmpty()){
                TreeNode node = stack.pop();
                if(node!=null){
                    str += node.val + ",";
                    stack.push(node.right);
                    stack.push(node.left);
                }
                else
                    str += "#,";
            }
            return str;
        }
        int index =0 ;
        public TreeNode deserialize(String data) {
           if(data=="")
                return null;
            String val = outValue(index,data);
            index += 1;
            if(val.equals("#"))
                return null;
            else{
                TreeNode node = new TreeNode(Integer.parseInt(val));
                
                node.left = deserialize(data);
                node.right = deserialize(data);
                return node;
            }
        }
        
        public String outValue(int index,String str){
            String res = "";
            for(int i=0,j=0;i<str.length();i++){
                if(str.charAt(i)==',')
                    j++;
                else if(j==index)
                    res += str.charAt(i);
                if(j>index)
                    break;
            }
            return res;
        }

    3.已知先序遍历和中序遍历构造二叉树

    思路:利用先序遍历区分根节点,利用中序遍历区分左右子树

    private int findPosition(int[] arr, int start, int end, int key) {
            int i;
            for (i = start; i <= end; i++) {
                if (arr[i] == key) {
                    return i;
                }
            }
            return -1;
        }
    
        private TreeNode myBuildTree(int[] inorder, int instart, int inend,
                int[] preorder, int prestart, int preend) {
            if (instart > inend) {
                return null;
            }
    
            TreeNode root = new TreeNode(preorder[prestart]);
            int position = findPosition(inorder, instart, inend, preorder[prestart]);
    
            root.left = myBuildTree(inorder, instart, position - 1,
                    preorder, prestart + 1, prestart + position - instart);
            root.right = myBuildTree(inorder, position + 1, inend,
                    preorder, position - inend + preend + 1, preend);
            return root;
        }
    
        public TreeNode buildTree(int[] preorder, int[] inorder) {
            if (inorder.length != preorder.length) {
                return null;
            }
            return myBuildTree(inorder, 0, inorder.length - 1, preorder, 0, preorder.length - 1);
        }

    4.已知中序遍历和后序遍历构造二叉树

    思路:和前者思路差不多,利用后序遍历来寻找根节点,利用中序遍历分辨左右子树

    public TreeNode buildTree(int[] inorder, int[] postorder) {
            // write your code here
            if(inorder.length != postorder.length)
                return null;
            return myTree(inorder,0,inorder.length-1,postorder,0,postorder.length-1);
        }
        public TreeNode myTree(int[]inorder,int instart,int inend,
                int[] postorder,int poststart,int postend){
            if(instart>inend)
                return null;
            TreeNode root = new TreeNode(postorder[postend]);
            int position = findPosition(inorder,instart,inend,postorder[postend]);
            root.left = myTree(inorder,instart,position-1,postorder,poststart,poststart+position-1-instart);
            root.right = myTree(inorder,position+1,inend,postorder,position - inend + postend ,postend-1);
            return root;
        }
        private int findPosition(int[] arr, int start, int end, int key) {
            int i;
            for (i = start; i <= end; i++) {
                if (arr[i] == key) {
                    return i;
                }
            }
            return -1;
        }
  • 相关阅读:
    Windows Phone学习笔记(6) — — 套接字概述
    Windows Phone学习笔记(4) — — 本地数据库操作
    Windows Phone学习笔记(7) — — TCP套接字
    Windows Phone学习笔记(8) — — UDP套接字
    Windows Phone学习笔记(10) — — 设置页面
    模拟退火摘要
    记 我的第一篇博客
    Splay算法摘要
    调格式专区
    路由器 设置DMZ主机 端口转发 实现外网访问
  • 原文地址:https://www.cnblogs.com/yfsmooth/p/4676668.html
Copyright © 2020-2023  润新知