typedef struct LNode {
int data;
struct LNode* next;
}LNode, *LinkList;
①强调结点 LNode *p;
②强调链表 LinkList p;
//初始化
LNode* initList() {
//定义头结点
LNode* L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
return L;
}
//带头结点单链表遍历打印
void Listvisit(LinkList L) {
LNode* p = L->next;
while (p != NULL) {
printf("%d", p->data);
p = p->next;
}
}
//不带头结点单链表遍历打印
void Listvisit_2(LinkList L) {
LNode* p = L;
while (p != NULL) {
printf("%d", p->data);
p = p->next;
}
}
LNode* Search_i(LinkList L,int i) {
if (i < 1) {//判断i是否合法
return NULL;
}
LNode* p = L->next;
int k = 1;//计数
while (p != NULL && k<i) {
p = p->next;
k++;
}
return p;
}
LNode* Search_e(LinkList L, int e) {
LNode* p = L->next;
while (p != NULL && p->data != e) {
p = p->next;
}
return p;
}
也可以采用下面的代码,方便理解
LNode* Search_e(LinkList L, int e) {
LNode* p = L->next;
while (p != NULL) {
if (p->data == e) {
return p;
}
else {
p = p->next;
}
}
return NULL;
}
LinkList List_HeadInsert(LinkList& L) {
L = (LinkList)malloc(sizeof(LNode));//创建头结点
L->next = NULL;//初始化头结点
LNode* s;//定义辅助指针 s
int x;//定义辅助变量 x
scanf("%d", &x);//输入新结点数据域中的值
while (x != -1) {//输入-1 表示输入结束
s = (LNode*)malloc(sizeof(LNode));//为新结点分配内存
s->data = x;//为新结点数据域赋值
s->next = L->next;//头插法核心代码
L->next = s;//头插法核心代码
scanf("%d", &x);//继续输入下一结点数据域中的值
}
return L;
}
LinkList List_TailInsert(LinkList& L) {
L = (LinkList)malloc(sizeof(LNode));//创建头结点
L->next = NULL;//养成习惯
LNode* s, * r = L;//尾插法核心代码(定义尾指针)
int x;//定义辅助变量 x
scanf("%d", &x);//输入新结点数据域中的值
while (x != -1) {//输入-1 表示输入结束
s = (LNode*)malloc(sizeof(LNode));//为新结点分配内存
s->data = x;//为新结点数据域赋值
r->next = s;//尾插法核心代码
r = s;//尾插法核心代码(更新尾指针位置)
scanf("%d", &x);//继续输入下一结点数据域中的值
}
r->next = NULL;//尾插法核心代码(尾插结束后尾结点指针域留空)
return L;
}
void Reverse(LinkList& L) {
LNode* p = L->next;//定义遍历指针 p,r 为防断链指针(头插法)
LNode* r;
L->next = NULL;//分离出 L 作为空表的头结点
while (p != NULL) {//遍历链表,将遍历到的结点重新进行头插
r = p->next;//防断链指针指向下一个需要遍历的结点位置,防止断链
p->next = L->next;//头插法核心代码
L->next = p;//头插法核心代码
p = r;//p 指针更新到下一遍历结点位置
}
}
LinkList Create_A_B(LinkList& A) {
LinkList B = (LinkList)malloc(sizeof(LNode));//创建 B 表头结点
B->next = NULL;
LNode* p = A->next;//定义遍历指针 p
LNode* ra = A, * rb = B;// 定义A 表 B 表尾指针
A->next = NULL;//断开 A 表头结点(这行代码可去掉)
while (p != NULL) {//遍历原表
ra->next = p;//将 p 指针所指结点尾插到 A 表
ra = p;//更新 A 表尾指针
p = p->next;//更新 p 指针,继续遍历
if (p != NULL) {//防止最后一次循环因为只有一个结点而报错
rb->next = p;//将 p 指针所指结点尾插到 B 表
rb = p; //更新 B 表尾指针
p = p->next;//更新 p 指针,继续遍历
}
}
ra->next = NULL;//A 表尾结点指针域置空
rb->next = NULL;//B 表尾结点指针域置空
return B;
}
LinkList Create_A_B(LinkList& A) {
LinkList B = (LinkList)malloc(sizeof(LNode));//创建 B 表头结点
B->next = NULL;//头插法需要先对头结点指针域进行初始化
LNode* p = A->next, * r = A, * q;//定义遍历指针 p,A 表尾指针和 B 表防断链指针
A->next = NULL;//断开 A 表头结点(这行代码可去掉)
while (p != NULL) {//遍历原表
r->next = p;//将 p 指针所指结点尾插到 A 表
r = p;//更新 A 表尾指针
p = p->next;//更新 p 指针,继续遍历
if (p != NULL) {//防止最后一次循环因为只有一个结点而报错
q = p->next;//防止使用头插法出现断链情况
p->next = B->next; //头插法核心代码
B->next = p;//头插法核心代码
p = q;//更新遍历指针位置
}
}
r->next = NULL;//A 表尾结点指针域置空
}
LinkList MergeList(LinkList& A, LinkList& B) {
LNode* p = A->next;//定义遍历指针
LNode* q = B->next;//定义遍历指针
LNode* r;//定义防断链指针
A->next = NULL;//将A作为最终合并后的单链表,使用头插法前需初始化头结点指针域
while (p != NULL && q != NULL) {//若两个表有任意一个处理完毕则停止循环
if (p->data <= q->data) {//比较当前两个链表数值最小的结点
r = p->next;//防止头插法断链
p->next = A->next;//p 遍历到的结点更小则将其头插到新表 A
A->next = p;
p = r;//更新遍历指针位置
}
else {//q 遍历到的结点更小则将其头插到新表 A
r = q->next;
q->next = A->next;
A->next = q;
q = r;
}
}
//以下将对两个判断操作合并到一步
if (q != NULL)//若 B 表未处理完则让 p 指针指向 B 表剩余所需遍历的结点
p = q; //若 A 表未处理完此行不用执行,p 已经指向 A 表需要遍历的结点
while (p != NULL) {//将剩余的结点挨个头插到新建立的 A 表
r = p->next;//防止头插法断链
p->next = A->next;//头插法核心代码
A->next = p;//头插法核心代码
p = r;//更新遍历指针位置
}
free(B);//释放 B 表头结点空间
return A;
}
void Del_Min(LinkList& L) {
LNode* pre = L, * p = L->next;//定义遍历指针 p 及其前驱指针 pre
LNode* minpre = pre, * minp = p;//记录所遍历过的结点中最小值结点的位置
//查找
while (p != NULL) {//遍历单链表
if (p->data < minp->data) {//判断所遍历结点是否小于之前记录的最小结点
minp = p;//若小于,则更新最小值结点位置
minpre = pre;
}
pre = p;//继续遍历
p = p->next;
}
//删除最小值
minpre->next = minp->next;//遍历结束,删除最小值结点
free(minp);//释放最小值结点空间
}
删除操作使用minpre和minp指针。
void Del_Min1(LinkList& L) {//不带头结点时 L 指向的是链表中第一个数据结点
LNode* pre, * p = L;//定义遍历指针 p 及其前驱指针 pre
LNode* minpre, * minp = p;//定义最小值记录指针 minp 及其前驱指针 minpre
while (p != NULL) {//遍历该链表
if (p->data < minp->data) {//判断遍历结点是否小于之前所记录的最小结点
minp = p;//若小于,则更新最小值结点位置
minpre = pre;
}
pre = p;//继续遍历
p = p->next;
}
//删除操作需要特殊考虑是否为第一个元素
if (minp == L) {//如果链表第一个数据结点是最小值结点需特殊考虑
L = L->next;//更新头指针位置
free(minp);//释放最小值结点空间
}
else {//如果链表最小值结点不是第一个数据结点,则直接删除
minpre->next = minp->next;//删除最小值结点
free(minp);//释放最小值结点空间
}
}
void Del_Min2(LinkList& L) {
LinkList head = (LinkList)malloc(sizeof(LNode));//创建头结点
head->next = L;//将原始链表链接到创建的头结点后面
LNode* pre = head, * p = head->next;//定义遍历指针 p 及其前驱指针 pre
LNode* minpre = pre, * minp = p;//定义记录最小值结点的指针 minp 及其前驱
while (p != NULL) {//遍历链表
if (p->data < minp->data) {//判断所遍历结点是否小于之前记录的最小结点
minp = p;//若小于,则更新最小值结点位置
minpre = pre;
}
pre = p;//继续遍历
p = p->next;
}
minpre->next = minp->next;//遍历结束,删除最小值结点
free(minp);//释放最小值结点空间
L = head->next;//更新头指针位置
free(head);//删除创建的头结点
}
第一次自己写的,漏洞百出
LinkList Del_x(LinkList& L,int x) {
LNode* p = L->next;
LNode* pre = L;
while (p != NULL) {
if (p->data == x) {
pre->next = p->next;
free(p);
}
pre = p;
p = p->next;
}
}
边找边删
上面我写的代码错误主要在
①pre = p;和p = p->next;应该是else的情况,因为if中已经删除结点和更新p指针,不把它放在else中会导致p又一次指向后一个结点;
②if里面的代码可以自己模拟一下,首先需要将p结点单独拿出来,所以pre->next = p->next;然后free掉,最后的话,p变成pre的下一个结点,此时p已经发生变化,不需要进行其他代码的编写。
LinkList Del_x(LinkList& L,int x) {
LNode* p = L->next;//定义遍历指针 p 及其前驱指针 pre
LNode* pre = L;
while (p != NULL) {
if (p->data == x) {//判断遍历结点数据域是否为 x
pre->next = p->next;//删除数据域为 x 的结点
free(p);//释放被删结点的空间
p = pre->next;//遍历指针更新到下一遍历结点位置
}
else {
pre = p;//两个遍历指针顺序后移,继续遍历
p = p->next;
}
}
}
void Del_min_max(LinkList& L, int min, int max) {
LNode* pre = L, * p = L->next;
while (p != NULL) {
if (p->data >= min && p->data <= max) {
pre->next = p->next;
free(p);
p = pre->next;
}
else {
pre = p;
p = p->next;
}
}
}
int Del_same(LinkList& L) {
if (L == NULL) {//代码健壮性
return -1;
}
LNode* pre = L->next;
LNode* p = pre->next;//遍历指针 p 指向第二个数据结点
while (p != NULL) {//遍历该链表
if (p->data == pre->data) {//若遍历结点与前驱结点数据值相同则证明重复
pre->next = p->next;//若重复删除 p 指针所指向的重复结点
free(p);//释放删除结点所占空间
p = pre->next;//更新遍历指针位置
}
else {//若不重复则继续遍历,指针后移
pre = p;
p = p->next;
}
}
}
}
LinkList Create_C(LinkList& A, LinkList& B) {
LinkList C = (LNode*)malloc(sizeof(LNode));
LNode* p = A->next, * q = B->next;
LNode* r = C, * s;//定义 C 表尾指针 r 和创建新结点所需的辅助指针 s
while (p != NULL && q != NULL) {//说明两个单链表中还有元素,因此需要比较
if (p->data < q->data) {//若 A 表中遍历结点值更小则 p 指针后移
p = p->next;
}
else if (p->data > q->data) {//若 B 表中遍历结点值更小则 q 指针后移
q = q->next;
}
else {//寻找到值相同的结点则建立新结点尾插到 C 表
s = (LNode*)malloc(sizeof(LNode));
s->data = p->data;
s->next = NULL;
r->next = s;//将新建立的结点尾插到 C 表中
r = s;//更新尾指针位置
//这两步别忘了,两指针继续遍历寻找值相同的结点
p = p->next;
q = q->next;
}
}
r->next = NULL;//尾插结束后尾结点指针域需置空
return C;
}
LinkList Create_same_A(LinkList& A, LinkList& B) {
LNode* p = A->next, * q = B->next;
LNode* r = A;//尾指针
LNode* s;//辅助记录被删结点
while (p != NULL && q != NULL) {
if (p->data < q->data) {//若 p 指针所指结点数值较小则释放此结点并后移 p
s = p;//s 指针记录此结点需释放空间,方便 p 指针先顺利后移
p = p->next;//p 指针顺利后移
free(s);//释放刚遍历到的数值较小结点
}
else if (p->data > q->data) {
s = q;
q = q->next;
free(s);
}
else {//寻找到两个链表的交集结点则将其中一个尾插入新 A 表
r->next = p;//将其中一个交集结点尾插到新 A 表中
r = p;
p = p->next;
s = q;//s 指针辅助删除另一个交集结点
q = q->next;//q 指针继续遍历,两指针继续比较
free(s);
}
}
//别忘了A表或B表还有剩余的情况
while (p != NULL) {//若循环结束原 A 表有剩余结点则全部删除释放空间
s = p;
p = p->next;
free(s);
}
while (q != NULL) {//若循环结束原 B 表有剩余结点则全部删除释放空间
s = q;
q = q->next;
free(s);
}
r->next = NULL;//尾插结束后尾结点指针域需置空
free(B);
return A;
}
void Del_Min(LinkList& L) {
LNode* pre = L, * p = L->next;//定义遍历指针 p 及其前驱指针 pre
LNode* minpre = pre, * minp = p;//记录所遍历过的结点中最小值结点的位置
//查找
while (p != NULL) {//遍历单链表
if (p->data < minp->data) {//判断所遍历结点是否小于之前记录的最小结点
minp = p;//若小于,则更新最小值结点位置
minpre = pre;
}
pre = p;//继续遍历
p = p->next;
}
//删除最小值
minpre->next = minp->next;//遍历结束,删除最小值结点
free(minp);//释放最小值结点空间
}
这是之前的删除最小值的代码,和本题相似。
void Print_min(LinkList& head) {
while (head->next != NULL) {//说明链表中还有元素
//之前写的那个寻找最小值的操作
LNode* pre = head, * p = head->next;
LNode* minpre = pre, * minp = p;
while (p != NULL) {//寻找最小值
if (p->data < pre->data) {//记录最小值
minp = p;
minpre = pre;
}
//不管上一步找到的是不是最小值,p和pre都得往后移
pre = p;
p = p->next;
}
//找到最小值了
printf("%d", minp->data);//输出此次循环找出的最小值结点数据
minpre->next = minp->next;//删除此次循环找出的最小值结点
free(minp);
}
free(head);
}
void Del_loop_min(LinkList& L) {
while (L->next != L) {//先while再定义结点,循环寻找最小值,直至数据结点都被找到并输出
LNode* pre = L, * p = L->next;//定义遍历指针 p 及其前驱指针 pre
LNode* minpre = pre, * minp = p;//记录每一次循环最小值结点
while (p != L) {//建议画图体会循环链表与普通链表循环条件的区别
if (p->data < pre->data) {//遍历结点是否小于之前记录的最小结点
minp = p;
minpre = pre;
}
pre = p;//继续遍历
p = p->next;
}
printf("%d", minp->data);
minpre->next = minp->next;
free(minp);
}
free(L);//释放头结点空间
}
int Pattern(LinkList A, LinkList B) {
LNode* p = A->next, * q = B->next;//定义遍历指针 p 和 q
LNode* pre = A->next;//定义辅助指针 pre,负责记录此次比较开始元素位置
while (p != NULL && q != NULL) {
if (p->data == q->data) {//若两遍历结点数据域相同则需继续遍历
p = p->next;
q = q->next;
}
else {//若两遍历结点数据域不同则证明此次比较失败
pre = pre->next;//指针 p 要借助 pre 去下一次比较的开始位置
p = pre;
q = B->next;//q 指针重新指向 B 表第一个结点开始新一次的比较
}
}
if (q == NULL)//若循环结束 q 指针指向空则证明此次比较成功,返回 1
return 1;
else//若 q 不为空则证明比较失败,返回 0
return 0;
}
定义一个fast指针和一个slow指针,fast指针一次走两步,slow指针一次走一步,若成环,则某一时刻他们会相遇。
结点个数奇偶数的判断条件不同,结点数为奇数,fast可以走两步,第一步有具体结点,第二步为NULL;结点数为偶数,fast最后一次一步都走不了;因此判断条件是fast != NULL && fast->next != NULL
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NfBGj8cy-1693057217464)(D:\Typorafile\Typorapicture\判断环.png)]
int FindLoop(LinkList L) {
LNode* fast = L, * slow = L;//分别定义快慢两个遍历指针并进行初始化
while (fast != NULL && fast->next != NULL) {//分别考虑结点数奇偶可画图理解
slow = slow->next;//慢指针每次遍历走一步
fast = fast->next->next;//快指针每次遍历走两步
if (slow == fast)//若快慢两个指针指向了同一位置则证明链表 L 有环
return 1;
}
return 0;//若循环正常结束则说明链表 L 没有环
}
LinkList Link(LinkList& h1, LinkList& h2) {
LNode* p, * q;
p = h1;
while (p->next != h1) {//循环使得 p 指针指向 h1 链表最后一个结点
p = p->next;
}
q = h2;
while (q->next != h2) {
q = q->next;
}
p->next = h2;
q->next = h1;
return h1;
}
typedef struct DNode {//循环双链表
int data;
struct DNode* prior, * next;
}DNode, *DLinkList;
int Func(DLinkList L) {
DNode* p = L->next, * q = L->prior;//两遍历指针以头结点为中心分别遍历两侧
while (p != q && q->next != p) {//需考虑结点是奇数还是偶数,强烈建议画图推理
if (p->data == q->data) {//若两遍历结点数据域相同则需继续遍历
p = p->next;
q = q->prior;
}
else//若两遍历结点数据域不同则证明循环双链表不对称,直接返回 0
return 0;
}
return 1;//若正常跳出循环则证明遍历结点数据域均相同,循环双链表对称
}
typedef struct DNode {//循环双链表
int data;
struct DNode* prior, * next;
}DNode, *DLinkList;
int Func(DLinkList L) {
DNode* p = L->next, * q = L->prior;//两遍历指针以头结点为中心分别遍历两侧
while (p != q && q->next != p) {//需考虑结点是奇数还是偶数,强烈建议画图推理
if (p->data == q->data) {//若两遍历结点数据域相同则需继续遍历
p = p->next;
q = q->prior;
}
else//若两遍历结点数据域不同则证明循环双链表不对称,直接返回 0
return 0;
}
return 1;//若正常跳出循环则证明遍历结点数据域均相同,循环双链表对称
}