数据结构-链表链表的相关算法
Posted Mount256
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构-链表链表的相关算法相关的知识,希望对你有一定的参考价值。
文章目录
(只是写了个大致思路,不具备可执行性)
1 删除元素
1.1 删除值为 x 的所有结点
1.1.1 不带头结点的单链表
- 正常写法:
void Delete_X (LinkList &L, int x)
LinkNode *p = L;
LinkNode *prevNode; // 记录 p 的前驱结点
LinkNode *nextNode; // 记录 p 的后继结点
while (p != NULL)
if (p->data == x)
if (p == L) // 如果是头结点
nextNode = p->next;
free(p);
L = nextNode;
else // 如果是中间结点或尾结点
nextNode = p->next;
free(p);
prevNode->next = nextNode;
prevNode = p; // 记录 p 的前驱结点
p = p->next; // 遍历下一个结点
- 递归写法:
void Delete_X (LinkList &L, int x)
LinkNode *p;
if (L->data == x)
p = L;
L = L->next;
free(p);
Delete_X(L, x);
else
Delete_X(L->next, x);
1.1.2 带头结点的单链表
void Delete_X (LinkList &L, int x)
LinkNode *p;
LinkNode *prevNode; // 记录 p 的前驱结点
LinkNode *nextNode; // 记录 p 的后继结点
p = L->next;
prevNode = L;
while (p != NULL)
if (p->data == x)
nextNode = p->next;
free(p);
prevNode->next = nextNode;
prevNode = p; // 记录 p 的前驱结点
p = p->next; // 遍历下一个结点
【注】欲删除值介于 s 和 t 之间的所有结点,需把条件改为
(s <= p->data) && (p->data <= t)
。
1.2 删除重复元素
1.2.1 不带头结点的单链有序表
void Delete_Same (LinkList &L)
LinkNode *p = L; // 初始指向链表头部
LinkNode *nextNode; // 记录 p 的后继结点
while (p != NULL)
nextNode = p->next; // 记录 p 的后继结点
if ((nextNode != NULL) && (nextNode->data == p->data)) // 发现重复元素
p->next = nextNode->next;
free(nextNode);
else // 没有发现重复元素
p = p->next; // 遍历下一个结点
1.2.2 带头结点的单链有序表
void Delete_Same (LinkList &L)
LinkNode *p = L->next; // 初始指向链表第一个结点
LinkNode *nextNode; // 记录 p 的后继结点
while (p != NULL)
nextNode = p->next; // 记录 p 的后继结点
if ((nextNode != NULL) && (nextNode->data == p->data)) // 发现重复元素
p->next = nextNode->next;
free(nextNode);
else // 没有发现重复元素
p = p->next; // 遍历下一个结点
2 链表逆置
2.1 逆序输出结点
2.1.1 不带头结点的单链表
设 L 为不带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
void Reverse_Print (LinkList &L)
LinkNode *p = L; // 指向链表头部
Stack S; // 定义栈
int x;
InitStack(S); // 初始化栈
while (p != NULL)
PushStack(S, p->data); // 入栈
p = p->next;
while (!EmptyStack(S))
x = PopStack(S); // 出栈
输出 x;
2.1.2 带头结点的单链表
设 L 为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
void Reverse_Print (LinkList &L)
LinkNode *p = L->next;
Stack S; // 定义栈
int x;
InitStack(S); // 初始化栈
while (p != NULL)
PushStack(S, p->data); // 入栈
p = p->next;
while (!EmptyStack(S))
x = PopStack(S); // 出栈
输出 x;
2.2 就地逆置
2.2.1 不带头结点的单链表
试编写一个算法将不带头结点的单链表就地逆置。
就地:空间复杂度为 O(1)。
思路:总是把第一个结点取下来,然后用尾插法重新建立链表。
LinkList Reverse (LinkList &L)
LinkNode *head = L; // 指向原链表头部
LinkNode *nextNode; // 保存原链表头部的指针域
LinkNode *newHead = NULL; // 指向新链表头部
while (head != NULL)
nextNode = head->next; // 保存原链表头部的指针域
head->next = newHead; // 原链表头部的指针域指向新链表头部
newHead = head; // 新链表头部更新
head = nextNode; // 原链表头部更新
L = newHead;
return L;
2.2.2 带头结点的单链表
LinkList Reverse (LinkList &L)
LinkNode *head = L->next; // 指向原链表第一个结点
LinkNode *nextNode; // 保存原链表头部的指针域
LinkNode *newHead = NULL; // 指向新链表头部
while (head != NULL)
nextNode = head->next; // 保存原链表头部的指针域
head->next = newHead; // 原链表头部的指针域指向新链表头部
newHead = head; // 新链表头指针更新
head = nextNode; // 原链表头指针更新
L->next = newHead; // 更新头结点指针域,头结点插到新链表头部
return L;
3 链表有序
3.1 链表排序
3.1.1 不带头结点的单链表
有一个不带头结点的单链表 L,设计一个算法使其元素递增有序。
思路:每次取出原链表的第一个元素,与有序链表中每个元素进行对比,然后插入到有序链表的相应位置。
void Sort (LinkList &L)
LinkNode *head = L; // 原链表头指针
LinkNode *nextNode; // 保存原链表头部的指针域
LinkNode *newHead; // 有序链表头指针
LinkNode *newNode, *newPrev; // 有序链表结点指针及其前驱结点
newHead = (LinkNode *) malloc(sizeof(LinkNode));
newHead->data = L->data;
newHead->next = NULL;
while (head != NULL)
newPrev = NULL;
newNode = newHead;
while ((newNode != NULL) && (head->data <= newNode->data)) // 有序链表指针指向第一个大于 head 元素的结点
newPrev = newNode; // 记录其前驱结点
newNode = newNode->next; // 继续遍历新链表
// 运行到此处,newPrev 在前,newNode 在后
nextNode = head->next; // 保存原链表头部的指针域
if (newPrev == NULL) // 如果要插入有序链表的头部位置
head->next = newNode;
newHead = head; // 更新有序链表头指针
else // 如果要插入有序链表的中间和尾部位置
newPrev->next = head;
head->next = newNode;
head = nextNode; // 继续遍历原链表
L = newHead;
3.1.2 带头结点的单链表
有一个带头结点的单链表 L,设计一个算法使其元素递增有序。
void Sort (LinkList &L)
LinkNode *head = L->next; // 原链表头指针
LinkNode *nextNode; // 保存原链表头部的指针域
LinkNode *newNode, *newPrev; // 有序链表结点指针及其前驱结点
while (head != NULL)
newPrev = L; // 原链表头结点作为新链表的头结点
newNode = L->next;
while ((newNode != NULL) && (head->data <= newNode->data)) // 有序链表指针指向第一个大于 head 元素的结点
newPrev = newNode; // 记录其前驱结点
newNode = newNode->next; // 继续遍历新链表
// 运行到此处,newPrev 在前,newNode 在后
nextNode = head->next; // 保存原链表头部的指针域
newPrev->next = head;
head->next = newNode;
head = nextNode; // 继续遍历原链表
3.2 链表有序输出
3.2.1 不带头结点的单链表
按递增次序输出单链表各结点的数据元素,每输出一个将该结点删除。
思路:每次遍历链表找出最小元素,输出该元素,然后删除。
void Print_Min (LinkList &L)
LinkNode *prev; // 扫描指针
LinkNode *prevNode, *nodeMin = L, *nextNode; // 最小值结点(初始指向链表第一个结点)以及其前驱和后继结点
int min = L->data;
while (L != NULL)
prev = L;
while (prev->next != NULL)
if (prev->next->data < min)
prevNode = prev; // 记录最小值的前驱结点
nodeMin = prev->next; // 记录最小值结点
min = nodeMin->data;
nextNode = prev->next->next;// 记录最小值的后继结点
prev = prev->next;
输出 min;
if (nodeMin == L) // 如果第一个结点为最小值
free(nodeMin);
L = nextNode;
else
free(nodeMin);
prevNode->next = nextNode;
3.2.2 带头结点的单链表
按递增次序输出单链表各结点的数据元素,每输出一个将该结点删除。
思路:每次遍历链表找出最小元素,输出该元素,然后删除。
void Print_Min (LinkList &L)
LinkNode *prev; // 扫描指针
LinkNode *nodeMin = L->next, *prevNode, *nextNode; // 最小值结点(初始指向链表第一个结点)以及其前驱和后继结点
int min = L->next->data;
while (L->next != NULL) // 循环到只剩头结点
prev = L;
while (prev->next != NULL)
if (prev->next->data < min)
prevNode = prev; // 记录最小值的前驱结点
nodeMin = prev->next; // 记录最小值结点
min = nodeMin->data;
nextNode = prev->next->next;// 记录最小值的后继结点
prev = prev->next;
输出 min;
free(nodeMin); // 删除最小值结点
prevNode->next = nextNode;
free(L);
4 合并链表
4.1 两个递增链表合并为一个递增链表
4.1.1 不带头结点的单链表
思路:设置双指针,建立新链表,运用尾插法,把两个链表的结点按升序插入即可。
LinkList Merge (LinkList &A, LinkList &B)
LinkNode *pa = A, *pb = B; // 扫描指针,指向前驱结点
LinkNode *prevNode, *node, *nextNode; // 记录前驱结点、当前要插入的结点、后继结点
LinkNode *C, *newNode; // 新链表
LinkNode *tail; // 新链表的尾指针
if (A->data < B->data) // 单独处理两个链表的第一个结点,这两个结点单独保留,将问题转化为“带头结点的单链表”
newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第一个结点
newNode->data = A->data;
C = newNode;
newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第二个结点
newNode->data = B->data;
newNode->next = NULL;
C->next = newNode;
tail = newNode;
else
newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第一个结点
newNode->data = B->data;
C = newNode;
newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第二个结点
newNode->data = A->data;
newNode->next = NULL;
C->next = newNode;
tail = newNode;
while ((pa->next != NULL) && (pb->next != NULL))
if (pb->next->data < pa->next->data)
prevNode = pb; // 记录前驱结点···(1)
node = pb->next; // 记录当前要插入的结点
nextNode = node->next; // 记录后继结点
prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
tail->next = node; // 往新链表尾插当前结点···(3)
tail = node; // 更新新链表尾指针
pb = pb->next; // 链表 B 的扫描指针继续遍历···(4)
else
prevNode = pa; // 记录前驱结点···(1)
node = pa->next; // 记录当前要插入的结点
nextNode = node->next; // 记录后继结点
prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
tail->next = node; // 往新链表尾插当前结点···(3)
tail = node; // 更新新链表尾指针
pa = pa->next; // 链表 A 的扫描指针继续遍历···(4)
// 运行到此处,一定有且仅有一个链表非空
if (A->next != NULL) // 若链表 A 还未空
tail->next = A->next; // 新链表尾部直接插入链表 A 的剩余部分
if (B->next != NULL) // 若链表 B 还未空
tail->next = B->next; // 新链表尾部直接插入链表 B 的剩余部分
free(A); // 删除链表 A
free(B); // 删除链表 B
return C;
4.1.2 带头结点的单链表
思路:设置双指针,建立新链表,运用尾插法,把两个链表的结点按升序插入即可。
LinkList Merge (LinkList &A, LinkList &B)
LinkNode *pa = A, *pb = B; // 扫描指针,指向前驱结点
LinkNode *prevNode, *node, *nextNode; // 记录前驱结点、当前要插入的结点、后继结点
LinkNode *C = (LinkNode *) malloc(sizeof(LinkNode)); // 新链表的头结点
LinkNode *tail = C; // 新链表的尾指针
while ((pa->next != NULL) && (pb->next != NULL))
if (pb->next->data < pa->next->data)
prevNode = pb; // 记录前驱结点···(1)
node = pb->next; // 记录当前要插入的结点
nextNode = node->next; // 记录后继结点
prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
tail->next = node; // 往新链表尾插当前结点···(3)
tail = node; // 更新新链表尾指针
pb = pb->next; // 链表 B 的扫描指针继续遍历···(4)
else
prevNode = pa; // 记录前驱结点···(1)
node = pa->next; // 记录当前要插入的结点
nextNode = node->next; // 记录后继结点
prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
tail->next = node; // 往新链表尾插当前结点···(3)
tail = node; // 更新新链表尾指针
pa = pa->next; // 链表 A 的扫描指针继续遍历···(4)
// 运行到此处,一定有且仅有一个链表非空
if (A->next != NULL) // 若链表 A 还未空
tail->next = A链表