数据结构c#语言实现

数据结构(c#语言实现)

前言

树是一种非线性数据结构,存在一对多的关系,本章重点讨论二叉树的性质、存储结构、遍历算法以及线索二叉树的定义和相关操作的实现


1.1 树的定义与基本术语

1.1.1 树的定义

  • 递归形式的定义:树是由n个节点组成的有限集合

1.1.2 基本术语

  1. 结点——表示树集合中的一个数据结构。

  2. 子结点与父结点——一个结点的前一个结点称为该结点的父结点,该结点的后一个结点成为子结点。

  3. 兄弟结点——同一个父结点的子结点间是兄弟关系。

  4. 祖先结点与后代结点——结点n的所有子结点以及子结点的子结点构成结点n的后代结点;从根结点到结点n经历的所有结点构成n的祖先结点。

  5. (1)结点的度——结点所拥有子树的根数
    (2)树的度——树中各结点度的最大值

  6. 叶子结点——度为0的结点,又称终端结点

  7. ——若结点M是结点N的父结点,用一条线将这两个连接起来形成的一条分支

  8. 路径与路径长度——若N1,N2,…,Nk是树中的结点构成的一个序列,且相邻两个结点都是树的边,则称该序列为从N1到Nk的一条路径;路径上边的数目称为路径长度

  9. 结点层次与根的深度——根结点层次为0,子结点层次依次递增,兄弟结点层次相同,结点的最大层次深度称为根的深度

  10. 树的五种基本形态
    树的基本形态

1.2 二叉树

1.2.1 二叉树的定义

  • 递归形式的定义:二叉树是由n个结点组成的有限集合,树包括次序且树的度为 2 。

1.2.2 二叉树的性质

  1. 性质一
  • 度为m的树至少有m+1个结点
  1. 性质二
  • 二叉树第i层的结点数目最多为2的i次方
  1. 性质三
  • 在深度为k的二叉树中,最多有(2的k+1次方-1)个结点
  1. 性质四
  • 含有n个结点的m次树,n=n0+n1+…+nm,所有结点度之和=n1+2n2+…+mnm
  1. 性质五
  • 对于含n个结点的树,无论度为多少,其所有结点度之和均为n-1
  1. 性质六
  • 如果一棵完全二叉树有n个结点,则其深度为k=log2n
  1. 性质七
  • (1)满二叉树一定是完全二叉树;完全二叉树只有最下面一层不满;
  • (2)完全二叉树最下面一层结点都集中于最左边的若干位置上
  • (3)完全二叉树最多只有最下面两层结点的度可以小于2
  1. 性质八
    若将一棵具有n个结点的完全二叉树按顺序表示,编号为i的结点,有如下规律:
  • (1)若i=0,则结点i为根结点;若i≠0,则结点i的双亲是编号为(i-1) /2(取整)的结点。
  • (2)若2i+1≤n-1,则i的左孩子是编号为2i+1的结点;若2i+1>n-1 ,则i无左孩子。
  • (3)若2i+2≤n-1,则i的右孩子是编号为2i+2的结点;若2i+2>n-1,则i无右孩子。

1.2.3 二叉树的链式存储结构

1. 二叉树的结点类

(1) 存储方法
数据域data,表示结点的数据元素;
左链域left,指向该结点的左子结点;
右链域right,指向该结点的右子结点。
(2) 代码实现

using System;
using System.Collections.Generic;

namespace CSharpEight
{
    public class BinaryTreeNode<T>
    {
        private T data;//据域
        private BinaryTreeNode<T> left, right;//左链域、右链域
        //构造缺省值的函数
        public BinaryTreeNode()
        {
            left = right = null;
        }
        //构造含一个参数的函数
        public BinaryTreeNode(T d)
        {
            data = d;
            left = right = null;
        }
        //获取或设置据域
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
        //获取或设置左链域
        public BinaryTreeNode<T> Left
        {
            get { return left; }
            set { left = value; }
        }
        //获取或设置右链域
        public BinaryTreeNode<T> Right
        {
            get { return right; }
            set { right = value; }
        }
    }
}

2. 二叉树类

public class BinaryTree<T>
    {
        protected BinaryTreeNode<T> root;//指向二叉树的根节点
        //获取或设置根节点
        public BinaryTreeNode<T> Root
        {
            get { return root; }
            set { root = value; }
        }
        //构造缺省值的函数
        public BinaryTree()
        {
            root = null;
        }
    }

1.2.4 二叉树的遍历

1. 按递归方法

(1)遍历
先根次序:访问根结点,遍历左子树,遍历右子树。
中根次序:遍历左子树,访问根结点,遍历右子树。
后根次序:遍历左子树,遍历右子树,访问根结点。
(2)代码实现

在Node类添加

//遍历的递归算法
        //先根次序
        public void ShowPreOrder()
        {
            Console.Write(this.Data + " ");//输出根
            BinaryTreeNode<T> q = this.left;//从左链域开始
            if (q != null)
                q.ShowPreOrder();//左链域不为空,递归调用
            q = this.Right;//左链域为空,从右链域开始
            if (q != null)
                q.ShowPreOrder();//右链域不为空,递归调用
        }
        //存放值于表中
        public void TraversalPreOrder(IList<T>sql)
        {
            sql.Add(this.Data);//将根存于表中
            BinaryTreeNode<T> q = this.left;//从左链域开始
            if (q != null)
                q.TraversalPreOrder(sql);//左链域不为空,递归调用
            q = this.Right;//左链域为空,从右链域开始
            if (q != null)
                q.TraversalPreOrder(sql);//右链域不为空,递归调用
        }
        //中根次序
        public void ShowInOrder()
        {
            BinaryTreeNode<T> q = this.left;//从左链域开始
            if (q != null)
                q.ShowInOrder();//左链域不为空,递归调用
            Console.Write(this.Data + " ");//输出根
            q = this.Right;//左链域为空,从右链域开始
            if (q != null)
                q.ShowInOrder();//右链域不为空,递归调用
        }
        //存放值于表中
        public void TraversalInOrder(IList<T> sql)
        {
            BinaryTreeNode<T> q = this.left;//从左链域开始
            if (q != null)
                q.TraversalInOrder(sql);//左链域不为空,递归调用
            sql.Add(this.Data);//将根存于表中
            q = this.Right;//左链域为空,从右链域开始
            if (q != null)
                q.TraversalInOrder(sql);//右链域不为空,递归调用
        }
        //后根次序
        public void ShowPostOrder()
        {
            BinaryTreeNode<T> q = this.left;//从左链域开始
            if (q != null)
                q.ShowPostOrder();//左链域不为空,递归调用
            q = this.Right;//左链域为空,从右链域开始
            if (q != null)
                q.ShowPostOrder();//右链域不为空,递归调用
            Console.Write(this.Data + " ");//输出根
        }
        //存放值于表中
        public void TraversalPostOrder(IList<T> sql)
        {
            BinaryTreeNode<T> q = this.left;//从左链域开始
            if (q != null)
                q.TraversalPostOrder(sql);//左链域不为空,递归调用
            q = this.Right;//左链域为空,从右链域开始
            if (q != null)
                q.TraversalPostOrder(sql);//右链域不为空,递归调用
            sql.Add(this.Data);//将根存于表中
        }

在Tree类添加

/*按先根次序遍历二叉树
            (1)访问当前结点;
            (2)若当前结点的左子树不空,则沿着left链进入该结点的左子树进行遍历。
            (3)若当前结点的右子树不空,则沿着right链进入该结点的右子树进行遍历。
        */
        public void ShowPreOrder()
        {
            Console.Write("先根次序:");
            if (root != null)
                root.ShowPreOrder();
            Console.WriteLine();
        }
        public List<T> TraversalPreOrder()
        {
            List<T> sql = new List<T>();
            if (root != null)
                root.TraversalPreOrder(sql);
            return sql;
        }
        //按中根次序遍历二叉树
        public void ShowInOrder()
        {
            Console.Write("中根次序:");
            if (root != null)
                root.ShowInOrder();
            Console.WriteLine();
        }
        public List<T> TraversalInOrder()
        {
            List<T> sql = new List<T>();
            if (root != null)
                root.TraversalInOrder(sql);
            return sql;
        }
        //按后根次序遍历二叉树
        public void ShowPostOrder()
        {
            Console.Write("后根次序:");
            if (root != null)
                root.ShowPostOrder();
            Console.WriteLine();
        }
        public List<T> TraversalPostOrder()
        {
            List<T> sql = new List<T>();
            if (root != null)
                root.TraversalPostOrder(sql);
            return sql;
        }

2. 非递归方法

(1)设置一个栈s来记录经过的路径
变量p从根结点开始,如果p不空或栈s不空时,循环执行以下操作,直到扫描完二叉树且栈为空。

  • 如果p不空,表示扫描到一个结点,将p结点入栈(s.Push(p)),进入其左子树(p=p.Left)。
  • 如果p为空并且栈s不空,表示已走过一条路径,必须返回一步以寻找另一条路径。置p指向出栈的结点(p=s.Pop()),访问p结点,再进入p的右子树(p=p.Right) 。
    (2)代码实现
/*非递归中根次序遍历二叉树(利用栈保存路径)
            1.中根次序遍历规则: 在每个结点处,先选择遍历左子树,其后必须返回该结点,对其进行访问,然后遍历右子树。
            2.设置一个栈s来记录经过的路径。变量p从根结点开始,如果p不空或栈s不空时,循环执行以下操作,直到扫描完二叉树且栈为空。
              (1)如果p不空,表示扫描到一个结点,将p结点入栈(s.Push(p)),进入其左子树(p=p.Left)。
              (2)如果p为空并且栈s不空,表示已走过一条路径,必须返回一步以寻找另一条路径。
                 置p指向出栈的结点(p=s.Pop()),访问p结点,再进入p的右子树(p=p.Right)。
        */
        public void ShowInOrderNR()
        {
            Stack<BinaryTreeNode<T>> s = new Stack<BinaryTreeNode<T>>(100);
            BinaryTreeNode<T> p = root;
            Console.Write("非递归中根次序:");
            while (p != null || s.Count != 0)//p非空或栈非空时
            {
                if (p != null)
                {
                    s.Push(p);//p结点入栈
                    p = p.Left;//进入左子树
                }
                else//p为空且栈非空时
                {
                    p = s.Pop();//p指向出栈的结点
                    Console.Write(p.Data + " ");//访问结点
                    p = p.Right;//进入右子树
                }
            }
            Console.WriteLine();
        }

3. 按层次方法

(1)设置一个队列变量q
结点变量p从根开始,当p不为空时,循环顺序执行以下操作:

  • 访问p结点。
  • 如果p的left链不空,将p结点的左孩子加入队列q(入队操作q.Enqueue(p.Left))。
  • 如果p的right链不空,将p结点的右孩子加入队列q(入队操作q.Enqueue(p.Right))。
  • 如果队列q为非空,设置p指向队列q出队的结点(p=Dequeue()),否则置p为null。
    (2)代码实现

在Tree类添加

/*按层次遍历二叉树(利用队列辅助输出)
        设置一个队列变量q。结点变量p从根开始,当p不为空时,循环顺序执行以下操作:
            (1)访问p结点。
            (2)如果p的left链不空,将p结点的左孩子加入队列q(入队操作q.Enqueue(p.Left))。
            (3)如果p的right链不空,将p结点的右孩子加入队列q(入队操作q.Enqueue(p.Right))。
            (4)如果队列q为非空,设置p指向队列q出队的结点(p=Dequeue()),否则置p为null。
        */
        public void ShowByLevel()
        {
            Queue<BinaryTreeNode<T>> q = new Queue<BinaryTreeNode<T>>(100);
            BinaryTreeNode<T> p = root;
            Console.Write("层次遍历:");
            while (p != null)
            {
                Console.Write(p.Data + " ");
                if (p.Left != null)
                    q.Enqueue(p.Left);//p的左子节点入队
                if (p.Right != null)
                    q.Enqueue(p.Right);//p的右子节点入队
                if (q.Count != 0)
                    p = q.Dequeue();//队列不空,p指向出队的结点
                else
                    p = null;//当队列为空,p置为空
            }
            Console.WriteLine();
        }

1.2.5 二叉树的构建

1. 建立链式结构的二叉树

(1)方法
对于一棵已经顺序存储的完全二叉树,由二叉树的性质五可知,第0个结点为根结点,第i个结点的左孩子为第2i+1个结点,右孩子为第2i+2个结点。
(2)代码实现

在Tree类添加

//构建链式存储的二叉树
        public static BinaryTree<T> ByOneList(IList<T> t)
        {
            int n = t.Count;
            BinaryTree<T> bt = new BinaryTree<T>();
            if (n == 0)
            {
                bt.Root = null;
                return bt;
            }
            int i, j;
            BinaryTreeNode<T>[] q = new BinaryTreeNode<T>[n];
            T v;
            for (i = 0; i < n; i++)
            {
                v = t[i];
                q[i] = new BinaryTreeNode<T>(v);
            }
            for (i = 0; i < n; i++)
            {
                j = 2 * i + 1;
                if (j < n)
                    q[i].Left = q[j];
                else
                    q[i].Left = null;
                j++;
                if (j < n)
                    q[i].Right = q[j];
                else
                    q[i].Right = null;
            }
            bt.Root = q[0];
            return bt;
        }

2. 根据广义表表达式建立二叉树

广义表形式有时不能唯一表示一棵二叉树,原因在于无法明确左右子树。例如,广义表A(B)没有表达出结点B是结点A的左子结点还是右子结点。为了唯一表示一棵二叉树,必须重新定义广义表的形式。
在广义表中,除数据元素外还定义四个边界符号:

  • 1.空子树符NullSubtreeFlag,如 ‘^’,以标明非叶子结点的空子树。
  • 2.左界符LeftDelimitFlag,如‘(’,以标明下一层次的左边界;
  • 3.右界符RightDelimitFlag,如‘)’,以标明下一层次的右边界。
  • 4.中界符MiddleDelimitFlag,如‘,’,以标明某一层次的左右子树的分界。

依次读取二叉树的广义表表示序列中的每个符号元素,检查其内容,如果

  • 遇到有效数据值,则建立一个二叉树结点对象;扫描下一元素,如果
    它为LeftDelimitFlag,则LeftDelimitFlag和RightDelimitFlag之间是该结点的左子树与右子树,递归调用,分别建立左、右子树,返回结点对象。
    没有遇到LeftDelimitFlag,表示该结点是叶子结点。
  • 遇到NullSubtreeFlag,表示空子树,返回null值。
//广义表的分界符
    public struct ListFlagsStruc<T>
    {
        public T NullSubtree;//空子树符
        public T LeftDelimit;//左界符
        public T RightDelimit;//右界符
        public T MiddleDelimit;//中界符
    }
    public static ListFlagsStruc<T> ListFlags;//记录广义表的分界符
    /*根据广义表表达式建立二叉树
        依次读取二叉树的广义表表示序列中的每个符号元素,检查其内容,
        (1)如果遇到有效数据值,则建立一个二叉树结点对象;扫描下一元素。
        (2)如果它为LeftDelimitFlag,则LeftDelimitFlag和RightDelimitFlag之间是该结点的左子树与右子树,递归调用,分别建立左、右子树,返回结点对象。
        (3)没有遇到LeftDelimitFlag,表示该结点是叶子结点。
        (4)遇到NullSubtreeFlag,表示空子树,返回null值。
        */
        public static BinaryTree<T> ByOneList(IList<T> sList, ListFlagsStruc<T> ListFlags)
        {
            BinaryTree<T>.ListFlags = ListFlags;
            BinaryTree<T>.idx = 0;//初始化递归变量
            BinaryTree<T> bt = new BinaryTree<T>();
            if (sList.Count > 0)
                bt.Root = RootByOneList(sList);
            else 
                bt.Root = null;
            return bt;
        }
        //建立树
        private static BinaryTreeNode<T> RootByOneList(IList<T> sList)
        {
            BinaryTreeNode<T> p = null;
            T nodeData = sList[idx];
            if (IsData(nodeData))
            {
                p = new BinaryTreeNode<T>(nodeData);//有效数据,建立节点
                idx++;
                nodeData = sList[idx];
                if (nodeData.Equals(ListFlags.LeftDelimit))
                {
                    idx++;//左边界,跳过
                    p.Left = RootByOneList(sList);//建立左子树,递归
                    idx++;//中界符,跳过
                    p.Right = RootByOneList(sList);//建立右子树,递归
                    idx++;//右边界,跳过
                }
            }
            if (nodeData.Equals(ListFlags.NullSubtree))
                idx++;//空子树,跳过
            return p;
        }
        //判断是否为有效数据
        private static bool IsData(T nodeValue)
        {
            if (nodeValue.Equals(ListFlags.NullSubtree))
                return false;
            if (nodeValue.Equals(ListFlags.LeftDelimit))
                return false;
            if (nodeValue.Equals(ListFlags.RightDelimit))
                return false;
            if (nodeValue.Equals(ListFlags.MiddleDelimit))
                return false;
            else
                return true;
        }

3. 根据先根和中根序列建立二叉树

设二叉树的先根及中根遍历序列分别为preList和inList 。

  • 确定根元素。由先根次序知,二叉树的根为preList[0]。查找它在inList中的位置k = inList.IndexOf(rootData);
  • 确定根的左子树的相关序列。由中根次序知, inList[k]之前的结点在根的左子树上, inList[k]之后的结点在根的右子树上。因此根的左子树由k个结点组成:
    先根序列——preList[1],…,preList [k]。
    中根序列——inList[0] ,…,inList [k-1]。
  • 根据左子树的先根序列和中根序列建立左子树,递归。
  • 确定根的右子树的相关序列。右子树由n-k-1个结点组成:
    先根序列——preList [k+1],…,preList [n-1]。
    中根序列——inList [k+1] ,…,inList [n-1]。
  • 根据右子树的先根序列和中根序列建立右子树,递归。
/*根据先根和中根次序建立二叉树
        (1)确定根元素。由先根次序知,二叉树的根为preList[0]。查找它在inList中的位置k = inList.IndexOf(rootData);
        (2)确定根的左子树的相关序列。由中根次序知, inList[k]之前的结点在根的左子树上, inList[k]之后的结点在根的右子树上。因此根的左子树由k个结点组成:
            先根序列——preList[1],…,preList [k]。
            中根序列——inList[0] ,…,inList [k-1]。
        (3)根据左子树的先根序列和中根序列建立左子树,递归。
        (4)确定根的右子树的相关序列。右子树由n-k-1个结点组成:
            先根序列——preList [k+1],…,preList [n-1]。
            中根序列——inList [k+1] ,…,inList [n-1]。
        (5)根据右子树的先根序列和中根序列建立右子树,递归。
        */
        public static BinaryTree<T> ByTwoList(IList<T> preList, IList<T> inList)
        {
            BinaryTree<T> bt = new BinaryTree<T>();
            bt.Root = RoomByTwoList(preList, inList);
            return bt;
        }
        //建立树
        private static BinaryTreeNode<T> RoomByTwoList(IList<T> preList, IList<T> inList)
        {
            BinaryTreeNode<T> p = null;
            T rooData;
            int i, k, n;
            IList<T> presub = new List<T>();//先根序列
            IList<T> insub = new List<T>();//中根序列
            n = preList.Count;
            if (n > 0)
            {
                rooData = preList[0];//当前根节点
                p = new BinaryTreeNode<T>(rooData);
                k = inList.IndexOf(rooData);
                Console.WriteLine("\t current root = " + rooData + "\t k = " + k);
                for (i = 0; i < k; i++)//准备当前根节点的左子树先根序列
                    presub.Add(preList[i + 1]);
                for (i = 0; i < k; i++)//准备当前根节点的左子树中根序列
                    insub.Add(inList[i]);
                p.Left = RoomByTwoList(presub, insub);//建立当前根节点的左子树,递归
                presub.Clear();
                for (i = 0; i < n - k - 1; i++)//准备当前根节点的右子树先根序列
                    presub.Add(preList[k + i + 1]);
                insub.Clear();
                for (i = 0; i < n - k - 1; i++)//准备当前根节点的右子树中根序列
                    insub.Add(inList[k + i + 1]);
                p.Right = RoomByTwoList(presub, insub);//建立当前根节点的右子树,递归
            }
            return p;
        }

1.2.6 线索二叉树

1. 线索二叉树的实现

  • n个结点的二叉树,总共有2n个链,仅需要n-1个链来指明各结点间的关系,其余n+1个链均为空值。利用空链作线索来指明结点在某种遍历次序下的前驱和后继结点,构成线索二叉树。对二叉树以某种次序进行遍历并加上线索的过程称为线索化。
  • 线索二叉树中,原先非空的链保持不变,仍然指向该结点的左、右子结点,它记录的是结点间的层次关系。原先空的左链用来指向遍历中该结点的前驱结点,原先空的右链指向后继结点,它记录的是结点间在遍历时的顺序关系。为了区别每条链是否是一个线索,可以在二叉树的结点结构中设置两个状态字段lefttag和righttag,用以标记相应链的状态。

2. 二叉树的中序线索化

设p指向一棵二叉树的某个结点,front指向p的前驱结点,它的初值为null。当p非空时,执行以下操作:

  • 中序线索化p结点的左子树。
  • 如果p的左子树为空,设置p的lefttag标记为true,它的left链为指向前驱结点front的线索。
  • 如果p的右子树为空,设置p的righttag标记为true
  • 如果前驱结点front非空并且它的右链为线索,设置front的right链为指向p的线索。
  • 移动front,使front指向p。
  • 中序线索化p结点的右子树。
  • 如果一开始让p指向二叉树的根结点root,则上述过程线索化整个二叉树。

3. 线索二叉树的遍历

(1)中序线索二叉树中查找中根次序的后继结点

设p指向当前结点,执行以下操作:

  • 如果p结点的右子树为线索,则p的right链为其后继结点,设置p为该结点。
  • 否则说明p的右子树为非空,则p的后继结点是p的右子树上第一个中序访问的结点,即p的右孩子的最左边的子孙结点,设置p为该结点。
  • 返回p,作为当前结点在中根次序下的后继结点。
(2) 中序线索二叉树的中根次序遍历
  • 寻找第一个访问结点。它是根的左子树上最左边的子孙结点,用p指向该结点。
  • 访问p结点。
  • 找到p的后继结点,用p指向该结点,跳转到上一步,直至p为null,说明已访问了序列的最后一个结点。
(3)先根次序遍历
  • (1)寻找第一个访问结点。它是根结点,用p指向该结点
  • (2)访问p结点。
  • (3)找到p的后继结点,用p指向该结点,跳转到上一步,直至p为null,说明已访问了序列的最后一个结点。

结点类

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpEight
{
    public class ThreadBinaryTreeNode<T>
    {
        private T data;//数据元素
        private ThreadBinaryTreeNode<T> left, right;//指向左右结点的链
        private bool lefttag;//左线索标志
        private bool righttag;//右线索标志
        //构造缺省值的函数
        public ThreadBinaryTreeNode()
        {
            left = right = null;
            lefttag = righttag = false;
        }
        //构造有值结点
        public ThreadBinaryTreeNode(T d)
        {
            data = d;
            left = right = null;
            lefttag = righttag = false;
        }
        //设置或获取左链
        public ThreadBinaryTreeNode<T> Left
        {
            get { return left; }
            set { left = value; }
        }
        //设置或获取右链
        public ThreadBinaryTreeNode<T> Right
        {
            get { return right; }
            set { right = value; }
        }
        //设置或获取数据元素
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
        //设置或获取左线索
        public bool LeftTag
        {
            get { return lefttag; }
            set { lefttag = value; }
        }
        //设置或获取右线索
        public bool RightTag
        {
            get { return righttag; }
            set { righttag = value; }
        }
        //查找中根次序的后继节点
        /*
         设p指向当前结点,执行以下操作:
         (1)如果p结点的右子树为线索,则p的right链为其后继结点,设置p为该结点。
         (2)否则说明p的右子树为非空,则p的后继结点是p的右子树上第一个中序访问的结点,即p的右孩子的最左边的子孙结点,设置p为该结点。
         (3)返回p,作为当前结点在中根次序下的后继结点。
        */
        public ThreadBinaryTreeNode<T> NextNodeInOrder()
        {
            ThreadBinaryTreeNode<T> p = this;
            if (p.RightTag) //右子树为空时
                p = p.Right;  //right指向后继结点
            else//右子树非空时
            {
                p = p.Right; //进入右子树
                while (!p.LeftTag)//找到最左边的子孙结点
                    p = p.Left;
            }
            return p;
        }
        //查找先根次序的后继节点
        /*
         设p指向当前结点,执行以下操作:
         (1)如果p结点的左子树为非空,则p的左孩子为其后继结点,设置p为该结点。
         (2)否则说明p的左子树为空,如果p的右孩子为非空,则p的后继结点是p的右孩子,设置p为该结点。
         (3)如果p结点的左、右子树均为空,p为叶子结点,则p的后继节点为它的中序线索祖先的右孩子,沿着右线索可以找到它,设置p为该结点。
         (4)返回p,作为当前结点在中根次序下的后继结点。
        */
        public ThreadBinaryTreeNode<T> NextNodePreOrder()
        {
            ThreadBinaryTreeNode<T> p = this;
            if (!p.LeftTag) //左子树为非空时
                p = p.Left;  //p的左孩子为其后继结点,设置p为该结点。
            else//右子树非空时
            {
                if (!p.RightTag) //右子树为非空时
                    p = p.Right;  //p的右孩子为其后继结点,设置p为该结点。
                else
                {
                    while (p.RightTag && p.Right != null)//叶子结点,后继节点为它的中序线索祖先的右孩子。
                        p = p.Right;//进入右子树
                    p = p.Right;
                }
            }
            return p;
        }
    }
}

Tree类

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpEight
{
    public class ThreadBinaryTree<T>
    {
        private ThreadBinaryTreeNode<T> front = null;
        private ThreadBinaryTreeNode<T> root;//指向二叉树的根节点
        //设置或获取根节点
        public ThreadBinaryTreeNode<T> Root
        {
            get { return root; }
            set { root = value; }
        }
        //构造空二叉树
        public ThreadBinaryTree()
        {
            root = null;
        }
        //中序线索化以p节点为根的子树
        /*
          设p指向一棵二叉树的某个结点,front指向p的前驱结点,它的初值为null。
          当p非空时,执行以下操作:
          (1)中序线索化p结点的左子树。
          (2)如果p的左子树为空,设置p的lefttag标记为true,它的left链为指向前驱结点front的线索。
          (3)如果p的右子树为空,设置p的righttag标记为true
          (4)如果前驱结点front非空并且它的右链为线索,设置front的right链为指向p的线索。
          (5)移动front,使front指向p。
          (6)中序线索化p结点的右子树。
        */
        private void SetThreadInOrder(ThreadBinaryTreeNode<T> p)
        {
            if (p != null)
            {
                SetThreadInOrder(p.Left);//中序线索化p的左子树
                if (p.Left == null)//p的左子树为空,设置p.left为线索,指向front
                {
                    p.LeftTag = true;
                    p.Left = front;
                }
                if (p.Right == null)//p的右子树为空,设置p.right为线索
                {
                    p.RightTag = true;
                }
                if (front != null && front.RightTag)//前驱结点front非空并且它的右链为线索,设置front的right链为指向p的线索。
                    front.Right = p;
                front = p;//移动front,使front指向p。
                SetThreadInOrder(p.Right);//中序线索化p结点的右子树。
            }
        }
        //中序线索化二叉树
        public void SetThreadInOrder()
        {
            front = null;
            SetThreadInOrder(root);
        }
        //中根次序遍历
        /*
         (1)寻找第一个访问结点。它是根的左子树上最左边的子孙结点,用p指向该结点。
         (2)访问p结点。
         (3)找到p的后继结点,用p指向该结点,跳转到上一步,直至p为null,说明已访问了序列的最后一个结点。
        */
        public void ShowUsingThreadInOrder()
        {
            ThreadBinaryTreeNode<T> p = root;
            if (p != null)
            {
                Console.Write("中根次序:  ");
                while (!p.LeftTag)
                    p = p.Left; //找到根的最左边子孙结点
                do
                {
                    Console.Write(p.Data + " ");
                    p = p.NextNodeInOrder();//返回p的后继结点
                }
                while (p != null);
                Console.WriteLine();
            }
        }
        //先根次序遍历
        /*
         (1)寻找第一个访问结点。它是根结点,用p指向该结点。
         (2)访问p结点。
         (3)找到p的后继结点,用p指向该结点,跳转到上一步,直至p为null,说明已访问了序列的最后一个结点。
        */
        public void ShowUsingThreadPreOrder()
        {
            ThreadBinaryTreeNode<T> p = root;
            if (p != null)
            {
                Console.Write("先根次序:  ");
                do
                {
                    Console.Write(p.Data + " ");
                    p = p.NextNodePreOrder();//返回p的后继结点
                }
                while (p != null);
                Console.WriteLine();
            }
        }
        //测试函数
        public static void Test()
        {
            ThreadBinaryTree<int> tbt = new ThreadBinaryTree<int>();//建立二叉树
            ThreadBinaryTreeNode<int>[] nodes = new ThreadBinaryTreeNode<int>[9];
            for (int i = 0; i < 9; i++)
                nodes[i] = new ThreadBinaryTreeNode<int>(i);//建立二叉树的节点
            tbt.Root = nodes[1];//设置二叉树的节点结构
            nodes[1].Left = nodes[2]; nodes[1].Right = nodes[3];
            nodes[2].Left = nodes[4];
            nodes[3].Left = nodes[5]; nodes[3].Right = nodes[6];
            nodes[4].Right = nodes[7]; nodes[6].Left = nodes[8];
            tbt.SetThreadInOrder();//中序线索化二叉树
            tbt.ShowUsingThreadInOrder();//中根次序遍历中序线索二叉树
            tbt.ShowUsingThreadPreOrder();//先根次序遍历中序线索二叉树
        }
    }
}

总结

以上就是今天要讲的内容,本章介绍树,希望大家有所收获,感谢!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章绪论...1 1.1 数据构...................1 1.1.1 学习数据结构的必要性...1 1.1.2 基本概念和术语...............1 1.2 算法...4 1.2.1算法的特性............................4 1.2.2算法的评价标准....................5 1.2.3算法的时间复杂度................6 1.3 数学预备知识...........................7 1.3.1 集合...................................7 1.3.2 常用的数学术语...............8 1.3.3 对数...................................8 1.3.4 递归...................................9 1.4 C#预备知识.............................10 1.4.1 接口.................................10 1.4.2 泛型编程.........................13 本章小结........20 习题一............20 第2章线性表.....................................22 2.1 线性表的逻辑结构.........................22 2.1.1 线性表的定义.....................22 2.1.2 线性表的基本操作.............22 2.2 顺序表.....24 2.2.1 顺序表的定义.....................24 2.2.2 顺序表的基本操作实现.....29 2.2.3 顺序表应用举例.................35 2.3 单链表.....38 2.3.1 单链表的定义.....................39 2.3.2 单链表的基本操作实现.....46 2.3.3 单链表应用举例.................56 2.4 其他链表.61 2.4.1 双向链表.............................61 2.4.2循环链表..............................64 2.5 C#中的线性表.................................64 本章小结........67 习题二............67 第3章栈和队列.................................69 3.1 栈............69 3.1.1 栈的定义及基本运算.........69 3.1.2 栈的存储和运算实现.........70 3.1.3 栈的应用举例.....................82 3.1.4 C#中的栈.............................87 3.2 队列........87 3.2.1队列的定义及基本运算......87 数据结构C#语言版) 目录 II 3.2.2 队列的存储和运算实现.....89 3.2.3 队列的应用举例...............103 3.2.4 C# 中的队列.....................104 本章小结.......105 习题三..........105 第4章串和数组...............................106 4.1 串..........106 4.1.1 串的基本概念...................106 4.1.2 串的存储及类定义...........106 4.1.3 串的基本操作的实现.......111 4.1.4 C#中的串...........................115 4.2 数组.......117 4.2.1 数组的逻辑结构...............117 4.2.2 数组的内存映象...............118 4.2.3 C#中的数组.......................119 本章小结.......121 习题四..........121 第5章树和二叉树...........................123 5.1 树..........123 5.1.1 树的定义...........................123 5.1.2 树的相关术语...................124 5.1.3 树的逻辑表示...................125 5.1.4 树的基本操作...................126 5.2 二叉树...126 5.2.1 二叉树的定义...................127 5.2.2 二叉树的性质...................128 5.2.3 二叉树的存储结构...........129 5.2.4二叉链表存储结构的类实现....................................132 5.2.5 二叉树的遍历...................137 5.3 树与森林.......................................141 5.3.2 树、森林与二叉树的转换.......................................144 5.3.3 树和森林的遍历...............147 5.4哈夫曼树147 5.4.1哈夫曼树的基本概念........147 5.4.2哈夫曼树类的实现............149 5.4.3哈夫曼编码........................153 5.5 应用举例...............................154 5.6 C#中的树...............................157 本章小结.......158 习题五..........159 第6章图...161 6.1 图的基本概念................................161 6.1.1 图的定义.............................161 6.1.2 图的基本术语...................161 数据结构C#语言版) 目录 III 6.1.3 图的基本操作...................165 6.2 图的存储结构...............................166 6.2.1邻接矩阵............................167 6.2.2 邻接表...............................172 6.3 图的遍历.......................................185 6.3.1 深度优先遍历...................185 6.3.2 广度优先遍历...................188 6.4 图的应用.......................................189 6.4.1 最小生成树.......................189 6.4.2 最短路径...........................199 6.4.3 拓扑排序...........................207 本章小结.......210 习题六..........210 第7章排序.......................................213 7.1 基本概念.......................................213 7.2 简单排序方法...............................214 7.2.1 直接插入排序...................214 7.2.2 冒泡排序...........................216 7.2.3 简单选择排序...................217 7.3 快速排序.......................................219 7.4 堆排序...222 7.5 归并排序.......................................230 7.6 基数排序.......................................232 7.6.1 多关键码排序...................232 7.6.2 链式基数排序...................233 7.7 各种排序方法的比较与讨论.......235 7.8 C#中排序方法...............................235 本章小结.......236 习题七..........236 第8章查找.......................................238 8.1 基本概念和术语............................238 8.2 静态查找表...................................238 8.2.1 顺序查找...........................238 8.2.2 有序表的折半查找...........239 8.2.3 索引查找...........................242 8.3 动态查找表...................................243 8.4 哈希表...252 8.4.1 哈希表的基本概念...........252 8.4.2 常用的哈希函数构造方法.......................................253 8.4.3 处理冲突的方法...............254 8.5 C#中的查找方法...........................256 本章小结.......256 习题八..........256 参考文献..............257

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值