树常见的算法操作

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

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

本文包括:

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;
    }
时间: 2024-10-10 01:18:55

树常见的算法操作的相关文章

第六章 常见排序算法

上章回顾 二叉树的定义 树深度的定义 什么样的二叉树是满二叉树 中序遍历的规则 [email protected]:Kevin-Dfg/[email protected]:Kevin-Dfg/Data-Structures-and-Algorithm-Analysis-in-C.git 第六章 第六章 常见排序算法 常见排序算法 [email protected]:Kevin-Dfg/[email protected]:Kevin-Dfg/Data-Structures-and-Algorith

常见排序算法总结(java实现)

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.常见的排序算法有选择排序,插入排序,希尔排序,归并排序和快速排序 由于在排序的过程中不可避免的要涉及到比较和交换,所以将他们抽取为两个单独的函数,如下所示 //为了排序代码的通用性,这里假定待排序的元素实现了Comparable接口 private static boolean less(Comparable v ,Comparable w){ return v.compareTo(w)<0; } priva

十种常见排序算法

1.常见算法分类 十种常见排序算法一般分为以下几种: (1)非线性时间比较类排序:交换类排序(快速排序和冒泡排序).插入类排序(简单插入排序和希尔排序).选择类排序(简单选择排序和堆排序).归并排序(二路归并排序和多路归并排序): (2)线性时间非比较类排序:计数排序.基数排序和桶排序. 总结: (1)在比较类排序中,归并排序号称最快,其次是快速排序和堆排序,两者不相伯仲,但是有一点需要注意,数据初始排序状态对堆排序不会产生太大的影响,而快速排序却恰恰相反. (2)线性时间非比较类排序一般要优于

数据结构与算法 介绍以及常见的算法排序

介绍 数据结构: 计算机存储,组织数据的结构.指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成. 线性数据结构:数组,链表 应用:队列 栈 非线性数据结构:树,图 算法: 对数据结构中数据的操作 常见的算法排序 low B三件套 #冒泡排序 #时间复杂度:O(n2) #空间复杂度:O(1) def bubble_sort(li): for i in range(len(li)-1): flag = False for j in range(len(li)-i-1):

常见排序算法(一) MergeSort

算法思想灰常重要,常见的用到分治思想的算法包括快速排序,归并,二分搜搜,大整数乘法等(参考 http://blog.csdn.net/com_stu_zhang/article/details/7233761,归纳很到位) 简单用归并对一个数组排序 思路: 简单来说对一个数组,只要他的左右两部分都是有序的,那么简单合并就ok了,那么左右两部分可以进一步划分各自的左右两部分----明显就是要递归了 算法:归并排序 1. 将数组一分为二,subArray1 和subArray2 2. 归并排序sub

二叉树常见遍历算法

这几天在复习关于树的各种算法,做了一些题,也搜索了网上各种算法,现在来总结一下树的各种常见算法.本文涵盖: 二叉树先中后序遍历(递归&非递归)算法 层次遍历(正序&逆序&锯齿形)非递归算法 二叉树深度算法 结点总数算法 1.二叉树先序非递归遍历 //先序非递归遍历 public ArrayList<Integer> preorderTraversal2(TreeNode root) { Stack<TreeNode> stack = new Stack<

uva 12299 线段树 点相关的操作模板

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=502&page=show_problem&problem=3720 唯一值得一说的就是shift,变成更新点就行 这道题主要是测试下我做的算法模板 先贴模板 /**************************************************************** 2014.4 By Pilgr

几种常见排序算法

几种常见排序算法 几种常见排序算法 写在前面 基础介绍 初级排序算法 selection sort选择排序 insertion sort插入排序 ShellSort希尔排序 shuffing不是排序算法 merge sort归并排序 Abstract in-place merge原地归并的抽象方法 Top-down mergesort自顶向下的归并排序 Bottom-up mergesort自底向上的归并排序 quicksort 三向切分的快速排序 Heapsort堆排序 总结和比较 命题 本文

深入学习高级非线性回归算法 --- 树回归系列算法

前言 前文讨论的回归算法都是全局且针对线性问题的回归,即使是其中的局部加权线性回归法,也有其弊端(具体请参考前文:) 采用全局模型会导致模型非常的臃肿,因为需要计算所有的样本点,而且现实生活中很多样本都有大量的特征信息. 另一方面,实际生活中更多的问题都是非线性问题. 针对这些问题,有了树回归系列算法. 回归树 在先前决策树 (链接) 的学习中,构建树是采用的 ID3 算法.在回归领域,该算法就有个问题,就是派生子树是按照所有可能值来进行派生. 因此 ID3 算法无法处理连续性数据. 故可使用二