数据结构对我来说其实并不陌生,写项目的时候其实也用的不少,但是很少关心他们底层是怎么实现的,还是要补一下,来自己写一下数据结构的实现。
今天主要是用两种方法实现List,一种是顺序表,一种是单链表
namespace 数据结构与算法
{
public interface IListDS<T>
{
/// <summary>
/// 获取List长度(元素个数)
/// </summary>
/// <returns></returns>
int GetLength();
/// <summary>
/// 清空List
/// </summary>
void Clear();
/// <summary>
/// 判断List是否为空
/// </summary>
/// <returns></returns>
bool IsEmpty();
/// <summary>
/// 往List添加元素
/// </summary>
/// <param name="item"></param>
void Add(T item);
/// <summary>
/// 往LIst插入元素
/// </summary>
/// <param name="item">要插入的元素</param>
/// <param name="index">要插入的位置</param>
void Insert(T item, int index);
/// <summary>
/// 删除元素
/// </summary>
/// <param name="index">要删除的元素位置</param>
/// <returns>要删除的元素</returns>
T Delete(int index);
/// <summary>
/// 索引器,通过下标访问具体元素
/// </summary>
/// <param name="index"></param>
T this[int index] { get; }
/// <summary>
/// 通过下标获取元素
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
T GetEle(int index);
/// <summary>
/// 通过元素值返回下标
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
int Locate(T value);
}
}
看源码的可以知道,list底层是用泛型数组来存储数据已经进行一系列操作的。
具体实现List中的方法
using System;
namespace 数据结构与算法
{
/// <summary>
/// 顺序表实现方式
/// </summary>
public class SeqList<T> : IListDS<T> //继承ListDS接口,实现其中的方法
{
private T[] data; //用来存储数据
private int count; //表示存了多少个数据
public SeqList(int size) //size就是最大容量
{
data = new T[size];
count = 0;
}
public SeqList() : this(10) //默认构造函数,默认容器的容量为10
{
}
/// <summary>
/// 往顺序表添加元素
/// </summary>
/// <param name="item"></param>
public void Add(T item)
{
if (count == data.Length) //当前数组已经存满了
{
Console.WriteLine("当前顺序表已经存满,不允许再存数入");
}
else
{
data[count] = item;
count++;
}
}
/// <summary>
/// 取得数据的个数
/// </summary>
/// <returns></returns>
public int GetLength()
{
return count;
}
/// <summary>
/// 清除所有元素
/// </summary>
public void Clear()
{
count = 0;
}
/// <summary>
/// 判断List是否为空
/// </summary>
public bool IsEmpty()
{
return count == 0;
}
/// <summary>
/// 往顺序表插入元素
/// </summary>
/// <param name="item">插入的元素</param>
/// <param name="index">插入的位置</param>
public void Insert(T item, int index)
{
//从后往前遍历到index的位置,把index往后的元素都向后移动一位
for (int i = count - 1; i >= index; i--)
{
data[i + 1] = data[i];
}
data[index] = item; //index位置上的元素改为要插入的元素
count++; //总元素数自增
}
/// <summary>
/// 按索引从顺序表删除元素
/// </summary>
/// <param name="index">索引值</param>
public T Delete(int index)
{
T temp = data[index];
//把被删除元素往后的元素都往前移动一位
for (int i = index + 1; i < count; i++)
{
data[i - 1] = data[i];
}
count--;
return temp;
}
public T this[int index] => GetEle(index);
/// <summary>
/// 按照索引获取元素
/// </summary>
/// <param name="index"> 索引值 </param>
/// <returns></returns>
public T GetEle(int index)
{
if (index >= 0 && index <= count - 1) //索引存在
{
return data[index];
}
else
{
Console.WriteLine("索引不存在");
return default(T);
}
}
/// <summary>
/// 根据元素值获取元素索引
/// </summary>
/// <param name="value">元素值</param>
public int Locate(T value)
{
for (int i = 0; i < count; i++)
{
if (data[i].Equals(value))
{
return i;
}
}
return -1;//如果返回-1,表示元素不存在
} }
}
自己写完这个实现也能发现List的特点:在末尾添加删除操作效率高,在中间插入删除元素效率较低,因为需要整体移动被修改元素之后的所有元素。
先定义一个Node节点类:
using System;
using System.Collections.Generic;
using System.Text;
namespace 数据结构与算法
{
/// <summary>
/// 单链表的节点
/// </summary>
/// <typeparam name="T"></typeparam>
class Node<T>
{
private T data;//存储数据
private Node<T> next;//指针,用来指向下一个元素
public Node()
{
data = default(T);//default:返回元素的默认值
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; }
}
}
}
具体代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace 数据结构与算法
{
class LinkList<T> : IListDS<T>
{
private Node<T> head;//存储一个头节点
public LinkList()
{
head = null;
}
/// <summary>
/// 索引器
/// </summary>
public T this[int index]
{
get
{
Node<T> temp = head;
for (int i = 1; i <= index; i++)
{
temp = temp.Next;
}
return temp.Data;
}
}
/// <summary>
/// 往单链表中添加元素
/// </summary>
/// <param name="item"></param>
public void Add(T item)
{
Node<T> newNode = new Node<T>(item);//根据新的数据创建一个新的节点
//如果头节点为空,那么这个新的节点的节点就是头节点
if (head == null)
{
head = newNode;
}
else //如果有头节点
{
/*要访问到链表的尾节点*/
Node<T> temp = head;//设置临时变量,并把头节点赋值给它
while (true)//死循环
{
if (temp.Next != null)//如果下一个节点不为空
{
temp = temp.Next;//把下一个节点赋值给temp
}
else //直到没有下一个节点了
{
break;
}
}
temp.Next = newNode;//把新的节点放到链表的尾部
}
}
/// <summary>
/// 清空链表
/// </summary>
public void Clear()
{
head = null;
}
/// <summary>
/// 按索引从顺序表删除元素
/// </summary>
/// <param name="index">索引值</param>
public T Delete(int index)
{
T data = default(T);//临时变量,要删除的元素值
if (index == 0)//如果要删除的是头节点
{
data = head.Data;
head = head.Next;
}
else
{
Node<T> temp = head;
for (int i = 1; i <= index - 1; i++)
{
//让temp向后移动index - 1个位置(移动到要删除的位置的前一个位置)
temp = temp.Next;
}
Node<T> preNode = temp;//要删除的位置的前一个节点就为temp
Node<T> curNode = temp.Next;//要删除的节点
data = curNode.Data;//要删除的节点的数据
Node<T> nextNode = temp.Next.Next;//要删除的节点的后一个节点
preNode.Next = nextNode;//把要删除的节点的【前一个节点】指向要删除的节点的【后一个节点】
}
return data;
}
public T GetEle(int index)
{
return this[index];
}
/// <summary>
/// 获取链表长度
/// </summary>
/// <returns></returns>
public int GetLength()
{
if (head == null)//如果头节点为空
{
return 0;//没有元素返回0
}
Node<T> temp = head;//设一个临时变量为头节点
int count = 1;//元素数量至少为1
while (true)
{
if (temp.Next != null)//如果头节点的下一个节点不为空
{
count++;//元素数量自增
temp = temp.Next;//重新赋值临时变量
}
else
{
break;//直到没有下一个节点为止,跳出循环
}
}
return count;//最后返回元素数量
}
/// <summary>
/// 往单链表中插入元素
/// </summary>
/// <param name="item"></param>
/// <param name="index"></param>
public void Insert(T item, int index)
{
Node<T> newNode = new Node<T>(item);
if (index == 0)//插入到头节点
{
newNode.Next = head;//新节点的下一个节点为之前的头节点
head = newNode;//头节点设为新节点
}
else
{
Node<T> temp = head;
for (int i = 1; i <= index - 1; i++)
{
//让temp向后移动index - 1个位置(移动到要插入的位置的前一个位置)
temp = temp.Next;
}
Node<T> preNode = temp;//要插入的位置的前一个节点就为temp
Node<T> curNode = temp.Next;//要插入的位置的节点就是上个节点的Next
preNode.Next = newNode;//把要插入的位置的前一个节点指向新节点
newNode.Next = curNode;//把新节点指向要插入的位置的节点
}
}
/// <summary>
/// 链表是否为空
/// </summary>
/// <returns></returns>
public bool IsEmpty()
{
return head == null;
}
/// <summary>
/// 根据元素值获取元素索引
/// </summary>
/// <param name="value">元素值</param>
public int Locate(T value)
{
Node<T> temp = head;
if (temp == null)//如果没有头节点
{
return -1;//返回-1,表示链表内没有元素
}
else
{
int index = 0;
while (true)
{
if (temp.Data.Equals(value))//如果节点的值等于value
{
return index;//返回下标
}
else//如果节点的值不等于value
{
if (temp.Next != null)//下一个节点也不为空
{
temp = temp.Next;//向下遍历
}
else
{
break;//直到没有下一个节点为止
}
}
}
return -1;
}
}
}
}
用单链表来实现还是有点复杂的。。。
具体的测试代码就不放了,都测试过没有问题了。
PS:以上代码只是简单实现,并没有考虑代码的健壮性,一些参数检查的工作没有做。
因篇幅问题不能全部显示,请点此查看更多更全内容