数据结构——树的笔记

一:树的定义

树是由一个集合以及在该集合上定义的一种关系构成的。

二:一些基本术语

1:树:指的是N个父子关系的结点的有限集合。

空树:集合为空,即N=0。

2:树的度:树中所有节点的度的最大值就是该树的度。

3:树的深度:树中节点的最大值称为的深度或高度。

4:树的节点:包含一个数据元素和若干指向其子树的分支。

(1):根节点:在任意非空树中,有且只有一个根节点。

(2):叶子节点(终端结点):度为0的结点。

(3):分支结点(非终端结点,普通节点):度不为0的结点

——按是否包含子节点来分,节点分为两种

————普通节点:包含子节点的节点。

————叶子结点:没有子节点的节点,因此叶子结点不可作为父节点。

——按节点是否具有唯一的父节点来分:

————根节点:没有父节点的节点,根节点不可作为父节点。

————普通节点:具有唯一父节点的节点。

5:节点的度(degree):结点拥有的子树的数目。

6:子节点,父节点,兄弟节点:

节点的子树的个被称为该结点的子节点,

而该节点称做子节点的父节点。

具有相同父节点的子节点互称为兄弟节点。

7:节点的层次:节点的层次从根开始算起,根的层次值为1,其余节点的层次值为父节点层次加1.

8:祖先:结点的祖先是从根到该结点所经分支上所有的结点。

9:子孙:以某结点为根的子树中的任意一结点都称为该结点的子孙。

10 :有序树与无序树如果将树中节点的各个子树看成从左到右是有序的,则称为有序树,否则称为无序树。

11:森林:森林是2颗或2颗以上互不相交的数的集合,删去一个树的根,就得到了一个森林。

三:树的基本操作:

1:初始化:通常是一个构造器,用于创建一颗空的树,或者以指定节点为根来创建树。

2:为根节点添加子节点。

3:判断树是否为空。

4:返回根节点。

5:返回指定节点(非根节点)的父节点

6:返回指定节点(非叶子结点)的所有子节点

7:返回指定节点(非叶子结点)的第i个字节点

8:返回该树的深度。

9:返回指定节点的位置。

三:树的存储结构:

父节点表示法:每个子节点都记录它的父节点。(使用数组顺序存储)

子节点链表示法,每个非叶子结点通过一个链表来记录它所有的子节点。(链式存储)

四:父节点表示法

1:简单介绍:

可知树中除根节点之外的每个节点都有一个父节点,为了记录节点与节点之间的父子关系,

可以为每个节点增加一个parent域,用以记录该节点的父节点。

用一个节点数组来保存树里的每个节点,并让每个节点记录其父节点的在数组索引即可。

因此父节点表示法的思想:就是让每个节点记住它的父节点的索引,这种方式是从子节点着手。

2:范例树

3:记录范例树

4:代码实现:java代码实现,采用父节点表示法实现一颗树。

package kejian;

import javax.swing.plaf.basic.BasicTreeUI;
import java.util.ArrayList;
import java.util.List;

public class TreeParent<E> {
    public static class Node<T> {
        T data;
        int parent;//增加的parent域,用来记录节点的父节点。
       
        //下面三个方法进行节点的初始化
        public Node() {

        }
        public Node(T data) {
            this.data = data;
        }
        public Node(T data, int parent) {
            this.data = data;
            this.parent = parent;
        }

        public String toString() {
            return "TreeParent$Node[date" + data + ",parent="
                    + parent + "]";
        }
    }
        private final int default_tree_size=100;
        private int treeSize=0;
        private Node<E>[] nodes;//记录该树里的所有节点
        private int nodeNums;//记录节点数


        public TreeParent(E data)
        {//以指定的根节点创建树
            treeSize=default_tree_size;
            nodes=new Node[treeSize];
            nodes[0]=new Node<E>(data,-1);
            nodeNums++;
        }
        public TreeParent(E data,int treeSize)
        {//以指定根节点,指定treeSize创建树。
            this.treeSize=treeSize;
            nodes=new Node[treeSize];
            nodes[0]=new Node<E>(data,-1);
            nodeNums++;
        }


        public void addNode(E data,Node parent)
        {//为指定节点添加子节点
            for(int i=0;i<treeSize;i++){
                //找到数组中第一个为null的元素,该元素保存新节点
                if(nodes[i]==null){
                    //创建新节点,并用指定的数组元素保存它
                    nodes[i]=new Node(data,pos(parent));
                    nodeNums++;
                    return ;
                }
            }
            throw new RuntimeException("该树已满,无法添加新节点");
        }

        public boolean empty()
        {//判断树是否为空
            return nodes[0]==null;
        }
        public Node<E> root()
        {//返回根节点
            return nodes[0];
        }
        public Node<E> parent(Node node)
        {//返回节点的父节点
            return nodes[node.parent];
            //每个节点的parent记录的节点的位置
        }
        public List<Node<E>> children(Node parent)
        {//返回指定节点的所有节点
            List<Node<E>> list=new ArrayList<Node<E>>();
            for(int i=0;i<treeSize;i++){
                //如果当前节点的父节点的位置等于parent节点的位置
                if(nodes[i]!=null && nodes[i].parent==pos(parent))
                {
                    list.add(nodes[i]);
                }
            }
            return list;
        }

        public int deep()
        {//返回该树的深度
            int max=0;
            for(int i=0;i<treeSize && nodes[i]!=null;
                    i++)
            {
                int def=1;//初始化本节点的深度
                int m=nodes[i].parent;//m记录当前节点的父节点的位置
                while(m!=-1 && nodes[m]!=null){//如果父节点存在
                    //向上继续搜索父节点
                    m=nodes[m].parent;
                    def++;
                }
                if(max<def)
                    max=def;
            }
            return max;
        }
        public int pos(Node node)
        {//返回包含指定值的节点
            for(int i=0;i<treeSize;i++)
            {
                if(nodes[i]==node)
                    return i;
            }
            return -1;
        }



