二叉树基础及应用

二叉树基础:

刚看到堆排序,顺便记录一下关于树的一些基本概念:

前言

前面介绍的栈、队列都是线性结构(linear structure)。而树是非线性结构(non-linear structure)。因此,树中的元素之间一般不存在类似于线性结构的一对一的关系,更多地表现为多对多的关系。直观地看,它是数据元素(在树中称为节点)按分支关系组织起来的结构。显然,树形结构是比线性结构更复杂的一种数据结构类型。

一、树

树的定义:树是含有n个节点的有穷集合,其中有一个节点比较特殊称为根节点。在图示树时,用一条边连接两个有逻辑关系的节点,这个关系被称为父子关系。

二、二叉树

二叉树(Binary Tree)由节点的有限集合构成。这个集合或者为空集,或者为由一个根节点(root)和两棵互不相交,分别称为这个根的左子树(left subtree)和右子树(right subtree)的二叉树组成的集合。

一棵二叉树的示意图:

三、树和二叉树的主要区别

  1. 树中节点的最大度数没有限制,而二叉树节点的度不超过2。
  2. 树中节点的孩子节点,无左右之分,而二叉树中是有区分的,即孩子是有区别的:左孩子、右孩子,且次序不可颠倒。
  3. 树的结点个数至少为1,而二叉树的结点个数可以为0。

四、常见概念

  1. 节点的度:某节点的度定义为该节点孩子节点的个数。
  2. 叶子节点:度为0的节点。
  3. 树的度:一棵树中,最大的节点的度称为树的度。
  4. 节点的高度:从该节点起到叶子节点的最长简单路径的边数。(简单路径:无重复边的路径)
  5. 树的高度:根节点的高度。
  6. 节点的层数:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。
  7. 数的层数:根节点的层数。
  8. 节点的深度:即该节点的层数。
  9. 树的深度:根节点的深度。
  10. 外节点:叶子节点。
  11. 内节点:除叶子节点之外的节点。
  12. 满二叉树:二叉树中节点的度只能是0或2。
  13. 完全二叉树:除最后一层,每一层的节点数都达到最大。最后一层若是没满,则节点集中在左边,空的只能是右边。
  14. 扩充二叉树:对二叉树中度为1的节点和叶子节点添加空节点,使之成为满二叉树。

注:关于树深度、层数、高度的定义会有不同的说法:有从0开始计数的,也有从1开始计数的。从哪儿开始计数不是关键,关键在于一致性,量的写法要一致。

五、几个量的关系

  1. 对于二叉树,根节点是第一层,则第i层至多有个结点。若共有k层,则最多有节点个。
  2. 按层次顺序对一棵有n个节点的完全二叉树的所有节点从0到n-1编号。若父节点的编号是i,则左孩子的编号是2*i+1,右孩子的编号是2*i+2。(当然,这是在存在左右孩子的情况下)。同样的,若孩子(无论左右孩子)节点是i,则父节点是(i-1)/2。
  3. 对于一棵满二叉树,外部节点或者说是叶子节点数是n,则内部节点数是n-1。
  4. 对于一棵二叉树,用ni表示度为i的节点个数,则n0=n2+1。证明如下:总节点数n=n0+n1+n2。用e表示边数,则n=e+1,这是因为除根节点外,每一个节点都和一条边对应。同时,e=2n2+n1,推出n0+n1+n2=2n2+n1+1,化简即得n0=n2+1。这个结论用语言表述:二叉树中,叶子节点比度为2的节点多一个。
  5. 有n个节点的完全二叉树,树高度,即logn向下取整,高度从0计数。
  6. 在二叉树中,第i层的第一个节点(最左边的节点)的编号是,层数从0计数。这个量在选择排序:树形选择中用到了。

六、示意图

二叉树遍历:

在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right
subtree)。

一颗简单的二叉树

二叉树的遍历分为三种:前(先)序、中序、后序遍历。

