线性表: 包含多个数据元素的有限序列,元素之间一般首尾相接,是一对一的线性关系,它包括顺序表和链表
顺序存储结构: 用一段在内存中连续的地址单元来存储数据,顺序表,会事先申请一段足够大的内存进行紧密存储,有可能出现内存的闲置和溢出情况,不过不需要额外的内存来存储与其它元素的关系,方便读取,时间复杂度O(1);不方便元素的插入和删除,因为保证顺序存储会有大量的数据移动,时间复杂度O(n)
链式存储结构: 用一段在内存中可连续可不连续的地址单元来存储数据,链表,链表中的数据使用结点表示,结点包括指针域(相邻结点的引用)和数据域(自己的数据),每次增加一个结点就向系统请求分配一个结点的内存,动态分配内存,和顺序表不同,链表是什么时候存储数据什么时候申请内存,不会出现内存的闲置和溢出情况,方便进行元素的增加插入和删除,因为只需要改变相邻结点的指针指向,时间复杂度O(1),不方便进行元素的查找,因为每次查找都要遍历链表,时间复杂度O(n)
空间利用: 链表的各个结点在内存上的存储可以是不连续的,通过指针域联系两个结点,但同一个结点所占的储存区域是连续的。因为链表每次向系统申请一个结点的存储空间的关系,而存储空间的位置随机,所以会造成存储空间的碎片化问题,而且链表还需要申请额外的存储空间用于存放相邻结点的关系,导致存储空间的利用率不及顺序表。
线性表,顺序表和链表的关系示意图如下:
(一)单链表
这里着重分析一下单链表,无外乎增删改查
1. 创建结点:
public class Node<T>
{
private T m_Data; // 数据域
private Node<T> m_Next; // 指针域,引用
public T Data { get { return m_Data; } set { m_Data = value; } }
public Node<T> Next { get { return m_Next; } set { m_Next = value; } }
public Node()
{
m_Data = default(T);
m_Next = null;
}
public Node(T data)
{
m_Data = data;
m_Next = null;
}
public Node(Node<T> node)
{
m_Next = node;
}
public Node(T data, Node<T> node)
{
m_Data = data;
m_Next = node;
}
}
2.创建链表:
public class LinkList<T>
{
private Node<T> m_Head;
public Node<T> Head { get { return m_Head; } set { m_Head = value; } }
public LinkList()
{
m_Head = null;
}
public int GetCount()
{
Node<T> p = m_Head;
int count = 0;
while (p.Next != null)
{
p = p.Next;
count++;
}
return count;
}
public bool IsEmpty()
{
return m_Head == null;
}
public void Clear()
{
m_Head = null;
}
}
3.结点的增:
/// <summary>
/// 在单链表尾部增加节点
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
Node<T> q = new Node<T>(item);
Node<T> p = new Node<T>();
if (IsEmpty())
{
m_Head = q;
return;
}
p = m_Head;
while (p.Next != null)
{
p = p.Next;
}
p.Next = q;
}
/// <summary>
/// 在指定位置插入一个节点
/// </summary>
/// <param name="item"></param>
/// <param name="index"></param>
public void Insert(T item, int index)
{
int count = GetCount();
if (IsEmpty() || index < 0 || index > count)
{
Debug.LogError("链表为空或者插入位置不允许");
return;
}
Node<T> q = new Node<T>(item);
if (index == 0)
{
q.Next = m_Head;
m_Head = q;
}
else
{
int i = 0;
Node<T> p = m_Head;
while (i < index - 1)
{
i++;
p = p.Next;
}
if (i == index - 1) // 找到待插入的前一个节点
{
q.Next = p.Next;
p.Next = q;
}
}
}
4.结点的删:
/// <summary>
/// 删除指定位置的一个节点
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T Remove(int index)
{
T data = default(T);
int count = GetCount();
if (IsEmpty() || index < 0 || index > count)
{
Debug.LogError("链表为空或者删除位置不允许");
return data;
}
int i = 0;
Node<T> p = m_Head;
Node<T> q = new Node<T>();
while (i < index - 1)
{
i++;
p = p.Next;
}
if (i == index - 1)
{
q = p.Next;
}
p.Next = q.Next;
data = q.Data;
return data;
}
5.结点的查:
/// <summary>
/// 获取指定位置的值
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T GetItem(int index)
{
T data = default(T);
int count = GetCount();
if (IsEmpty() || index < 0 || index > count)
{
Debug.LogError("链表为空或者获取位置不允许");
return data;
}
int i = 0;
Node<T> p = m_Head;
while (i < index)
{
i++;
p = p.Next;
}
if (i == index)
{
data = p.Data;
}
return data;
}
/// <summary>
/// 查找值在链表中的指定位置
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public int GetItemIndex(T item)
{
if (IsEmpty())
{
Debug.LogError("链表为空");
return -1;
}
int index = 0;
Node<T> p = m_Head;
while (!p.Data.Equals(item) && p.Next != null)
{
index++;
p = p.Next;
}
if (p.Data.Equals(item))
{
return index;
}
else
{
Debug.LogError("指定值不存在");
}
return -1;
}
(二)循环链表
尾结点指针指向头结点,形成一个环:
(三)双向链表
每个结点包含两个指针域,分别指向上一个结点与下一个结点: