单链表也是顺序表的一种,是呈线性链式结构的一种存储类型;
首先写出一个单链表需要知道一些基本的知识,一个链表单元节点由数据域和指针域组成。
数据域(Data)顾名思义就是存放数据的地方,不同于顺序表使用的数组,其多用于T类型泛型定义,方便存储数据。
指针域(Next)其用法是指向下一个节点,例如: temp = temp.next; 这样子就完成了一次节点套娃,将节点向后移动了一位。
其实搞明白单链表,主要是要弄懂其套娃的方式,弄懂了,就豁然开朗了;
首先,我们定义一个关于链表抽象的接口:
//interface意为接口
interface IListDS<T> //加DS是因为系统中以及有一个Ilist接口了,为了避免重复
{
//定义方法
int GetLenghth();//得到长度
void Clear();//清空操作
bool IsEmpty();//
void Add(T item);//添加操作
void Insert(T item, int index);//插入数据
T Delect(int index);//根据索引删除
T this[int index] { get; } //定义一个索引器 用的get set 方法,用于取值
T GetEle(int index);//根据索引得到元素
int Locate(T value);//根据值得到索引
}
然后我们需要新建一个节点类,方便我们实现接口;
internal class Node<T>
{
private T data; //存储数据
private Node<T> next; //下一个节点
public Node()//空的构造方法用于初始化
{
data = default(T);//默认值
next = null;
}
public Node(T value) //构造方法,构造结点
{
//赋值传过来的数据进行初始化
data = value;
next = null;
}
public Node(T value, Node<T> next) //构造方法,用于定义两个参数
{
this.data = value;
this.next = next;
}
//构造出单独设置某值的方法
public Node(Node<T> next)
{
this.next = next;
}
//设置属性的方法
public T Data
{
get { return data; }
set { data = value; }
}
//指针
public Node<T> Next
{
get { return next; }
set { next = value; }
}
}
用继承实现并丰富IListDS接口
internal class LinkList<T> : IListDS<T> //实现接口
{
public T this[int index] => throw new NotImplementedException();
private Node<T> head;//定义一个头结点
public void Add(T item)//添加数据项
{
Node<T> newNode = new Node<T>(item);//根据新的数据项创建一个新的节点
//如果head头结点为空,则新结点添加到链表最后一个元素,即使新节点就是头结点
if (head == null)//为空直接赋新节点
{
head = newNode;
}
else//如果头节点不为空,那么就把头节点赋值给临时节点
{
//
Node<T> temp = head;
while (true)//死循环
{
//判断临时节点的下一个节点 是否为空,为空代表下一个格子没有元素,可以插入
if (temp.Next != null)
{
//不等于空,就把当前这个临时节点的next节点更新成当前节点
temp = temp.Next;
}
else//为空退出循环
{
break;
}
}
//将新来的节点放入链表的尾部,将要插入的这个节点赋值给temp.next 意为指向下一个节点
temp.Next = newNode;
}
}
public void Clear()//链表清空
{
//只需要把头结点设置为空,后续节点会被垃圾回收器回收
head = null;
}
public T Delect(int index)//根据索引删除节点
{
//默认值
T data = default(T);
if (index == 0) //删除头结点
{
head = head.Next; //直接后移一位
}
else
{
//声明一个临时节点,拿到头结点
Node<T> temp = head;
//根据索引访问到索引位置的节点
for (int i = 0; i <= index - 1; i++)
{//只要小于索引大小就循环
temp = temp.Next;//向后移动
}//取到链表要插入的索引的之前哪个节点 【preNode】-【newNode】-【currentNode】
Node<T> preNode = temp;
Node<T> currentNode = temp.Next;//要删除的节点数据,方便返回
data = currentNode.Data;
Node<T> nextNode = temp.Next.Next;
preNode.Next = nextNode;
}
//返回数据
return data;
}
public T GetEle(int index)//根据索引获取节点
{
Node<T> temp = head;
for (int i = 0; i <= index-1; i++) //index-1 是因为索引从0开始
{
temp = temp.Next;
}
return temp.Data; //待循环结束返回索引的节点元素
}
public int GetLenghth()//得到长度需要遍历
{
//如果头结点为空那么返回长度为0
if (head == null) return 0;
//临时节点指头结点
Node<T> temp = head;
int count = 1;
//不知道具体长度所以用到死循环
while (true)
{
if (temp.Next!=null)
{
count++;
temp = temp.Next;
}
else
{
break;
}
}
return count;
}
public void Insert(T item, int index)//插入操作
{
//创建一个新的节点
Node<T> newNode = new Node<T>(item);
//判断是否要插入到头结点
if (index == 0)
{
//如果插入索引为0,name就将这个节点next指向head
newNode.Next = head;
//更新头结点为当前这个新节点,因为是插在第一个
head = newNode;
}
else//往链表中间插入
{
//声明一个临时节点,拿到头结点
Node<T> temp = head;
//根据索引访问到索引位置的节点
for (int i = 0; i <= index -1;i++ )
{//只要小于索引大小就循环
temp = temp.Next;//向后移动
}//取到链表要插入的索引的之前哪个节点 【temp】-【newNode】-【currentNode】
//所以要把temp的next指向newNode
Node<T> preNode = temp;
Node<T> currentNode = temp.Next;
preNode.Next = newNode;
//将插入的这个新元素next 指向currentNode
newNode.Next = currentNode;
}
}
public bool IsEmpty() //判断单链表是否为空
{
if(GetLenghth()==0) return true;
else { return false; }
}
public int Locate(T value)//判断元素是否在链表中
{
Node<T> temp = head;
if (temp == null)
{
return -1;
}
else
{
int index = 0;
while (true)
{
if (temp.Data.Equals(value))
{
return index;
}
else
{
if(temp.Next != null)
{
temp = temp.Next;
}
else
{
break;
}
}
}
return -1;
}
}
}
接下来只需要去program.cs中new一个对象使用即可!
代码中已做详细注释,就不多多赘述了