【JAVA】复习数据结构——树

我的复习一般是按照自己需求而定,所以不一定是按照顺序来的,在此也是为了怕自己忘记今天所学,特地记下来。


首先什么是树结构?

树是一种描述非线性层次关系的数据结构,树是n个数据结点的集合,这些集结点包含一个根节点,根节点下有着互相不交叉的子集合,这些子集合便是根节点的子树。

树的特点

  • 在一个树结构中,有且仅有一个结点没有直接前驱,它就是根节点。
  • 除了根节点,其他结点有且只有一个直接前驱
  • 每个结点可以有任意多个直接后继

树的名词解释

  • 结点的度:一个结点包含子树的数量。
  • 树的度:该树所有结点中最大的度。
  • 兄弟结点:具有同一父结点的结点称为兄弟结点。
  • 树的深度(高度):叶子结点的深度(高度)为1,根节点深度(高度)最高;
  • 层数:从树根开始算,树根是第一层,以此类推。
  • 森林:由多个树组成

只说干货,现在直接复习最重要的——二叉树

二叉树

二叉树是一种超级超级超级重要的数据结构!也是树表家族最为基础的结构
先看看定义:二叉树嘛,每个结点最多只能有二棵子树,二叉树的子树有左右之分,次序不能颠倒
再看看完全二叉树的性质:

  • 第i层至多有 2的i次方减一 个结点
  • 深度为k的二叉树至多有2k-1个结点
  • 任意二叉树,度为0的节点数=度为2的节点数+1;
  • 如果i为父亲的编号,则孩子的编号为2i和2i+1;
  • 如果孩子编号为n,父亲结点编号为k/2,向下取整

关于树的度:
二叉树中连接节点和节点的线就是度
上面说过——度为0的节点数为度为2的节点数加1,即n0=n2+1
这个公式的推理方法如下:
设:
k:总度数
k+1:总节点数
n0:度为0的节点
n1:度为1的节点
n2:度为二的节点
根据二叉树中度和节点的守衡原理,可列出以下一组方程:
k=n2*2+n1;
k+1=n2+n1+n0;
根据方程可以求出n0=n2+1

基本概念不说,研究一下二叉树的遍历

二叉树的遍历

首先,我们看看前序、中序、后序遍历的特性:
前序遍历: (根——左——右)
1.访问根节点
2.前序遍历左子树
3.前序遍历右子树
中序遍历: (左——根——右)
1.中序遍历左子树
2.访问根节点
3.中序遍历右子树
后序遍历: (左——右——根)
1.后序遍历左子树
2.后序遍历右子树
3.访问根节点

要知道:
中序遍历是很重要的一个判断参照!如果只给我们前序遍历和后序遍历的结果,我们将无法推导出唯一的树。
给我们前序遍历和中序遍历,我们可以推出中序
给我们后序遍历和中序遍历,我们可以推出中序


好了,已经写了很多但是并没有拿出来什么真干货,接下来用代码写一下关于树的操作,这里先讲好——对于“树”这个运行结构,最重要的概念是“递归”,要想把树的概念理解好,必须先培养递归的思想。我向这篇文章:写递归函数的正确思想学习了一下,真的写的很棒,条理清晰而且有详有略。在这之后我就希望通过自己亲手敲代码的方式去学习,但是研究了两天,依然进展比较一般,最后通过研究各方博客的C代码,从而找到了思路。不再说废话了,这东西,必须通过自己动手敲才能有收获,好的接下来就要拿代码出来了。这里感谢这片文章二叉树题目实现
以下则是用java代码去写的,注解已经很详细,没有分开写,时不时有新体会也会去添加或修改:


二叉树类:

package learnTree;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;

/**
 * Created by AceCream on 2017/3/14.
 * 二叉树
 */
class TreeNode {
    int key = 0; //key 为层序编码
    String data = null; //data 为数据域
    boolean isVisited = false;
    /*树的每一个节点的数据结构都是TreeNode类型,
    createBinTree里定义的root为TreeNode类型,所以左右孩子也为TreeNode类型,
    加上二叉树的递归思想,所以所有节点都是TreeNode类型
     */
    TreeNode leftChild = null;
    TreeNode rightChild = null;

    public TreeNode(int key,String data){
        this.key = key;
        this.data = data;
        this.isVisited = false;
        this.leftChild = null;
        this.rightChild = null;
    }

}

