数据结构(七)-树-基本概念、三种存储结构(Java、C语言)

数据结构(七)-树-基本概念、三种存储结构(Java、C语言),第1张

数据结构(七)-树-基本概念、三种存储结构(Java、C语言)

每天get一个新知识,超开心~
前面讲的数据结构都是前后具有一对一的关系,无论是线性表还是队列,栈等都是一对一的关系,那么如果出现一对多的关系的时候该怎么办呢?这种一对多的关系,就是今天要学习的数据结构——树。

文章目录

一、树的定义二、树的基本概念三、树的抽象数据类型四、树的双亲表示法(Java、C语言)五、树的孩子表示法(Java、C语言)六、树的孩子兄弟表示法(Java、C语言)

一、树的定义

树(Tree)是n(n>=0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:

    有且仅有一个特定的称为根(root)的结点;当n>1时,其余结点可分为m(m>0)个互补交互的有限集T1、T2…Tm,其中每一个集合本身又是一棵树,并称为根的子树(SubTree)。

    树需要注意两点
    1.有且仅有一个根结点
    2.子树没有限制,但是它们一定是不能相交的
二、树的基本概念

1、结点的分类:
树的结点包含一个数据元素及若干个指向其子树的分支。结点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。

2、结点间的关系
结点的子树的根称为该结点的孩子(Child),相应的,该结点称为孩子的双亲(Parent)(注意是双亲,不是单亲)。同一个双亲的孩子之间互称兄弟(Sibling)。结点的祖先是从根到该结点所经分支上的所有结点。以某结点为根的子树中的任一结点都称为该结点的子孙。

3、树的其他相关概念
结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层。双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度(Depth)或高度。

如果将树中结点的各子树看成从左至右是有次序的,不能互换,则称该树为有序树,否则为无序树。
森林(Forest)是m(m>=0)棵互不相交的树的集合。

三、树的抽象数据类型
ADT 树(tree)
Date
树是由一个根结点和若干颗子树构成。树中结点具有相同数据类型及层次关系。
Operation
InitTree(*T):构造空树T。
DestroyTree(*T):销毁树T。
CreateTree(*T,definition):按definition中的定义给出树。
ClearTree(*T):若树存在,则将树T清为空树。
TreeEmpty(T):若T为空树,则返回ture,否则返回false。
Tree depth(T):返回T的深度。
Root(T):返回T的根结点。
value(T,cur_e):cur_e是树中的一个结点,返回该结点的值。
Assign(T,cur_e,value):将结点cur_e的值赋值给value.
Parent(T,cur_e):若cur_e是T的根结点,则返回空,否则返回其双亲。
LeftChild(T,cur_e):若cur_e是树T的非叶结点,则返回cur_e的最左孩子。
RightSibiling(T,cur_e):若cur_e有右兄弟,则返回它的右兄弟,否则返回空。
InsertChild(*T,*p,i,c):其中p指向树T的某个结点,i为p所指的结点度加上1,
                             非空树c与T不相交,将c插入p为所指结点的第i个子树
DeleteChild(*T,*p,i):其中p指向树T的某个结点,i为所指结点p的度,
                                   *** 作结果为删除T中p所指结点的第i颗子树。
endDate

四、树的双亲表示法(Java、C语言)

双亲表示法定义
假设以一组连续空间存储数的结点,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。
双亲表示的结点结构

data(数据域)parent(指针域)存储结点的数据信息存储该结点的双亲所在数组中的下标

#define MAX_TREE_SIZE 100

typedef int TElemType;				

typedef struct PTNode				
{
	TElemType data;					
	int parent;						
} PTNode;

typedef struct						
{
	PTNode nodes[MAX_TREE_SIZE];	
	int r,n;						
} PTree;

下面是Java代码

package Trees;

import java.util.Arrays;

public class TreeImp1 {
    private int capacity;//树容量
    private int nodeCount;//树结点数量
    private Node[] elements;//存放Node类型的数组
    //查询树的结点数量
    public int getNodeCount() {
        return nodeCount;
    }

    public void setNodeCount(int nodeCount) {
        this.nodeCount = nodeCount;
    }

    public int getCapacity() {
        return capacity;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    public Node[] getElements() {
        return elements;
    }

    public void setElements(Node[] elements) {
        this.elements = elements;
    }

    //设置数组容量的构造方法
    public TreeImp1(int capacity) {
        this.capacity = capacity;
        this.elements = new Node[capacity];
    }

	    @Override
    public String toString() {
        return "TreeImp1{" +
                "elements=" + Arrays.toString(elements) +
                '}';
    }

    //是否空树
    public boolean isEmpty(){
        return nodeCount == 0;
    }

    //设置根结点
    public Node setRootNode(int a){
        this.elements[0] = new Node(a,-1);
        nodeCount++;
        return new Node(a, -1);
    }

    //查询根结点
    public Node getRootNode(){
        return this.elements[0];
    }

    //为某个结点添加结点
    
    public void addNode(int data, int parent, int newData){
        if (nodeCount > capacity){
            throw new RuntimeException("超出容量");
        }
        else{
            for (int i = 0; i < nodeCount; i++) {
                Node oldNode = new Node(data, parent);
                if (this.elements[i].equals(oldNode)){
                    this.elements[nodeCount] = new Node(newData,i);
                    nodeCount++;
                    break;
                }
            }
        }
    }

    //查找第一个存放数值为data的结点的索引位置,有的话返回该数据结点索引,否则返回-1
    public int indexNode(int data){
        for (int i = 0; i < nodeCount; i++) {
            if (this.elements[i].getData() == data){
                return i;
            }
        }
        return -1;
    }

    //查询某个结点的所有子结点,并将他们放入一个新的数组中
    public TreeImp1 arrayChildren(int data, int parent){
        Node oldNode = new Node(data,parent);
        TreeImp1 t = new TreeImp1(this.capacity);
        for (int i = 0; i < nodeCount; i++) {
            if (this.elements[i].equals(oldNode)){
                for (int j = 0; j < nodeCount; j++) {
                    if (this.elements[j].getParent() == i){
                        t.elements[t.nodeCount] = elements[j];
                        t.nodeCount++;
                    }
                }
                return t;
            }
        }
        return new TreeImp1(0);
    }

    //求树的度
    public int degreeForTree(){
        int max = 0;
        for (int i = 0; i < nodeCount; i++) {
            TreeImp1 x = arrayChildren(this.elements[i].getData(), this.elements[i].getParent());
            int z = x.nodeCount;
            max = z > max ? z : max;
        }
        return max;
    }


}

//结点类
class Node{
    private int data;//结点中存放的数据
    private int parent;//结点的父结点的索引
    //两个参数构造方法
    public Node(int data, int parent) {
        this.setData(data);
        this.setParent(parent);
    }
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public int getParent() {
        return parent;
    }
    public void setParent(int parent) {
        this.parent = parent;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) 		  return false;
        Node node = (Node) o;
        return data == node.data &&
                parent == node.parent;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", parent=" + parent +
                '}';
    }
}

树的存储描述是比较自由的,我们可以在指针域中加入一个指向孩子的指针域,那我们就可以知道这个结点的孩子是谁。

五、树的孩子表示法(Java、C语言)

把每个结点的孩子结点排列起来,以单链表作为存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。

孩子表示法的结点结构

孩子表示法有两种结点结构:孩子链表的孩子结点和表头数组的表头结点

孩子链表的孩子结点

child(数据域)next(指针域)存储某个结点在表头数组中的下标存储指向某结点的下一个孩子结点的指针

表头数组的表头结点

data(数据域)firstchild(头指针域)存储某个结点的数据信息存储该结点的孩子链表的头指针

C语言代码

#define MAX_TREE_SIZE 100

typedef int TElemType;			

typedef struct CTNode			
{
	int child;	
	struct CTNode *next;	
} *ChildPtr;

typedef struct 					
{
	TElemType data;	
	ChildPtr firstchild;	
} CTBox;

typedef struct	     			
{
	CTBox nodes[MAX_TREE_SIZE];	
	int r,n;					
} CTree;

Java代码(转载的代码)

package com.datastruct.study.tree;
public class SonShow {
    private final static Integer ARRAY_SIZE = 10;
    private  InnerArray[] dataArray;
    private Integer position = null;
    
    class InnerArray{
        private String data;
        private ChildNode firstChild;

        public InnerArray(String data, ChildNode firstChild) {
            this.data = data;
            this.firstChild = firstChild;
        }

        public String getData() {
            return data;
        }

        public ChildNode getFirstChild() {
            return firstChild;
        }
    }
    class ChildNode{
        private Integer childPosition;
        private ChildNode next;

        public ChildNode(Integer childPosition, ChildNode next) {
            this.childPosition = childPosition;
            this.next = next;
        }

        public Integer getChildPosition() {
            return childPosition;
        }

        public void setChildPosition(Integer childPosition) {
            this.childPosition = childPosition;
        }

        public ChildNode getNext() {
            return next;
        }

        public void setNext(ChildNode next) {
            this.next = next;
        }
    }
    public SonShow(){
        dataArray = new InnerArray[ARRAY_SIZE];
    }
    public SonShow(int size){
        dataArray = new InnerArray[size];
    }
    public String add(String value,Integer parentPosition){
        if(position == null){
            position = 0;
            dataArray[position++] = new InnerArray(value,null);
            return value;
        }
        //先插入到数组的位置,再将该节点链接到对应的父节点的链表最后
        dataArray[position++] = new InnerArray(value,null);

        //如果还没有孩子链表则生成
        if(dataArray[parentPosition].firstChild==null){
            //上面++了所以次数要-1,指定刚插入的孩子节点的位置
            dataArray[parentPosition].firstChild = new ChildNode(position-1,null);
            return value;
        }
        //若是已经存在链表,则遍历链表,放在最后面
        ChildNode firstNode = dataArray[parentPosition].getFirstChild();
        while (firstNode.next!=null){
            firstNode = firstNode.next;
        }
        firstNode.next = new ChildNode(position-1,null);
        return value;
    }
    public InnerArray getValueAndChild(Integer position){
        return dataArray[position];
    }
    public int size(){
        return position;
    }
    }
}
六、树的孩子兄弟表示法(Java、C语言)

