>设置快慢指针
>用快的指针去追慢的指针
#include <iostream>
using namespace std;
// 定义链表节点结构
typedef struct LNode
{
int data; // 数据域
struct LNode *next; // 指向下一个节点的指针
} LNode, *LinkList;
// 尾插法构建链表
void TailInsert(LinkList &L)
{
int val = 0;
LNode *r = L; // r指向链表的尾部
while (cin >> val) // 循环输入值
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 赋值
r->next = s; // 尾插
r = s; // 更新r为新插入的节点
// 检查是否到达行尾
if (cin.get() == '\n')
{
break;
}
}
r->next = L; // 形成循环链表
}
// 遍历输出链表元素
void Print(LinkList L)
{
LNode *p = L->next; // 从第一个有效节点开始遍历
while (p != L) // 遍历直到回到头节点
{
cout << p->data << '\t'; // 输出节点数据
p = p->next; // 移动到下一个节点
}
cout << endl; // 换行
}
// 检测链表是否有环
void LoopNode(LinkList &L)
{
LNode *fast, *slow; // 定义两个指针,快指针和慢指针
fast = slow = L->next; // 都从链表的第一个有效节点开始
// 同时遍历链表
while (fast != NULL && fast->next != NULL) // 确保快指针不越界
{
slow = slow->next; // 慢指针每次走一步
fast = fast->next->next; // 快指针每次走两步
// 如果快慢指针相遇,则说明链表有环
if (fast == slow)
{
break;
}
}
// 检查链表是否无环
if (fast == NULL || fast->next == NULL)
{
cout << "链表无环" << endl; // 输出无环提示
return;
}
cout << "链表有环" << endl; // 输出有环提示
}
int main()
{
// 创建四个链表节点
LinkList L1 = new LNode;
LinkList L2 = new LNode;
LinkList L3 = new LNode;
LinkList L4 = new LNode;
// 构建链表并使其形成环
L1->next = L2; // L1指向L2
L2->next = L3; // L2指向L3
L3->next = L4; // L3指向L4
L4->next = L2; // L4指向L2,形成环
// 检测链表是否有环
LoopNode(L1);
}
>设置快慢指针
>利用两个指针的差
#include <iostream>
using namespace std;
// 定义链表节点结构
typedef struct LNode
{
int data; // 数据域
struct LNode *next; // 指向下一个节点的指针
} LNode, *LinkList;
// 尾插法构建链表
void TailInsert(LinkList &L)
{
int val = 0; // 用于存储输入的值
LNode *r = L; // r指向链表的尾部
r->next = NULL; // 初始化尾部指针
// 循环输入链表节点的值
while (cin >> val)
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 将输入值赋给新节点
r->next = s; // 将新节点连接到链表尾部
r = s; // 更新尾部指针为新节点
r->next = NULL; // 确保新节点的next指针为NULL
// 检查输入是否到达行尾
if (cin.get() == '\n')
{
break; // 如果是换行符,则停止输入
}
}
}
// 遍历输出链表元素
void Print(LinkList L)
{
LNode *p = L->next; // 从第一个有效节点开始遍历
while (p) // 遍历链表
{
cout << p->data << '\t'; // 输出当前节点的数据
p = p->next; // 移动到下一个节点
}
cout << endl; // 换行
}
// 查找链表中倒数第k个节点
void SearchK(LinkList &L, int k)
{
LNode *p, *q; // 定义两个指针,分别是快指针p和慢指针q
p = q = L->next; // 同时指向第一个有效节点
int count = 0; // 计数器,用于统计节点数量
while (p) // 遍历链表
{
// 计数,先找到第k个节点
if (count < k)
{
count++; // 计数器加1
}
// 当到达第k个节点之后,慢指针开始移动
else
{
q = q->next; // 慢指针移动到下一个节点
}
p = p->next; // 快指针移动到下一个节点
}
// 当快指针p到达链表尾部时,慢指针q指向的就是倒数第k个节点
if (count < k) // 如果节点总数小于k
{
cout << "查找失败" << endl; // 输出查找失败提示
return; // 结束函数
}
cout << q->data << endl; // 输出倒数第k个节点的数据
}
int main()
{
LinkList L = new LNode; // 创建一个链表头节点
L->next = NULL; // 初始化头节点的next指针
TailInsert(L); // 调用尾插法构建链表
SearchK(L, 3); // 查找倒数第3个节点并输出其数据
}
获取公共点
#include <iostream>
using namespace std;
// 定义链表节点结构
typedef struct LNode
{
char data; // 节点数据
struct LNode *next; // 指向下一个节点的指针
} LNode, *LinkList;
// 头插法
void HeadInsert(LinkList &L)
{
char val = '\0';
// 读取输入,直到遇到换行符
while (cin >> val)
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 设置节点数据
s->next = L->next; // 新节点指向当前链表的第一个节点
L->next = s; // 更新头指针指向新节点
if (cin.get() == '\n') // 如果读取到换行符,结束输入
{
break;
}
}
}
// 尾插法
void TailInsert(LinkList &L)
{
char val = 0;
LNode *r = L; // r指向链表的最后一个节点
// 读取输入,直到遇到换行符
while (cin >> val)
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 设置节点数据
r->next = s; // 将当前尾节点的next指向新节点
r = s; // 更新r为新节点
r->next = NULL; // 新节点的next指向NULL
if (cin.get() == '\n') // 如果读取到换行符,结束输入
{
break;
}
}
}
// 遍历输出链表元素
void Print(LinkList L)
{
LNode *p = L->next; // p指向链表的第一个节点
while (p) // 遍历链表
{
cout << p->data << '\t'; // 输出当前节点数据
p = p->next; // 移动到下一个节点
}
cout << endl; // 输出换行
}
// 查找两个链表的公共节点
void PublicNode(LinkList &LA, LinkList &LB)
{
int lena = 0, lenb = 0;
LNode *p = LA->next;
// 计算LA的长度
while (p)
{
lena++;
p = p->next;
}
p = LB->next;
// 计算LB的长度
while (p)
{
lenb++;
p = p->next;
}
LNode *longList, *shortList; // 定义两个指针分别表示长的和短的链表
int dist = 0;
// 判断哪个链表更长
if (lena > lenb)
{
longList = LA->next;
shortList = LB->next;
dist = lena - lenb; // 计算长度差
}
else
{
longList = LB->next;
shortList = LA->next;
dist = lenb - lena; // 计算长度差
}
// 让长的链表先遍历若干节点
while (dist--)
{
longList = longList->next;
}
// 同步遍历两个链表
while (longList != NULL)
{
// 如果相等,表示找到了公共节点
if (longList == shortList)
{
break;
}
else
{
longList = longList->next; // 移动到下一个节点
shortList = shortList->next; // 移动到下一个节点
}
}
// 打印公共节点后的数据
while (longList)
{
cout << longList->data << '\t'; // 输出数据
longList = longList->next; // 移动到下一个节点
}
}
int main()
{
LNode *p1 = new LNode; // 创建链表A的头节点
LNode *q1 = new LNode; // 创建链表B的头节点
LNode *q2 = new LNode; // 创建链表B的第二个节点
LNode *q3 = new LNode; // 创建链表B的第三个节点
// 创建公共节点
LNode *m1 = new LNode;
m1->data = 'm'; // 设置公共节点数据
LNode *m2 = new LNode;
m2->data = 'n'; // 设置公共节点数据
m1->next = m2; // m1指向m2
m2->next = NULL; // m2的next指向NULL
p1->next = m1; // 链表A的next指向公共节点m1
// 链表B的结构设置
q1->next = q2; // q1指向q2
q2->next = q3; // q2指向q3
q3->next = m1; // q3指向公共节点m1
// 查找并打印公共节点后的数据
PublicNode(p1, q1);
}
>用空间换时间
>开辟辅助数组保存链表中的绝对值
#include <iostream>
#include <string.h>
using namespace std;
// 定义链表节点结构
typedef struct LNode
{
int data; // 节点数据
struct LNode *next; // 指向下一个节点的指针
} LNode, *LinkList;
// 头插法
void HeadInsert(LinkList &L)
{
int val = '\0';
// 读取输入,直到遇到换行符
while (cin >> val)
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 设置节点数据
s->next = L->next; // 新节点指向当前链表的第一个节点
L->next = s; // 更新头指针指向新节点
if (cin.get() == '\n') // 如果读取到换行符,结束输入
{
break;
}
}
}
// 尾插法
void TailInsert(LinkList &L)
{
int val = 0;
LNode *r = L; // r指向链表的最后一个节点
// 读取输入,直到遇到换行符
while (cin >> val)
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 设置节点数据
r->next = s; // 将当前尾节点的next指向新节点
r = s; // 更新r为新节点
r->next = NULL; // 新节点的next指向NULL
if (cin.get() == '\n') // 如果读取到换行符,结束输入
{
break;
}
}
}
// 遍历输出链表元素
void Print(LinkList L)
{
LNode *p = L->next; // p指向链表的第一个节点
while (p) // 遍历链表
{
cout << p->data << '\t'; // 输出当前节点数据
p = p->next; // 移动到下一个节点
}
cout << endl; // 输出换行
}
// 删除链表中绝对值重复的节点
void DelValue(LinkList &L, int n)
{
LNode *p, *r, *pre; // 定义工作指针p、删除指针r和前驱指针pre
int x = 0; // 保存当前节点数据的绝对值
int arr[n + 1] = {0}; // 定义辅助数组,用于记录绝对值是否出现过
p = L->next; // p指向链表的第一个节点
pre = L; // pre指向头节点
while (p) // 遍历链表
{
x = p->data > 0 ? p->data : -p->data; // 获取当前节点数据的绝对值
// 如果绝对值没有出现过,则记录并继续
if (arr[x] == 0)
{
arr[x] = 1; // 标记该绝对值已出现
pre = p; // 更新前驱指针为当前节点
p = p->next; // 移动到下一个节点
}
// 如果绝对值已经出现过,则删除当前节点
else
{
r = p->next; // 记录下一个节点
pre->next = p->next; // 前驱节点的next跳过当前节点
delete p; // 删除当前节点
p = r; // 移动到下一个节点
}
}
}
int main()
{
LinkList L = new LNode; // 创建链表的头节点
TailInsert(L); // 调用尾插法插入数据
Print(L); // 输出链表元素
DelValue(L, 30); // 删除绝对值重复的节点
Print(L); // 再次输出链表元素
}
data
值的绝对值,这与一般删除重复值问题不同。要实现高效删除,可以使用一个哈希表来记录已经出现过的数值的绝对值。使用哈希表的原因是其查找和插入操作的时间复杂度为O(1)。这样我们可以在线性时间内(O(n))遍历链表,并处理每个节点是否需要删除。
next
指针,跳过当前节点。#include <iostream>
#include <string.h>
using namespace std;
// 定义链表节点结构体
typedef struct LNode
{
int data; // 节点数据
struct LNode *next; // 指向下一个节点的指针
} LNode, *LinkList;
// 头插法
void HeadInsert(LinkList &L)
{
int val = '\0'; // 初始化输入值
while (cin >> val) // 循环读取整数
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 设置节点数据
s->next = L->next; // 新节点指向当前链表的第一个节点
L->next = s; // 将新节点插入链表头部
// 如果输入回车,则结束输入
if (cin.get() == '\n')
{
break;
}
}
}
// 尾插法
void TailInsert(LinkList &L)
{
int val = 0; // 初始化输入值
LNode *r = L; // r指向链表的尾部(初始为头节点)
while (cin >> val) // 循环读取整数
{
LNode *s = new LNode; // 创建新节点
s->data = val; // 设置节点数据
r->next = s; // 当前尾节点的 next 指向新节点
r = s; // 更新 r 为新节点
r->next = NULL; // 新节点的 next 设置为 NULL
// 如果输入回车,则结束输入
if (cin.get() == '\n')
{
break;
}
}
}
// 遍历输出链表元素
void Print(LinkList L)
{
LNode *p = L->next; // 从链表第一个节点开始
while (p) // 当 p 不为空时
{
cout << p->data << '\t'; // 输出节点数据
p = p->next; // 移动到下一个节点
}
cout << endl; // 输出换行
}
// 变换链表
void ChangeList(LinkList &L)
{
LNode *p, *q, *r, *s;
p = q = L; // p 和 q 都指向头节点
// 找到链表的末尾节点
while (q->next)
{
p = p->next; // p 指向当前的最后一个节点
q = q->next; // q 向前移动一位
if (q->next != NULL)
{
q = q->next; // q 再向前移动一位,跳过一个节点
}
}
q = p->next; // q 指向中间部分的第一个节点
p->next = NULL; // 将后半部分的头节点的 next 设为 NULL
// 反转后半部分链表
while (q != NULL)
{
r = q->next; // 保存下一个节点
q->next = p->next; // 将当前节点插入到 p 后面
p->next = q; // 更新 p 的 next
q = r; // 处理下一个节点
}
s = L->next; // 保存前半部分的头节点
q = p->next; // q 指向反转后的链表
p->next = NULL; // 将 p 的 next 设为 NULL,表示结束
// 将后半部分插入到前半部分
while (q != NULL)
{
r = q->next; // 保存下一个节点
q->next = s->next; // 将后半部分节点插入到前半部分
s->next = q; // 更新前半部分的 next
s = q->next; // 移动前半部分的指针
q = r; // 处理下一个节点
}
}
int main()
{
LinkList L = new LNode; // 创建链表的头节点
TailInsert(L); // 调用尾插法插入节点
ChangeList(L); // 调用变换链表的函数
Print(L); // 输出变换后的链表
}