public class BinaryTree {

    //二叉树通常用树结点结构存储,有时也包含指向唯一父节点的指针
    TreeNode root = null;

    //BinaryTree()该方法与类名字相同,所以是构造方法,被默认强制为void
    //初始化根
    public  BinaryTree(){
        root = new TreeNode(1,"A");
    }

    //创建二叉树bt,树由结点构成
    public void createBinTree(TreeNode root){
        TreeNode newNodeB = new TreeNode(2,"B");
        TreeNode newNodeC = new TreeNode(3,"C");
        TreeNode newNodeD = new TreeNode(4,"D");
        TreeNode newNodeE = new TreeNode(5,"E");
        TreeNode newNodeF = new TreeNode(6,"F");
        //填充它
        root.leftChild = newNodeB;
        root.rightChild = newNodeC;
        root.leftChild.leftChild = newNodeD;
        root.leftChild.rightChild = newNodeE;
        root.rightChild.rightChild = newNodeF;
    }

    //创建二叉树bt2,树由结点构成
    public void createBinTree2(TreeNode root){
        TreeNode newNodeB = new TreeNode(2,"B");
        TreeNode newNodeC = new TreeNode(3,"C");
        TreeNode newNodeD = new TreeNode(4,"D");
        TreeNode newNodeE = new TreeNode(5,"E");
        //填充它
        root.leftChild = newNodeB;
        root.rightChild = newNodeC;
        root.leftChild.leftChild = newNodeD;
        root.leftChild.rightChild = newNodeE;
    }


    //访问结点,输出结点,便于我们查看效果
    public void visitNode(TreeNode node){
        System.out.print(node.data+" ");
    }

    //1.获取结点个数
    //递归解法:
    //(1)如果二叉树为空,节点个数为0
    //(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1
    public int size(){
        return size(root);
    }
    //通过递归求size
    public int size(TreeNode subtree){
        if (subtree==null){
            return 0;
        }else {
            return 1+size(subtree.leftChild)+size(subtree.rightChild);
        }
    }

    //2.求深度
    //递归解法:
    //(1)如果二叉树为空,二叉树的深度为0
    //(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
    public int getDepth(TreeNode root){
        if (root==null){
            return 0;
        }else {
            int depthLeft = getDepth(root.leftChild);
            int depthRight = getDepth(root.rightChild);
            return depthLeft > depthRight ? (depthLeft+1) : (depthRight+1);
        }
    }


    //3. 前序遍历,中序遍历,后序遍历
        //a.前序遍历
        //前序遍历递归解法:
        //(1)如果二叉树为空,空操作
        //(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树
    public void preOrderTraverse(TreeNode root){
        if (root!=null){
            visitNode(root);//此处访问根节点
            preOrderTraverse(root.leftChild);
            preOrderTraverse(root.rightChild);
        }
    }
        //b.中序遍历
        //中序遍历递归解法
        //(1)如果二叉树为空,空操作。
        //(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
    public void inOrderTraverse(TreeNode root){
        if (root!=null){
            inOrderTraverse(root.leftChild);
            visitNode(root);//此处访问根节点
            inOrderTraverse(root.rightChild);
        }
    }

        //c.后序遍历
        //后序遍历递归解法
        //(1)如果二叉树为空,空操作。
        //(2)如果二叉树不p为空,后序遍历左子树,后序遍历右子树,访问根节点,
    public void postOrderTraverse(TreeNode root){
        if (root!=null){
            postOrderTraverse(root.leftChild);
            postOrderTraverse(root.rightChild);
            visitNode(root);//此处访问根节点
        }
    }


