对比上一篇文章“顺序存储二叉树”,链式存储二叉树的优点是节省空间。

二叉树的性质:

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

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

3、对任何一棵二叉树T,如果其终结点数为n0,度为2的节点数为n2,则n0=n2+1。

4、具有n个节点的完全二叉树的深度为log2n+1。

5、对于一棵有n个节点的完全二叉树的节点按层序编号,若完全二叉树中的某节点编号为i,则若有左孩子编号为2i,若有右孩子编号为2i+1,母亲节点为i/2。

在此记录下链式二叉树的实现方式 :

/// <summary>
/// 树节点
/// </summary>
/// <typeparam name="T"></typeparam>
public class TreeNode<T>
{
/// <summary>
/// 节点数据
/// </summary>
public T data { get; set; }
/// <summary>
/// 左节点
/// </summary>
public TreeNode<T> leftChild { get; set; }
/// <summary>
/// 右节点
/// </summary>
public TreeNode<T> rightChild { get; set; } public TreeNode()
{
data = default(T);
leftChild = null;
rightChild = null;
} public TreeNode(T item)
{
data = item;
leftChild = null;
rightChild = null;
}
}
    /// <summary>
/// 二叉树 链表存储结构
/// </summary>
/// <typeparam name="T"></typeparam>
public class LinkStorageBinaryTree<T>
{
/// <summary>
/// 树根节
/// </summary>
private TreeNode<T> head { get; set; } public LinkStorageBinaryTree()
{
head = null;
} public LinkStorageBinaryTree(T val)
{
head = new TreeNode<T>(val);
}
/// <summary>
/// 获取左节点
/// </summary>
/// <param name="treeNode"></param>
/// <returns></returns>
public TreeNode<T> GetLeftNode(TreeNode<T> treeNode)
{
if (treeNode == null)
return null;
return treeNode.leftChild;
}
/// <summary>
/// 获取右节点
/// </summary>
/// <param name="treeNode"></param>
/// <returns></returns>
public TreeNode<T> GetRightNode(TreeNode<T> treeNode)
{
if (treeNode == null)
return null;
return treeNode.rightChild;
}
/// <summary>
/// 获取根节点
/// </summary>
/// <returns></returns>
public TreeNode<T> GetRoot()
{
return head;
}
/// <summary>
/// 插入左节点
/// </summary>
/// <param name="val"></param>
/// <param name="node"></param>
/// <returns></returns>
public TreeNode<T> AddLeftNode(T val,TreeNode<T> node)
{
if (node == null)
throw new ArgumentNullException("参数错误");
TreeNode<T> treeNode = new TreeNode<T>(val);
TreeNode<T> childNode = node.leftChild;
treeNode.leftChild = childNode;
node.leftChild = treeNode;
return treeNode;
} /// <summary>
/// 插入右节点
/// </summary>
/// <param name="val"></param>
/// <param name="node"></param>
/// <returns></returns>
public TreeNode<T> AddRightNode(T val, TreeNode<T> node)
{
if (node == null)
throw new ArgumentNullException("参数错误");
TreeNode<T> treeNode = new TreeNode<T>(val);
TreeNode<T> childNode = node.rightChild;
treeNode.rightChild = childNode;
node.rightChild = treeNode;
return treeNode;
}
/// <summary>
/// 删除当前节点的 左节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public TreeNode<T> DeleteLeftNode(TreeNode<T> node)
{
if (node == null || node.leftChild == null)
throw new ArgumentNullException("参数错误");
TreeNode<T> leftChild = node.leftChild;
node.leftChild = null;
return leftChild;
} /// <summary>
/// 删除当前节点的 右节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public TreeNode<T> DeleteRightNode(TreeNode<T> node)
{
if (node == null || node.leftChild == null)
throw new ArgumentNullException("参数错误");
TreeNode<T> rightChild = node.rightChild;
node.rightChild = null;
return rightChild;
} /// <summary>
/// 先序遍历
/// </summary>
/// <param name="index"></param>
public void PreorderTraversal(TreeNode<T> node)
{
//递归的终止条件
if (head == null)
{
Console.WriteLine("当前树为空");
return;
}
if (node != null)
{
Console.Write(node.data+ " ");
PreorderTraversal(node.leftChild);
PreorderTraversal(node.rightChild);
}
} /// <summary>
/// 中序遍历
/// </summary>
/// <param name="index"></param>
public void MiddlePrefaceTraversal(TreeNode<T> node)
{
//递归的终止条件
if (head == null)
{
Console.WriteLine("当前树为空");
return;
}
if (node != null)
{
MiddlePrefaceTraversal(node.leftChild); Console.Write(node.data + " "); MiddlePrefaceTraversal(node.rightChild);
}
} /// <summary>
/// 后序遍历
/// </summary>
/// <param name="index"></param>
public void AfterwordTraversal(TreeNode<T> node)
{
//递归的终止条件
if (head == null)
{
Console.WriteLine("当前树为空");
return;
}
if (node != null)
{
AfterwordTraversal(node.leftChild);
AfterwordTraversal(node.rightChild);
Console.Write(node.data + " ");
}
} public void LevelTraversal()
{
if (head == null)
return;
//使用队列先入先出
Queue<TreeNode<T>> queue = new Queue<TreeNode<T>>();
queue.Enqueue(head); while (queue.Any())
{
TreeNode<T> item = queue.Dequeue();
Console.Write(item.data +" ");
if (item.leftChild != null)
queue.Enqueue(item.leftChild);
if (item.rightChild != null)
queue.Enqueue(item.rightChild);
}
}
/// <summary>
/// 校验节点是否是叶子节点
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public bool ValidLeafNode(TreeNode<T> node)
{
if (node == null)
throw new ArgumentNullException("参数错误");
if (node.leftChild != null && node.rightChild != null)
{
Console.WriteLine($"节点 {node.data} 不是叶子节点");
return false;
}
Console.WriteLine($"节点 {node.data} 是叶子节点");
return true;
}
}

