# 链表

## 从单链表说起

typedf struct LNode    //定义单链表结点类型
{
ElemType data;    //数据域，存放数据
struct LNode *next;    //指针域，指向后继结点


# 建立单链表

## 头插法建链表

void CreateListF(LinkList& L, int n)
{

for (int i = 0; i < n; i++)
{
ptr = new(LNode);    //创建新结点
cin >> ptr->data;
}
}


## 尾插法建链表

void CreateListR(LinkList& L, int n)
{

for (int i = 0; i < n; i++)
{
ptr = new LNode;    //创建新结点
cin >> ptr->data
ptr->next = NULL;
tail->next = ptr;    //新结点插在表尾
tail = ptr;    //更新表尾
}
}


# 结点的插入与删除

## 插入新结点

s = new LNode;
s->next = pre->next
pre->next = s;


bool Insert(List L, ElementType X, Position P)
{
ptr = new LNode;
ptr->Data = X;

while (L != NULL)
{
if (L->Next == P)
{
ptr->Next = L->Next;    //将新结点的后继连接到后续结点
L->Next = ptr;    //插入新结点
return true;
}
L = L->Next;    //移动结点直到i-1位置
}
return false;
}


## 删除结点

ptr = pre->next;
pre->next = pre->next->next;
delete ptr;


bool Delete(List L, Position P)
{

while (L->Next != NULL)
{
if (L->Next == P)
{
ptr = L->Next;    //拷贝要删除的结点
L->Next = L->Next->Next;    //连接后续结点
delete ptr;    //释放空间
return true;
}
L = L->Next;    //移动结点直到P位置之前
}
return false;
}


# 链表实现线性表基本操作

## 初始化链表

List MakeEmpty()
{

}


## 销毁链表

void DestroyList(LinkList &L)
{
while (L != NULL)    //遍历单链表
{
ptr = L;    //拷贝结点
L = L->next;
delete ptr;    //释放单个结点的空间
}
}


## 判断是否为空表

bool ListEmpty(LinkList *L)
{
return (L->next == NULL);
}


## 获取表长

int Length(LinkList L)
{
int length = 0;
while (L->next != NULL)
{
length++;
L = L->next;
}
return length;
}


## 链表逆置

void reversel(LNode *L)
{
LNode *p = L->next, *q;
L->next = NULL;           // 将 L 作为新表的头结点

while(p != NULL)
{
q = p->next;          // 暂时保持后续结点的信息
p->next = L->next;    // 头插法
L->next = p;
p = q;
}
}


# 有序表

## 插入操作

void ListInsert(List &L,ElemType e)
{
List *pre = L, *ptr;

while(pre->next != NULL && e > pre->next->data)
{
pre = pre->next;    //找到插入位置的前驱结点
}
ptr = new LNode;    //申请空间作为新结点
ptr->data = e;
ptr->next = pre->next;    //插入操作
pre->next = ptr;
}


## 有序表归并

void UnionList(List& L1, List &L2)
{

while (L1->next != NULL && L2->next != NULL)
{
if (L1->next->data == L2->next->data)
{
L2 = L2->next;
}
else if (L1->next->data > L2->next->data)
{
ptr = L2->next;
L2->next = L2->next->next;
ptr->next = L1->next;
L1->next = ptr;
}
L1 = L1->next;
}
if (L2->next != NULL)
{
L1->next = L2->next;
}
}


# 链表的优缺点分析

## 优点

1. 插入删除速度较快，确定需要插入、删除的结点后，操作的时间复杂度仅为O(1)；
2. 内存利用率高，链表需要的内存空间根据需求动态申请；
3. 链表的结点数量没有固定，拓展链表的数据量显得灵活。

## 浅谈顺序表和链表的选择

1. 若线性表需要随机提取元素，频繁查找元素，很少进行插入和删除操作时，用顺序存储结构。需要频繁插入和删除时，采用单链表结构；
2. 若事先知道线性表大致需要多少空间，用顺序存储结构，当线性表中的元素个数需要动态变化或者不知道有需要多少空间存储时，最好用单链表结构；
3. 顺序表和链表各有优缺点，需要结合实际的需求选择合适的结构存储。

# 循环链表

## 头结点与尾指针

### 初始化循环表

List MakeEmpty()
{

}


### 合并两个循环表

Link MergeLink(Link list1_tail, Link list2_tail)
{
LNode *ptr;

ptr = list1_tail->next;    //保存 list1 的头结点
list1_tail->next = list2_tail->next->next;    //将 list1 的后继修改为 list2 的表头结点
delete list2_tail->next;    //释放 list2 的头结点
list2_tail->next = ptr;    //修改 list2 尾结点的后继为 list1 的头结点

return list2_tail;    //返回合并后的头结点
}


# 双向链表

## 前驱指针域

typedef struct DulNode
{
ElemType data;
struct DulNode *prior;    //前驱指针域
struct DulNode *next;    //后继指针域
}DulNode,*DulList;


List MakeEmpty()
{

}


## 插入与删除操作

### 插入操作

ptr->prior = pre;    //修改 ptr 的前驱为 pre
ptr->next = pre->next;    //修改 ptr 的后继为 pre->next
pre->next->prior = ptr;    //修改 pre->next 的前驱为 ptr
pre->next = ptr;    //修改 pre 的后继为 ptr


### 删除操作

ptr->prior->next = ptr->next;    //修改 ptr 前驱的后继为 ptr->next
ptr->next->prior = ptr->prior;    //修改 ptr 后继的前驱为 ptr->prior
delete ptr;    //释放 ptr 的空间


# 例题解析

## jmu-ds-链表分割

### 代码实现

void SplitList(LinkList& L, LinkList& L1, LinkList& L2)
{
L2 = new LNode;
L2->next = NULL;

{
ptr->next = L2->next;
L2->next = ptr;
}
L1 = L;
}


## jmu-ds-链表倒数第m个数

### 代码实现

int Find(LinkList L, int m)
{
LinkList qptr = L->next, sptr = L->next;
//初始化快、慢指针
if (m <= 0)
{
return -1;    //不合法数据判断
}
//快指针先开始遍历，制造步差
for (int i = 0; i < m; i++)
{
//如果慢指针苏醒前，快指针已到表尾，说明数据不合法
if (qptr == NULL)
{
return -1;
}
qptr = qptr->next;
}
//慢指针苏醒，和快指针同时遍历
while (qptr != NULL)
{
qptr = qptr->next;
sptr = sptr->next;
}
return sptr->data;
}


## 《剑指 Offer》题 6：从尾到头打印链表

《剑指 Offer》学习记录：题 6：从尾到头打印链表

## 《剑指 Offer》题 22：链表中倒数第 k 个结点

《剑指 Offer》学习记录：题 22：链表中倒数第 k 个结点

## 《剑指 Offer》题 24：反转链表

《剑指 Offer》学习记录：题 24：反转链表

## 《剑指 Offer》题 25：合并两个排序的链表

《剑指 Offer》学习记录：题 25：合并两个排序的链表

# 参考资料

《大话数据结构》—— 程杰 著，清华大学出版社
《数据结构教程》—— 李春葆 主编，清华大学出版社
《数据结构与算法》—— 王曙燕 主编，人民邮电出版社

C语言中文网

posted @ 2020-03-08 01:51  乌漆WhiteMoon  阅读(5434)  评论(2编辑  收藏  举报