【 C# 】(一) --------- 泛型带头节点的单链表,双向链表实现

在编程领域,数据结构与算法向来都是提升编程能力的重点。而一般常见的数据结构是链表,栈,队列,树等。事实上C#也已经封装好了这些数据结构,在头文件 System.Collections.Generic 中,直接创建并调用其成员方法就行。不过我们学习当然要知其然,亦知其所以然。

本文实现的是链表中的单链表和双向链表,并且实现了一些基本方法

一. 定义一个链表接口 MyList

接口里声明了我们要实现的方法:

	interface MyList<T>
	{
		int GetLength();							//获取链表长度
		void Clear();								//清空链表				
		bool IsEmpty();								//判断链表是否为空
		void Add(T item);							//在链表尾部添加新节点
		void AddPre(T item,int index);				//在指定节点前添加新节点
		void AddPost(T item,int index);				//在指定节点后添加新节点
		T Delete(int index);						//按索引删除节点
		T Delete(T item,bool isSecond = true);		//按内容删除节点,如果有多个内容相同点,则删除第一个
		T this[int index] { get; }					//实现下标访问
		T GetElem(int index);						//根据索引返回元素
		int GetPos(T item);							//根据元素返回索引地址
		void Print();								//打印
	}

 

二. 实现单链表

 

2.1 节点类

先定义一个单链表所用的节点类,Node。而且我们要实现泛型

先定义一个数据域和下一节点(“Next”),并进行封装,然后给出数个重载构造器。这一步比较简单,这里直接给出代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 线性表
{
	/// <summary>
	/// 单向链表节点
	/// </summary>
	/// <typeparam name="T"></typeparam>
	class Node<T>
	{
		private T data;						//内容域
		private Node<T> next;				//下一节点

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

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

		public Node(T value,Node<T> next)
		{
			this.data = value;
			this.next = next;
		}

		public T Data
		{
			get { return data; }
			set { data = value; }
		}

		public Node<T> Next
		{
			get { return next; }
			set { next = value; }
		}
	}
}

 

2.2 链表类

创建一个链表类,命名为 LinkList 并继承 MyList。 

 先定义一个头结点,尾节点和一个 count;

其中,head 表示该链表的头部,不包含数据;

           tail 表示尾节点,指向该链表最后一个节点,当链表中只有 head 时,tail 指向 head。定义 tail 会方便接下来的操作

           count 用来表示该链表中除了 head 以外的节点个数

 

构造函数:

		/// <summary>
		/// 构造器
		/// </summary>
		public LinkList()
		{
			head = new Node<T>();
			tail = head;
			count = 0;
		}

在我们实现成员函数之前,先实现两个特别的方法,因为在许多的成员方法中都要做两个操作:

  • 判断索引 index 是否合法,即是否小于0或者大于当前链表的节点个数
  • 寻找到 index 所代表的节点

 

①. 判断索引是否合法,然后可以根据其返回的数值进行判断操作

 

 

②. 寻找节点。

定义这两个方法主要是它们的重复使用率高,所以把它们的代码抽出来。

 相对于数组,链表的插入与删除更方便,而查找却更加费时,一般都是从头结点开始遍历链表,时间复杂度为 O(n) ,而跳跃链表则会对查询进行优化,当然这会在下一篇中详述。现在继续来实现成员方法。

 

1. 获取链表长度

 

 这个方法实际上是比较简单的,因为 count 会随着添加,删除等操作自动增减,所以直接返回 count 就相当于 链表长度。

需要注意的是,本文中的 count 是不计算空头结点的,即 head 不会计算入内

 

2. 清空链表

这里要注意对 tail 的操作,而 head.Next 原本所指的节点不再被引用后,会被GC自动回收

 

3. 判断链表是否为空

因为本文实现的链表是带空头结点的,所以这里认为,当除了头结点外没有别的节点时,则为空链表

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值