        public static void main(String[] args){
            TreeParent<String> t=new TreeParent<String>("root");
            TreeParent.Node root=t.root();
            System.out.println(root);
            t.addNode("节点1",root);
            System.out.println("此树的深度:"+t.deep());
            t.addNode("节点2",root);
            //获取根节点的所有子节点
            List<TreeParent.Node<String>> nodes=t.children(root);
            System.out.println("根节点的第一个节点:"+nodes.get(0));
            //为根节点的第一个子节点新增一个子节点
            t.addNode("节点3",nodes.get(0));
            System.out.println("此树的深度:"+t.deep());
        }
}

5:父节点表示法的特点是:

每个节点都可以快速找到它的父节点。但是如果要找某个节点的所有子节点就比较麻烦,程序要遍历整个节点数组。

 

五:子节点链表示法:

1:简单介绍

子节点链表示法:让父节点记住它的所有子节点,在这种方式下,由于每个父节点需要记住多个子节点,

因此必须采用“子节点链”表示法。采用子节点表示法来记录树时,需要为每个节点维护一个子节点链。

通过该子节点链来记录该节点的所有子节点。

2:范例树

3:子节点链表示法:A后面的节点链为A节点的所有子节点,同理B,C,D,E……

 

 

4:代码:java代码实现子节点链表示法。

定义树节点的first域:用于保存对该节点的子节点链的引用,通过这种方式记录树中节点之间的父子关系。

使用这种子节点链表示法来存储树时,添加节点时只需找到指定父节点的子节点链

的最后节点,并让它指向新增的节点。

package kejian;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by 20378 on 2018-11-02.
 */
public class TreeChild<E> {
    private static class SonNode {
        private int pos;//记录当前节点位置
        private SonNode next;

        public SonNode(int pos, SonNode next) {
            this.pos = pos;
            this.next = next;
        }
    }

    public static class Node<T> {
        T data;
        SonNode first;//记录第一个节点

        public Node(T data) {
            this.data = data;
            this.first = null;
        }

        public String toString() {
            if (first != null)
            {
                return "TreeChild $Node[data" +
                        data + ",first" + first.pos + "]";
            } else
            {
                return "TreeChild $Node[data=" +
                        data + ",first=-1]";
            }
        }
    }


    private final int default_tree_size = 100;
    private int treeSize = 0;
    private Node<E>[] nodes;//使用数组来记录该树里的所有节点
    private int nodeNums;//记录节点数

    public TreeChild(E data) {//以指定根节点创建树
        treeSize = default_tree_size;
        nodes = new Node[treeSize];
        nodes[0] = new Node<E>(data);
        nodeNums++;
    }

    public TreeChild(E data, int treeSize) {//以指定根节点,指定treeSize
        this.treeSize = treeSize;
        nodes = new Node[treeSize];
        nodes[0] = new Node<E>(data);
        nodeNums++;
    }

    public void addNode(E data, Node parent) {
        for (int i = 0; i < treeSize; i++) {
            //找到数组中第一个为null的元素,该元素保存新节点
            if (nodes[i] == null) {
                //创建新节点,并用指定数组元素来保存它
                nodes[i] = new Node(data);
                if (parent.first == null) {
                    parent.first = new SonNode(i, null);
                } else {
                    SonNode next = parent.first;
                    while (next.next != null) {
                        next = next.next;
                    }
                    next.next = new SonNode(i, null);
                }
                nodeNums++;
                return;
            }
        }
        throw new RuntimeException("该树已满,无法添加新节点");
    }

    public boolean empty()
    {
        return nodes[0] == null;
    }

    public Node<E> root()
    {
        //返回根节点
        return nodes[0];
    }

    public List<Node<E>> children(Node parent)
    {
        List<Node<E>> list = new ArrayList<Node<E>>();
        SonNode next = parent.first;//获取parent节点的第一个节点
        while (next != null)//沿着孩子链不断搜索下一个子节点
        {
            //添加新节点
            list.add(nodes[next.pos]);
            next = next.next;
        }
        return list;
    }

    public Node<E> child(Node parent,int index)
    {//返回指定节点的第index个子节点
        //获取parent节点的第一个子节点
        SonNode next=parent.first;
        //沿着孩子链不断搜索下一个节点
        for(int i=0;next!=null;i++){
            if(index==i)
                return nodes[next.pos];
            next=next.next;
        }
        return null;
    }
    public int deep()
    {//返回该树的深度
        return deep(root());
    }
    private int deep(Node node)
    {
        if(node.first==null)
            return 1;
        else
        {
            int max=0;
            SonNode next=node.first;
            //沿着孩子链不断搜索下一个节点
            while(next!=null)
            {
                int tmp=deep(nodes[next.pos]);
                if(tmp>max)
                    max=tmp;
                next=next.next;
            }
            return max+1;//返回其所有子树的最大深度加1.
        }
    }
    public int pos(Node node)
    {//返回包含指定值的节点
        for(int i=0;i<treeSize;i++)
        {
            //找到指定节点
            if(nodes[i]==node)
                return i;
        }
        return -1;
    }

    public static void main(String[] args)
    {
        TreeChild<String> t=new TreeChild<String>("root");
        TreeChild.Node root=t.root();
        System.out.println("根节点:"+root);
        t.addNode("节点一",root);
        t.addNode("节点二",root);
        System.out.println("添加子节点后的根节点:"+root);
        

    }



}

 

5:子节点链表示法的特点:

每个节点都可以快速找到它的所有子节点。但如果要找到某个父节点则比较麻烦,程序要遍历整个节点数组。

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值