遍历方式在顺序存储一文中已经用图表示过,在此不做重复说明。

现在测试下:

LinkStorageBinaryTree<string> linkStorageBinary = new LinkStorageBinaryTree<string>("A");
TreeNode<string> tree1 = linkStorageBinary.AddLeftNode("B", linkStorageBinary.GetRoot());
TreeNode<string> tree2 = linkStorageBinary.AddRightNode("C", linkStorageBinary.GetRoot());
TreeNode<string> tree3 =linkStorageBinary.AddLeftNode("D", tree1);
linkStorageBinary.AddRightNode("E",tree1);
linkStorageBinary.AddLeftNode("F", tree2);
linkStorageBinary.AddRightNode("G", tree2); //先序遍历
Console.Write("先序遍历:");
linkStorageBinary.PreorderTraversal(linkStorageBinary.GetRoot());
Console.WriteLine(); //中序遍历
Console.Write("中序遍历:");
linkStorageBinary.MiddlePrefaceTraversal(linkStorageBinary.GetRoot());
Console.WriteLine(); //中序遍历
Console.Write("后序遍历:");
linkStorageBinary.AfterwordTraversal(linkStorageBinary.GetRoot());
Console.WriteLine(); //层次遍历
Console.Write("层次遍历:");
linkStorageBinary.LevelTraversal(); linkStorageBinary.ValidLeafNode(tree1);
linkStorageBinary.ValidLeafNode(tree3);
Console.ReadKey();

输出:

先序遍历:A B D E C F G
中序遍历:D B E A F C G
后序遍历:D E B F G C A
层次遍历:A B C D E F G 节点 B 不是叶子节点
节点 D 是叶子节点