设L、D、R分别表示二叉树的左子树、根结点和遍历右子树,则先(根)序遍历二叉树的顺序是DLR,中(根)序遍历二叉树的顺序是LDR,后(根)序遍历二叉树的顺序是LRD。还有按层遍历二叉树。这些方法的时间复杂度都是O(n),n为结点个数。

假设我们有一个包含值的value和指向两个子结点的left和right的树结点结构。我们可以写出这样的过程:

先序遍历(递归实现):

visit(node)

print node.value

if node.left  != null then visit(node.left)

if node.right != null then visit(node.right)

中序遍历(递归实现):

visit(node)

if node.left  != null then visit(node.left)

print node.value

if node.right != null then visit(node.right)

后序遍历(递归实现):

visit(node)

if node.left  != null then visit(node.left)

if node.right != null then visit(node.right)

print node.value

二叉树创建和遍历:

public class BinaryTree {  

    private Node root;  

    /**
     *
     * 内部节点类
     * @author yhh
     */
    private class Node{
        private Node left;
        private Node right;
        private int data;
        public Node(int data){
            this.left = null;
            this.right = null;
            this.data = data;
        }
    }  

    public BinaryTree(){
        root = null;
    }  

    /**
     * 递归创建二叉树
     * @param node
     * @param data
     */
    public void buildTree(Node node,int data){
        if(root == null){
            root = new Node(data);
        }else{
            if(data < node.data){
                if(node.left == null){
                    node.left = new Node(data);
                }else{
                    buildTree(node.left,data);
                }
            }else{
                if(node.right == null){
                    node.right = new Node(data);
                }else{
                    buildTree(node.right,data);
                }
            }
        }
    }  

    /**
     * 前序遍历
     * @param node
     */
    public void preOrder(Node node){
        if(node != null){
            System.out.println(node.data);
            preOrder(node.left);
            preOrder(node.right);
        }
    }  

    /**
     * 中序遍历
     * @param node
     */
    public void inOrder(Node node){
        if(node != null){
            inOrder(node.left);
            System.out.println(node.data);
            inOrder(node.right);
        }
    }  

    /**
     * 后序遍历
     * @param node
     */
    public void postOrder(Node node){
        if(node != null){
            postOrder(node.left);
            postOrder(node.right);
            System.out.println(node.data);
        }
    }  

    public static void main(String[] args) {
        int[] a = {2,4,12,45,21,6,111};
        BinaryTree bTree = new BinaryTree();
        for (int i = 0; i < a.length; i++) {
            bTree.buildTree(bTree.root, a[i]);
        }
        bTree.preOrder(bTree.root);
        bTree.inOrder(bTree.root);
        bTree.postOrder(bTree.root);
    }  

}  

插入操作

二叉树查找树b插入操作x的过程如下:

1、若b是空树,则直接将插入的结点作为根结点插入。

2、x等于b的根结点的数据的值,则直接返回,否则。

3、若x小于b的根结点的数据的值,则将x要插入的结点的位置改变为b的左子树,否则。

4、将x要出入的结点的位置改变为b的右子树。

具体代码:

 public void insert(T t)
   {
       rootTree = insert(t, rootTree);
   }  

   public BinaryNode insert(T t,BinaryNode node)
   {
       if(node==null)
       {
           //新构造一个二叉查找树
           return new BinaryNode(t, null, null);
       }
       int result = t.compareTo(node.data);
       if(result<</SPAN>0)
          node.left= insert(t,node.left);
       else if(result>0)
          node.right= insert(t,node.right);
       else
           ;//doNothing
       return node;
   }  

删除操作:

对于二叉查找树的删除操作(这里根据值删除,而非结点)分三种情况:

不过在此之前,我们应该确保根据给定的值找到了要删除的结点,如若没找到该结点

不会执行删除操作!

下面三种情况假设已经找到了要删除的结点。

1、如果结点为叶子结点(没有左、右子树),此时删除该结点不会玻化树的结构

直接删除即可,并修改其父结点指向它的引用为null.如下图:

2、如果其结点只包含左子树,或者右子树的话,此时直接删除该结点,并将其左子树

或者右子树设置为其父结点的左子树或者右子树即可,此操作不会破坏树结构。

3、 当结点的左右子树都不空的时候,一般的删除策略是用其右子树的最小数据 (容易找到)代替要删除的结点数据并递归删除该结点(此时为null),因为 右子树的最小结点不可能有左孩子,所以第二次删除较为容易。 z的左子树和右子树均不空。找到z的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替z的值.如图:

                   

具体代码:

 public void remove(T t)
   {
       rootTree = remove(t,rootTree);
   }
   public BinaryNode remove(T t,BinaryNode node)
   {
       if(node == null)
           return node;//没有找到,doNothing
       int result = t.compareTo(node.data);
       if(result>0)
           node.right = remove(t,node.right);
       else if(result<</SPAN>0)
           node.left = remove(t,node.left);
       else if(node.left!=null&&node.right!=null)
       {
           node.data = findMin(node.right).data;
           node.right = remove(node.data,node.right);
       }
       else
           node = (node.left!=null)?node.left:node.right;
       return node;  

   }  

查找二叉树节点:

在二叉查找树中查找x的过程如下:

1、若二叉树是空树,则查找失败。

2、若x等于根结点的数据,则查找成功,否则。

3、若x小于根结点的数据,则递归查找其左子树,否则。

4、递归查找其右子树。

具体代码:

public TreeNode search(int Key) {
        TreeNode node = root;
        // 首先定义一个节点让其指向根,在下面的循环中
        // 只要节点值不等于要查找的节点值就进入循环如果没有找到则返回null
        while (node.keyValue != Key) {
            if (Key < node.keyValue) { // 如果要查找的值小于节点值则指向左节点
                node = node.leftNode;
            } else { // 否则指向右节点
                node = node.rightNode;
            }
            if (node == null) { // 如果节点为空了则返回null
                return null;
            }
        }
        return node;
    }  

最小值和最大值:

思路:最大值一直往右走,最小值一直往左走。

具体代码:

public int max() {
    TreeNode node = root;
    TreeNode parent = null;
    while (node != null) {
        parent = node;
        node = node.rightNode;
    }
    return parent.keyValue;
}  

public int min() {
    TreeNode node = root;
    TreeNode parent = null;
    while (node != null) {
        parent = node;
        node = node.leftNode;
    }
    return parent.keyValue;
}  

树的深度:

具体代码:

int length(Node root){
     int depth1;
     int depth2;
     if(root == null) return 0;
     //左子树的深度
     depth1 = length(root.right);
     //右子树的深度
     depth2 = length(root.left);
     if(depth1>depth2)
	    return depth1+1;
     else
	    return depth2+1;
}

参考链接:

http://blog.sina.com.cn/s/blog_401823210101f8t6.html

http://blog.sina.com.cn/s/blog_937cbcc10101dmqm.html

http://www.cnblogs.com/wuchanming/p/4067951.html

http://blog.csdn.net/fansongy/article/details/6798278

http://www.cricode.com/3489.html

http://www.cricode.com/3212.html

http://blog.csdn.net/sjf0115/article/details/8645991

http://www.cnblogs.com/vamei/archive/2013/03/17/2962290.html

http://blog.csdn.net/sysu_arui/article/details/7865876

http://blog.163.com/xiaopengyan_109/blog/static/14983217320108168618624/

时间: 2024-08-09 14:45:01

二叉树基础及应用的相关文章

二叉树基础