定义:任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟存在也是唯一的。因此,设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。

孩子兄弟表示法的结点结构

data(数据域)firstchild(指针域)firstchild(指针域)存储结点的数据信息存储该结点的第一个孩子的存储地址存储该结点的右兄弟结点的存储地址

typedef struct CSNode
{
	TElemType data;	
	struct CSNode *firstchild,*rightsib;	
} CSNode,*CSTree;

这个孩子兄弟表示法,其实更好理解,就两个指针,一个指向孩子,一个指向兄弟,那么,这样一来就把复杂的树转化成为了二叉树。
这个Java代码也很简单,如下:
Java代码

class TreeNode {
    private TreeNode sonNode;//这里存储的是最左的儿子节点
    private TreeNode brotherNode;//这里存储的是自己的兄弟节点
    private String value;

    public TreeNode(TreeNode sonNode, TreeNode brotherNode, String value) {
        this.sonNode = sonNode;
        this.brotherNode = brotherNode;
        this.value = value;
    }
    public TreeNode getSonNode() {
        return this.sonNode;
    }
    public TreeNode getBrotherNode() {
        return brotherNode;
    }
    public String getValue() {
        return value;
    }
    public void setSonNode(TreeNode sonNode) {
        this.sonNode = sonNode;
    }
    public void setBrotherNode(TreeNode brotherNode) {
        this.brotherNode = brotherNode;
    }
}

