37.递增的单链表,去掉表中重复的元素,只保留一个数值
示例
输入:1 -> 2 -> 2 -> 3 -> 3->4->5-> 5 输出:1->2->3->4->5
说明:在递增的单链表中,重复的元素包括 2,3 和 5,删除后只保留了一个数值,得到的单链表为 1->2->3->4->5。
思路
1. 递增有序:
因为链表是递增有序的,所以默认第一个是不重复的元素,将第一个与下一个进行比较,若相等则用临时指针指住下一个值的位置,将当前指针指向下下个位置,完成删除。
2.对于不是递增有序的
- 遍历链表,从虚拟头节点开始,每次判断下一个节点,是否为首次出现,(其中
recorde[val] == 0表示首次出现,否则不是),如果是首次出现就将该节点记录在辅助数中record中,否则删除该节点。 - 每次判断下一个节点,是否为首次出现,而不是判断当前节点,是为了方便删除操作,这样每次删除一个节点的时候,我们都可以快速的拿到该节点的前驱节点。
- 辅助数组中
record[key] = val,其中key为节点的数据,val为该节点出现的次数。
void DelDupNode(LinkList& L) {
LNode* cur = L, * tmp; // 遍历指针,临时指针
unordered_map<int, int> record; // 辅助数组,记录各个节点出现的次数
while (cur->next) {
int val = cur->next->val; // 数据域
if (record[val] == 0) { // 对于第一次出现的节点,把它记录到辅助数组中
record[val] = 1;
cur = cur->next;
}
else {
tmp = cur->next; // 重复节点
cur->next = cur->next->next; // 防止断链
delete tmp; // 删除重复节点
}
}
}
int main() {
LNode* cur = L->next;
cout << "去重后的链表:";
while (cur) {
cout << cur->val << ' ';
cur = cur->next;
}
}
代码实现
//删除重复元素
#include <stdio.h>
#include <stdlib.h>
//删除重复元素
typedef struct LNode { //定义链表的结构体
int data; //数据域
struct LNode* next;//指针域
} LNode, * LinkList;
//创建链表-尾插法
LinkList ListInsert(int n) { // n代表链表的结点个数
LNode* L = (LinkList)malloc(sizeof(LNode)); //创建头结点
L->next = NULL; //初始为空链表
LNode* s, * r = L; // s是待插入的结点指针,r是尾指针
int val; //待插入的结点数据
while (n-- && scanf_s("%d", &val)) {
s = (LNode*)malloc(sizeof(LNode)); //创建新结点
s->data = val;
r->next = s; // r链接s
r = s; // r指向新的表尾结点
}
printf("创建成功\n");
return L->next; // 返回真正的头节点
}
//删除重复的值
LNode* DeleteDupList(LNode* L) {
// 如果链表为空或只有一个节点,直接放回原链表
if (L == NULL || L->next == NULL) {
return L;
}
LNode* p = L->next, * temp; //移动指针p指向头结点,temp临时指针
while (p->next != NULL) {
temp = p->next; // temp指向*p的后继结点
if (p->data == temp->data) { //找到重复的结点
p->next = temp->next; //释放*temp结点
free(temp);
}
else {
p = p->next;
}
}
return L;
}
int main() {
//创建链表
int n;
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入%d个数据,递增有序:", n);
LinkList L = ListInsert(n); //创建链表 [,测试数据[1,1,2],直接将n替换为3]
// 输出去重后的链表
LNode* newL = DeleteDupList(L);
LNode* cur = newL;
printf("删除重复元素后的序列:");
while (cur != NULL) {
printf(" %d", cur->data);
cur = cur->next;
}
return 0;
}
运行结果
vs2022上运行时总报错:
别人可运行出来
复杂度
- 时间复杂度O(n)--- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
- 空间复杂度 O(1)--- 单链表为必要空间仅需常数级变量
38.判断带头结点的循环双链表是否对称
示例
输入:1 2 2 1
输出:这个是对称的
输入:1 2 3 1 2
输出 :这个不是对称的
思路
让P从左向右扫描,q从右向左扫描,直到他们指向同一结点(p==q,当循环双链表中节点个数等于奇数时)或相邻(q->prior=p,当循环双链表中节点个数为偶数时)为止,若他们指向结点值相同,则继续下去,否则返回false,.若比较全部相等,,则返回true.
代码实现
//双链表是否对称
#include<stdio.h>
#include<stdlib.h>
// 定义双链表节点结构体
typedef struct DNode {
int data;
struct DNode* next,*prior;//后继指针和前驱指针
}DNode,*DLinkList;
//从两头扫描循环双链表,以判断链表是否对称
bool symmetry(DLinkList L) {
DNode* p = L->next, * q = L->prior;//p指向头指针,q指向尾指针
while (p != q && p->next != q) {
if (p->data == q->data) {//所指结点值相同则继续比较
p = p->next;
q = q->prior;
}
else {
return false;
}
return true;
}
}
int main() {
// 创建循环双链表测试输入:1 2 2 1输出:这个是对称的
DNode* L = (DNode*)malloc(sizeof(DNode)); // 虚拟头节点,不存储实际的数据
DNode* n1 = (DNode*)malloc(sizeof(1));
DNode* n2 = (DNode*)malloc(sizeof(2));
DNode* n3 = (DNode*)malloc(sizeof(2));
DNode* n4 = (DNode*)malloc(sizeof(1));
L->next = n1;
n1->prior = L;
n1->next = n2;
n2->prior = n1;
n2->next = n3;
n3->prior = n2;
n3->next = n4;
n4->prior = n3;
n4->next = L;
L->prior = n4;
// 判断链表是否对称
if (symmetry(L)) {
printf( "这个是对称的" );
}
else {
printf("这个不是对称的");
}
return 0;
}
运行结果
复杂度
- 时间复杂度O(n)--- 其中n是链表的长度。因为链表中的每个节点都只会被遍历一次。
- 空间复杂度O(1)---双链表为必要空间,除此仅需常数级变量