zoukankan      html  css  js  c++  java
  • java数据结构和算法(一):二叉树

      有序数组的查找速度很快,但是插入一个数据项时,就必须先找到插入数据项的位置,然后将后面所有的数据项后移一位,平均要移动N/2次,这是很费时的,删除数据也是。

      链表的插入和删除很快,只需要改变引用值就行了,但是查找数据却很慢,需要从链表的第一个节点查找到所需要的数据项为止,平均需要比较N/2次。

      树这种数据结构就具有了两者的优点,数组查找快以及链表插入、删除快。

    1.树的定义:

      树是n个有限个数据点通过连接他们的边组成的具有层次关系的集合。

      节点高度:从叶节点向上数,连接该节点的边数就是节点的高度

      节点深度:从根节点往下数,该节点所在的层数就是节点的深度。

      树中节点高度和深度的最大值就是树的高度和深度。注意:有两种说法,一种是根的深度为0,一种根的深度为1,所以树的深度取决于根节点所在的层数是0还是1。

    2.二叉树的性质:

      (1)两种特殊的树:满二叉树和完全二叉树。在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有的叶节点都在同一层上称为满二叉树;对于一颗具有n个节点的二叉树按层序编号,如果编号为i(0<i<=n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则称完全二叉树。

      (2)在二叉树中的第i层上至多有2^(i-1)个节点(i>=1)

      (3)深度为k的二叉树至多有2^k-1个节点(k>=1)

        (4)  对于任何一颗二叉树T,如果其叶子节点数为n0,度为2的节点数为n2,则n0 = n2+1

      (5)具有n个节点的完全二叉树的深度为|log2n|+1(|log2n|为不大于它自身的最大整数)。

    3.二叉树的插入、查找和删除

      这里主要讲二叉搜索树的操作。首先谈二叉搜索树的简单定义:如果节点不为空,有右子节点》根节点》左子节点,满足这样条件的树称为二叉搜索树。

    插入:

    public void insert(int data){
    if(root==null){
    root = new Node(data);
    return;
    }
    Node temp = root;
    Node pre = new Node();//获取待插入节点的父节点
    while(temp!=null){
    pre = temp;
    if(temp.data==data){
    return;
    }else if(temp.data>data){
    temp = temp.leftnode;
    }else{
    temp = temp.rightnode;
    }
    }
    if(pre.data>data){
    pre.leftnode = new Node(data);
    }else{
    pre.rightnode = new Node(data);
    }

    }

    查找:

    public Node search(int data){
    Node temp = root;
    while(temp!=null){
    if(temp.data==data){
    return temp;
    }else if(temp.data>data){
    temp = temp.leftnode;
    }else{
    temp = temp.rightnode;
    }
    }
    return new Node();
    }

    删除:

    分情况讨论: 

     (1)删除的节点如果没有右节点则直接用左子节点替换。

     (2)否则,如果删除节点的右节点不存在左子树,则直接用该右子节点替换。

     (3)否则,找到右节点的左子树中最左的节点替换被删除的节点,然后将替换节点的右子节点放到替换节点的位置。

     (1)第一种情况         (2)第二种情况

     (3)第三种情况

     1 public boolean delete(int data){
     2         Node temp = root;
     3         Node pre = new Node();
     4         pre.leftnode = root;
     5         byte state = 0;               // 0代表左子节点,1代表右子节点
     6         while(temp!=null&&temp.data!=data){    //找到待删除的节点
     7             pre = temp;
     8             if(temp.data>data){
     9                 temp = temp.leftnode;
    10                 state = 0;
    11             }else{
    12                 temp = temp.rightnode;
    13                 state = 1;
    14             }
    15         }
    16         if(temp==null) return false;
    17 
    18         Node templ = temp.leftnode;      //保存待删除节点的左子树和右子树
    19         Node tempr = temp.rightnode;
    20 
    21         if(tempr==null){            //第一种情况
    22             connectNode(pre,templ,state);
    23         }else if(tempr.leftnode==null){   //第二种情况
    24             connectNode(pre,tempr,state);
    25             tempr.leftnode = templ;
    26         }else{                  //第三种情况
    27             Node pre2 = tempr;
    28             temp = pre2.leftnode;
    29             while(temp.leftnode!=null){
    30                 pre2 = temp;
    31                 temp = temp.leftnode;
    32             }
    33             pre2.leftnode = temp.rightnode;
    34 
    35             temp.leftnode = templ;
    36             temp.rightnode = tempr;
    37             if(pre.leftnode==root) root = temp;
    38             connectNode(pre,temp,state);
    39         }
    40         return true;
    41     }

    private void connectNode(Node pre, Node node, byte state) {
        if(state==0){
    pre.leftnode = node;
    }else{
    pre.rightnode = node;
    }
    }

      通过上面的删除分类讨论,我们发现删除其实是挺复杂的,那么其实我们可以不用真正的删除该节点,只需要在Node类中增加一个标识字段isDelete,当该字段为true时,表示该节点已经删除,反正没有删除。那么我们在做比如find()等操作的时候,要先判断isDelete字段是否为true。这样删除的节点并不会改变树的结构。

    4.遍历二叉树

    (1)前序遍历:父节点=》左子节点=》右子节点

    递归算法:

    1 public void beforeOrder(Node node){
    2         if(node==null){
    3             return;
    4         }
    5         System.out.println(node.data);
    6         beforeOrder(node.leftnode);
    7         beforeOrder(node.rightnode);
    8     }

    非递归算法:

     1     public void beforeOrder2(Node node){
     2         Stack<Node> stack = new Stack<>();
     3         stack.push(node);
     4         while(!stack.isEmpty()){
     5             Node pop = stack.pop();
     6             if (pop==null) continue;
     7             System.out.println(pop.data);
     8             stack.push(pop.rightnode);
     9             stack.push(pop.leftnode);
    10         }
    11 
    12     }

    (2)中序遍历:左子节点=》父节点=》右子节点

    递归算法:

    1 public void inOrder(Node node){
    2     if(node==null){
    3         return;
    4     }
    5     inOrder(node.leftnode);
    6     System.out.println(node.data);
    7     inOrder(node.rightnode);
    8 }

    非递归算法:

     1 public void inOrder2(Node node){
     2     Stack<Node> stack = new Stack<>();
     3     Node cur = node;
     4     while(!stack.isEmpty()||cur!=null){
     5         while(cur!=null){
     6             stack.push(cur);
     7             cur = cur.leftnode;
     8         }
     9         Node pop = stack.pop();
    10         System.out.println(pop.data);
    11         if(pop.rightnode==null) continue;
    12         cur = pop.rightnode;
    13     }
    14 }

    (3)后序遍历:左子节点=》右字节点=》父节点

    递归算法:

    1 public void afterOrder(Node node){
    2     if(node==null){
    3         return;
    4     }
    5     afterOrder(node.leftnode);
    6     afterOrder(node.rightnode);
    7     System.out.println(node.data);
    8 }

    非递归算法:

     1 public void afterOrder2(Node node){
     2     Stack<Node> stack = new Stack<>();
     3     stack.push(node);
     4     List<Node> mylist = new ArrayList<>();
     5     while(!stack.isEmpty()){
     6         Node pop = stack.pop();
     7         if (pop==null) continue;
     8         mylist.add(pop);
     9         stack.push(pop.leftnode);
    10         stack.push(pop.rightnode);
    11     }
    12     Collections.reverse(mylist);
    13     for (Node node1 : mylist) {
    14         System.out.println(node1.data);
    15     }
    16 }

    (4)层次遍历

     1 public void levelOrder(Node node){
     2     Queue<Node> queue = new LinkedList<>();
     3     queue.offer(node);
     4     while(!queue.isEmpty()){
     5         int size = queue.size();
     6         while(size-->0){
     7             Node node1 = queue.poll();
     8             if(node1==null){
     9                 continue;
    10             }
    11             System.out.println(node1.data);
    12             queue.offer(node1.leftnode);
    13             queue.offer(node1.rightnode);
    14         }
    15     }
    16 }

    5.效率分析

      在插入的数据是无序的情况下,插入的效率是logN;在二叉树是平衡树的情况下,删除、查找的效率都为logN;但是树的遍历效率比较低。

  • 相关阅读:
    使用RSA实现客户端和后台的非对称加密
    PHP异步并行网络扩展swoole安装
    rvm is not a function的解决方法
    python中的sort(),sorted()的用法
    RESTful框架中的Mixin中的五个类视图
    RESTful框架的API请求流程
    理解RESTful架构
    Django中的admin管理工具
    一个程序员的自我介绍--英文版
    Django中的认证系统
  • 原文地址:https://www.cnblogs.com/amazing-eight/p/12672054.html
Copyright © 2011-2022 走看看