C#数据结构-二叉树-链式存储结构的更多相关文章

  1. javascript实现数据结构:线性表--线性链表(链式存储结构)

    上一节中, 线性表的顺序存储结构的特点是逻辑关系上相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,它的存储位置可用一个简单,直观的公式来表示.然后,另一方面来看,这个特点也造成这种存储 ...

  2. [置顶] ※数据结构※→☆线性表结构(queue)☆============优先队列 链式存储结构(queue priority list)(十二)

    优先队列(priority queue) 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除.在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有 ...

  3. c数据结构 -- 线性表之 复杂的链式存储结构

    复杂的链式存储结构 循环链表 定义:是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环) 优点:从表中任一节点出发均可找到表中其他结点 注意:涉及遍历操作时,终止条件是判断 ...

  4. c数据结构 -- 线性表之 顺序存储结构 于 链式存储结构 (单链表)

    线性表 定义:线性表是具有相同特性的数据元素的一个有限序列 类型: 1:顺序存储结构 定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构 算法: #include <stdio. ...

  5. C++编程练习(6)----“实现简单的队列的链式存储结构“

    队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出.简称链队列. 实现代码如下: /* LinkQueue.h 头文件 */ #include<iostream> #defi ...

  6. C++编程练习(4)----“实现简单的栈的链式存储结构“

    如果栈的使用过程中元素数目变化不可预测,有时很小,有时很大,则最好使用链栈:反之,如果它的变化在可控范围内,使用顺序栈会好一些. 简单的栈的链式存储结构代码如下: /*LinkStack.h*/ #i ...

  7. C++编程练习(2)----“实现简单的线性表的链式存储结构“

    单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素. 对于查找操作,单链表的时间复杂度为O(n). 对于插入和删除操作,单链表在确定位置后,插入和删除时间仅为O(1). 单链表不需要分配存储 ...

  8. java资料——顺序存储结构和链式存储结构(转)

    顺序存储结构 主要优点 节省存储空间,随机存取表中元素 缺    点 插入和删除操作需要移动元素 在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构. 顺序存储结 ...

  9. C++线性表的链式存储结构

    C++实现线性表的链式存储结构: 为了解决顺序存储不足:用线性表另外一种结构-链式存储.在顺序存储结构(数组描述)中,元素的地址是由数学公式决定的,而在链式储存结构中,元素的地址是随机分布的,每个元素 ...

随机推荐

  1. 8、Django之模型层第三篇:更多字段与参数

    1 ORM字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为id的列. IntegerField 一个整数类型 ...

  2. Mybatis的缓存——一级缓存和源码分析

    目录 什么是缓存? 一级缓存 测试一. 测试二. 总结: 一级缓存源码分析: 1. 一级缓存到底是什么? 得出结论: 2. 一级缓存什么时候被创建? 3. 一级缓存的执行流程 结论: 一级缓存源码分析 ...

  3. pc端兼容IE9及以上版本

    最近业务部门反映我们商城的兼容性不是很好,尤其是在IE浏览器上,经过调研,我们决定对IE9及以上版本的IE内核浏览器进行主流程测试,发现有哪些功能在IE9上不兼容 一.CSS兼容性 如下图所示 使用了 ...

  4. Pytest学习(七) - skip、skipif的使用

    前言 作为一个java党,我还是觉得pytest和testng很像,有时候真的会感觉到代码语言在某种程度上是相通的,那么今天来说说这两个知识点. skip和skipif,见名知意,就是跳过测试呗,直白 ...

  5. Excel字符串函数

    Excel字符串函数(用到的记录一下)"a1"代表a1单元格 mid(a1,start_num,nums)  a1格从第start_num取nums个,从1开始. len(a1) ...

  6. icmp port unreachable

    端口不可达: client------>server 结果server回复端口不可达, 由于是icmp报文: 到达client内核协议栈后进入icmp_rcv处理: /* * Deal with ...

  7. Idea eclipse 快捷键版

    查找/搜索 打开搜索界面     Ctrl+H 查找类文件             Ctrl+Shift+T 最近访问上一个文件      Ctrl+Alt+ ← 最近访问下一个文件        C ...

  8. DB2 建表,添加字段,删除字段,修改字段等常用操作

    转载:http://blog.sina.com.cn/s/blog_67aaf4440100v01p.html,稍作修改. --创建数据库 create database Etp; --连接数据库 c ...

  9. 循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储

    在我们一些系统里面,有时候会需要一些让用户自定义的数据信息,一般这些可以使用扩展JSON进行存储,不过每个业务表的显示项目可能不一样,因此需要根据不同的表单进行设计,然后进行对应的数据存储.本篇随笔结 ...

  10. PHP代码审计入门(SQL注入漏洞挖掘基础)

    SQL注入漏洞 SQL注入经常出现在登陆页面.和获取HTTP头(user-agent/client-ip等).订单处理等地方,因为这几个地方是业务相对复杂的,登陆页面的注入现在来说大多数是发生在HTT ...