
- 单链表练习题-删除无序单链表中所有的重复元素(两种方法,C语言实现)
- 一、前言
- 二、两种实现方式与优劣
- 2.1 使用哈希存储
- 2.2 使用两重循环
- 三、全部代码
- 四、测试
与前一篇单链表练习题-删除有序单链表中的重复元素(C语言实现)不同,本次实现的是删除单链表中所有的重复元素,无论数据是否有序。
二、两种实现方式与优劣 2.1 使用哈希存储 哈希存储是一种典型的以空间换时间的方法,之前用它来统计数字出现的次数,参见:C语言统计数字出现的次数
所以其优点是:时间复杂度为O(n)
缺点是空间复杂度较高,尤其是数据比较稀疏的时候,显然有点浪费内存。
实现代码如下:
bool Del_All_duplicate1(linkList *L)
{
int arr[N] = {0};//初始化为0
linkList *pre = L->next;
linkList *p = L->next;
while(p->next!=NULL)
{
if(arr[p->data]>0)//如果这个元素已经出现过了,那就删除p指向的结点
pre->next = p->next;
else
{
arr[p->data]++;//该元素是第一次出现就加一
pre = p;
}
p = p->next;
}
return true;
}
如上,用数组arr存储数字出现的次数,我这里定义了N为100,所以数据输入时应该不大于99,用了一个pre方便执行删除 *** 作。只用了一个while循环就可以实现删除所有重复的元素,因为这是边遍历边删除。时间复杂度为O(n)。
2.2 使用两重循环 思路:定义两个指针,p表示外循环,q表示内循环,同样用一个pre记录前驱方便删除。外循环p正常遍历链表,例如当p指向第一个元素时,令pre等于p,令q指向第二个元素,依次往后扫描是否有出现与p值相等的情况,如果有即删除,没有内循环就继续往后遍历,直到链表末尾,当外循环也走到末尾时,循环结束,此时已经得到了不重复的序列。
优点:省内存 缺点:由于是双重循环,所以时间复杂度较高,为O(n2)
实现代码如下:
bool Del_All_duplicate2(linkList *L)
{
linkList *p = L->next;
linkList *q,*pre;//用pre是为了方便删除 *** 作
while(p->next!=NULL)//外循环
{
pre = p;
q = p->next;
while(q->next!=NULL)//内循环
{
if(q->data==p->data)
pre->next = q->next;//删除q指向的结点
//q后移的同时也修改pre
pre = q;
q = q->next;//q后移
}
p = p->next;//p后移
}
return true;
}
三、全部代码
#include四、测试#include #include //根据C99标准,C语言使用bool类型需要添加这个头文件 #define N 100 //定义哈希表的最大长度,使用这个方法时数据最大不得超过99 typedef int ElemType; typedef struct linkNode { ElemType data; struct linkNode *next; }linkList; //------------ 函数声明 ----------// void MainMenu(); bool InitlinkList(linkList *L);//初始化 bool InsertlinkList(linkList *L,ElemType e);//插入 void PrintAll(linkList *L);//输出所有元素 bool Del_All_duplicate1(linkList *L);//删除重复元素--哈希表 bool Del_All_duplicate2(linkList *L);//删除重复元素--双重循环 //------------ 函数声明 ----------// int main() { linkList L; int ch; ElemType element; if(InitlinkList(&L)) printf("初始化成功!n"); else printf("初始化失败!n"); while(1) { MainMenu(); printf("请输入您要执行的 *** 作:"); scanf("%d",&ch); switch(ch) { case 0: printf("感谢使用,已退出!"); exit(0); break; case 1: printf("请输入您要插入的元素:n"); scanf("%d",&element); if(InsertlinkList(&L,element)) printf("插入成功!n"); else printf("插入失败!n"); break; case 2: PrintAll(&L); break; case 3: if(Del_All_duplicate1(&L)) printf("删除成功!n"); else printf("删除失败!n"); break; default: printf("您输入的 *** 作指令有误!请重新输入!"); } } return 0; } //主菜单,显示 void MainMenu() { printf("nnn"); printf("t **** 删除单链表中所有的重复元素 ****nn"); printf("t ------- 0.退出 nn"); printf("t ------- 1.插入元素nn"); printf("t ------- 2.输出所有元素nn"); printf("t ------- 3.删除重复元素nn"); printf("t *************************************n"); } //初始化单链表(带头结点) bool InitlinkList(linkList *L) { //先申请一个头结点 linkList *head = (linkList *)malloc(sizeof(linkList)); L->next = head; head->next = NULL;//头结点之后一开始还没元素 return true; } //插入 bool InsertlinkList(linkList *L,ElemType e) { //头插法 linkList *p = L;// //申请一个新的结点 linkList *s = (linkList *)malloc(sizeof(linkList)); s->data = e;//赋值 //修改指针 将结点s插入到结点p之后 s->next = p->next;//s指针指向 p->next = s; return true; } void PrintAll(linkList *L) { linkList *q; q = L->next; //打印出所有元素 while(q->next!=NULL) { printf("%d ",q->data); q = q->next; } } bool Del_All_duplicate1(linkList *L) { int arr[N] = {0};//初始化为0 linkList *pre = L->next; linkList *p = L->next; while(p->next!=NULL) { if(arr[p->data]>0)//如果这个元素已经出现过了,那就删除p指向的结点 pre->next = p->next; else { arr[p->data]++;//该元素是第一次出现就加一 pre = p; } p = p->next; } return true; } bool Del_All_duplicate2(linkList *L) { linkList *p = L->next; linkList *q,*pre;//用pre是为了方便删除 *** 作 while(p->next!=NULL)//外循环 { pre = p; q = p->next; while(q->next!=NULL)//内循环 { if(q->data==p->data) pre->next = q->next;//删除q指向的结点 //q后移的同时也修改pre pre = q; q = q->next;//q后移 } p = p->next;//p后移 } return true; }
1.先随便输入几次元素,得到一个序列:
2.接着执行删除重复元素 *** 作:
3.查看删除的结果:
经测试,两种方法的结果都正确。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)