单链表练习题-删除无序单链表中所有的重复元素(两种方法,C语言实现)

单链表练习题-删除无序单链表中所有的重复元素(两种方法,C语言实现)

一、前言

​ 与前一篇单链表练习题-删除有序单链表中的重复元素(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<stdio.h>
#include<stdlib.h>
#include<stdbool.h> //根据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("\n\n\n");
	printf("\t      **** 删除单链表中所有的重复元素 ****\n\n"); 
	printf("\t      -------	0.退出 \n\n");
	printf("\t      -------	1.插入元素\n\n");
	printf("\t      -------	2.输出所有元素\n\n");
	printf("\t      -------	3.删除重复元素\n\n");
	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.查看删除的结果:
在这里插入图片描述
经测试,两种方法的结果都正确。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值