    //4.分层遍历二叉树(按层次从上往下,从左往右)
    //相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。
    //当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。
    public void levelTraverse(TreeNode root){
        if (root==null){
            return;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        //让根节点入队(队列:先进先出)
        queue.offer(root);
        while (!queue.isEmpty()){
            //让元素出队
            TreeNode node = queue.poll();
            //访问它 这里就是用visit方法输出看效果~~
            visitNode(node);
            if (node.leftChild!=null){
                queue.offer(node.leftChild);
            }
            if (node.rightChild!=null){
                queue.offer(node.rightChild);
            }
        }
        return;
    }

    //5.求二叉树第K层的节点个数
    //递归解法:
    //(1)如果二叉树为空或者k<1返回0
    //(2)如果二叉树不为空并且k==1,返回1
    //(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和
    public int getNodeNumKthLevel(TreeNode root,int k){
        if (root==null||k<1){
            return 0;
        }else if (k==1){
            return 1;
        }else {
            int leftNum = getNodeNumKthLevel(root.leftChild,k-1);
            int rightNum = getNodeNumKthLevel(root.rightChild,k-1);
            return leftNum+rightNum;
        }
    }

    //6.求二叉树中叶子节点的个数
    // 递归解法:
    //(1)如果二叉树为空,返回0
    //(2)如果二叉树不为空且左右子树为空,返回1
    //(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数
    public int getLeafNodeNum(TreeNode root){
        if (root==null){
            return 0;
        }else if (root.leftChild==null&&root.rightChild==null){
            return 1;
        }else {
            int leftNum = getLeafNodeNum(root.leftChild);
            int rightNum = getLeafNodeNum(root.rightChild);
            return leftNum+rightNum;
        }
    }

      //8. 判断两棵二叉树是否结构相同
      //不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。
      //递归解法:
      //(1)如果两棵二叉树都为空,返回真
      //(2)如果两棵二叉树一棵为空,另一棵不为空,返回假
      //(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
      public boolean StructureCmp(TreeNode root1,TreeNode root2){
          if (root1==null&&root2==null){
              //都为空,返回真
              return true;
          }else if (root1==null||root2==null){
              //一个为空,一个不为空,返回假
              return false;
          }else {
              //都不为空
              boolean leftResult = StructureCmp(root1.leftChild,root2.leftChild);
              boolean rightResult = StructureCmp(root1.rightChild,root2.rightChild);
              //都同构则为真,否则为假
              return leftResult&&rightResult;
          }
      }
}

以上是方法。接下来是测试类:主函数

package learnTree;

/**
 * Created by AceCream on 2017/3/14.
 */
public class Main {
    public static void main(String[] args) {
        BinaryTree bt = new BinaryTree();
        BinaryTree bt2 = new BinaryTree();
        /*
            由初始化的时候可知:我创建了一个这样的树,供查看写的方法是否正确
            这棵树起名为bt:           这棵树起名为bt2 供比较
                      A                 A
                    /  \               / \
                   B   C              B  C
                  /\  /              /\
                 D E F              D E
        */
        bt.createBinTree(bt.root);
        bt2.createBinTree2(bt2.root);

        //1.看结点数
        System.out.println("1.树的结点个数为:" + bt.size(bt.root));
        //2.看深度
        System.out.println("2.树的深度为:"+bt.getDepth(bt.root));
        //3. 前序遍历,中序遍历,后序遍历
        System.out.print("3-1.先序遍历:");
        bt.preOrderTraverse(bt.root);
        System.out.println();

        System.out.print("3-2.中序遍历:");
        bt.inOrderTraverse(bt.root);
        System.out.println();

        System.out.print("3-3.后序遍历:");
        bt.postOrderTraverse(bt.root);
        System.out.println();

        //4.将二叉树用层次遍历
        System.out.print("4.二叉树层次遍历:");
        bt.levelTraverse(bt.root);
        System.out.println();

        //5.二叉树K层有多少个结点:由上图绘制可知:0层没有,1层有一个根节点,第二层有俩,第三次有仨
        System.out.println("5.二叉树K层结点个数:");
        System.out.println("    当K=0时有"+bt.getNodeNumKthLevel(bt.root,0)+"个结点");
        System.out.println("    当K=1时有"+bt.getNodeNumKthLevel(bt.root,1)+"个结点");
        System.out.println("    当K=2时有"+bt.getNodeNumKthLevel(bt.root,2)+"个结点");
        System.out.println("    当K=3时有"+bt.getNodeNumKthLevel(bt.root,3)+"个结点");

        //6.求二叉树中叶子节点的个数
        System.out.print("6.二叉树叶子节点个数:");
        System.out.println(bt.getLeafNodeNum(bt.root));

        //7.比较两个二叉树相不相同
        System.out.println("7.测试两树结构是否相同:");
        System.out.println("    b1和b1:"+bt.StructureCmp(bt.root,bt.root));
        System.out.println("    b1和b2:"+bt.StructureCmp(bt.root,bt2.root));
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容