class SBTree {
    private TreeNode root;//这个是根节点

    public SBTree(String rootValue) {//构建树的时候需要吧根节点的值初始化一下
        root = new TreeNode(null, null, rootValue);
    }

    public TreeNode getRoot() {
        return this.root;
    }

    public TreeNode insertSonNode(TreeNode targetNode, String value) {
        TreeNode nowSonNode = new TreeNode(null, null, value);
        TreeNode preSonNode = targetNode.getSonNode();
        if (preSonNode != null) { //已经有儿子,把前儿子的兄弟设置为自己.
            return insertBrotherNode(preSonNode, value);
        } else
            targetNode.setSonNode(nowSonNode);
        return nowSonNode;
    }

    public TreeNode insertBrotherNode(TreeNode targetNode, String value) {
        TreeNode nowBrotherNode = new TreeNode(null, null, value);
        while (targetNode.getBrotherNode() != null) { //有兄弟了
            targetNode = targetNode.getBrotherNode();
        }
        targetNode.setBrotherNode(nowBrotherNode);
        return nowBrotherNode;
    }

    static void printTree(TreeNode root, int depth) {
        if (root == null)
            return;
        for (int i = 0; i < depth; i++) {
            System.out.print("t|");
        }
        System.out.println(root.getValue());
        printTree(root.getSonNode(), depth + 1);
        printTree(root.getBrotherNode(), depth);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5703284.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存