一.二叉树深度优先遍历 只介绍先序遍历: (1) 第一种方法是使用stack的结构 (2) 主要要理解后面的分治法 Version 0: Non-Recursion (Recommend) /** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class

二叉树基础算法总结

记录一些二叉树的基础算法 二叉树节点结构: typedef struct TreeNode{ int val; struct TreeNode *left; struct TreeNode *right; }TreeNode,*Node; 1.遍历 前.中.后序递归遍历: void pre_order_traversal(TreeNode *root){ if(root! = NULL){ visit(root); pre_order_traversal(root->left); pre_ord

二叉树基础(创建方法,遍历方法(前序/中序/后序/层序、递归/非递归)

二叉树的创建及遍历是很多二叉树问题的基础,递归遍历逻辑清晰,代码简约漂亮,然则效率低下(所有递归方案的通病,非不得已不用递归): 非递归遍历高效,却不是能信手写出来的,特别是后续非递归遍历,相信很多资深码工也有这样的经历: 5年前学习了二叉树的非递归遍历,一个月前复习了并达到能熟练写出的程度,在不参考任何资料的情况下,今天却怎样也写不出来. 如果你也有过这种经历,恭喜你,这说明你是一个正常人…… 另一方面市面上有些国人写的教材,各种语法.逻辑错误层出不起,不知祸害了多少未来的码工,深感痛心. 印

【二叉树】 二叉树基础

在计算机科学中,二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用于实现二叉查找树和二叉堆. 二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有个结点:深度为k的二叉树至多有个结点:对任何一棵二叉树T,如果其终端结点数为,度为2的结点数为,则. 树和二叉树的三个主要差别: 树的结点个数至少为1,而二叉树的

二叉树基础练习

前序遍历(先根遍历):根,左子树,右子树 中序遍历:左子树,根,右子树后序遍历:左子树,右子树,根 先序遍历:ABDECF 中序遍历:DBEAFC 后序遍历:DEBFCA 层次遍历:ABCDEF UVA 112  Tree Summing 题目:给你一个数和一棵树,问是否存在根到叶子的路径使得路径上的数字和与已知数相等. 注意数据中可能有负数 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #i

树、二叉树基础

刚看到堆排序,顺便记录一下关于树的一些基本概念: 前言 前面介绍的栈.队列都是线性结构(linear structure).而树是非线性结构(non-linear structure).因此,树中的元素之间一般不存在类似于线性结构的一对一的关系,更多地表现为多对多的关系.直观地看,它是数据元素(在树中称为节点)按分支关系组织起来的结构.显然,树形结构是比线性结构更复杂的一种数据结构类型. 一.树 树的定义:树是含有n个节点的有穷集合,其中有一个节点比较特殊称为根节点.在图示树时,用一条边连接两个

二叉树基础算法--遍历

二叉树的前序遍历(144. Binary Tree Preorder Traversal) 递归 1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class So

二叉树基础——前序遍历、中序遍历、后序遍历、按层遍历

转载请注明原文地址: 一:树的结点 一般默认树的结点由:结点值.左儿子.右儿子,构造函数组成. class TreeNode{ int value; TreeNode left; TreeNode right; public TreeNode(int i){ this.value=i; } } 二:二叉树的遍历实现--递归和非递归 1:递归实现==按照遍历方式(前.中.后)对左.根.右的访问顺序去 打印结点值.递归左儿子.递归右儿子 public void preorder(TreeNode r

树与二叉树基础算法的比较

一:树的创建 在数据结构中,树是以二叉树的形式储存的. 树转换为二叉树形式分为三步: ⑴加线——树中所有相邻兄弟之间加一条连线. ⑵去线——对树中的每个结点,只保留它与第一个孩子结点之间的连线,删去它与其它孩子结点之间的连线. ⑶层次调整——以根结点为轴心,将树顺时针转动一定的角度,使之层次分明. 转换后结果如图: 所以树的创建算法有两个思路: 1.将树转化为二叉树后,以二叉树中结点的关系输入而创建树. 2.直接以树中结点的关系输入,用代码转换为相应的二叉树. 第一种方法实际就是二叉树创建,只不