一.集合运算目的
        通过对集合交集,并集,差集运算来进一步熟悉和掌握链表的创建,删除,插入等一些基本的操作。
二.集合运算内容
      定义两个集合A和B:
           差集 :A与B的差集就是该元素属于A集合而不属于集合。并集:A与B相结合该元素既属于A又属于B。交集:A与B相同的一部分,既属于A又属于B。其实这个运算器有两种做法,一是重新malloc空间,将它们处理后的数都专门放到一个链表中去,这样虽然简单,易操作,但是空间的复杂度就高了;二是不malloc空间,我直接用A链为主链对其操作,在其上对链表的元素进行插入,删除,等操作,空间复杂度就可以不用考虑了。
三.集合运算代码



#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
typedef struct node
{
int data;
struct node *next;
}linklist;
linklist *initlist() //链表的初始化
{
int d;
linklist *p,*head,*q;
head=(linklist*)malloc(sizeof(linklist)); //具有头结点
head->next=NULL;
p=head;
scanf("%d",&d);
while(d)
{
q=(linklist*)malloc(sizeof(linklist));
q->data=d;
p->next=q; // 利用尾差法
p=q;
p->next=NULL;
scanf("%d",&d);
}
return head;
}
linklist *xiangjian(linklist *head1,linklist *head2) //集合的差运算
{
int flag=0;
linklist*p1,*p2,*q;
p1=head1;
p2=head1->next;
while(p2&&p2->next)
{
for(q=head2->next;q;q=q->next)
if(p2->data==q->data)
{
p1->next=p2->next;
free(p2);
p2=p1->next;
flag=1;
}
if(flag==0)
{
p1=p1->next;
p2=p2->next;
}
else
flag=0;
}

return head1;
}
linklist *hebing(linklist *head1,linklist *head2) // 集合的合并
{
int flag=0;
linklist *p,*q,*m;
p=head1->next;
while(p)
{
for(q=head2->next;q;q=q->next)
{
if(p->data==q->data)
{
flag=1;
break;
}

}
if(flag==0)
{
m=(linklist*)malloc(sizeof(linklist));
m->data=p->data;
m->next=head2->next;
head2->next=m;
}
flag=0;
p=p->next;
}
return head2;
}
linklist *jiaoji(linklist*head1,linklist *head2) // 集合的交集
{
linklist *p,*q,*m,*head;
head=(linklist*)malloc(sizeof(linklist));
head->next=NULL;
p=head1->next;
while(p)
{
for(q=head2->next;q;q=q->next)
if(q->data==p->data)
{
m=(linklist*)malloc(sizeof(linklist));
m->data=p->data;
m->next=head->next;
head->next=m;
break;
}
p=p->next;
}
return head;
}
int estimate(linklist *head) // 看链表里面是否有重复元素
{
linklist *p,*q;
if(head->next==NULL)
return 0;
for(p=head->next;p->next;p=p->next)
for(q=p->next;q;q=q->next)
if(p->data==q->data)
{
return 1;
}
return 0;
}
void print(linklist *head) //链表的输出
{
linklist *p;
p=head->next;
while(p)
{

printf("%d ",p->data);
p=p->next;
}
}
int main()
{
int x;
char ch;
linklist *head1,*head2,*head3,*head4,*head5;
printf("请输入链表1: ");
head1=initlist();
printf("请输入链表2: ");
head2=initlist();
if(estimate(head1)||estimate(head2))
{
printf("集合 1 或者 集合 2 不合法,自动退出\n");
exit(-1);
}
else
printf("\n创建列表成功\n");
Sleep(2000);
while(1)
{
system("cls");
printf("1 集合相减 \n");
printf("2 集合合并 \n");
printf("3 集合交集 \n");
printf(" 请选择(1-3)");
scanf("%d",&x);
switch(x)
{
case 1 :system("cls");
printf("输出链表1: ");
print(head1);
printf("\n 输出链表2: ");
print(head2);
head3=xiangjian(head1,head2);
printf("\n 集合相减: ");
print(head3);
printf("\n按任意继续.................\n");
getch();
break;
case 2 :system("cls");
printf("输出链表1: ");
print(head1);
printf("\n 输出链表2: ");
print(head2);
head4=hebing(head1,head2);
printf("\n 集合合并: ");
print(head4);
printf("\n按任意继续.................\n");
getch();
break;
case 3 :system("cls");
printf("输出链表1: ");
print(head1);
printf("\n 输出链表2: ");
print(head2);
head5=jiaoji(head1,head2);
printf("\n 集合交集: ");
print(head5);
printf("\n按任意继续.................\n");
getch();
break;
default:system("cls");
printf("你的输入有误 ");
printf("\n按任意继续.................\n");
getch();
break;
}
printf("\n如果想退出,请按n\n");
ch=getch();
if(ch=='n')
break;
}
printf("程序已完成,谢谢\n");
return 0;
}

四.集合运算分析

     差集:将A和B链表传入差集函数xiangjian(head1,head2),定义3个指针变量,p1总是p2的前驱节点,遇到相等就把A链中的值删除了,p2和p1重新指向,用flag标记遇到相等转变一次,否则,p1和p2同时向后面移动,主要思路还是两层循环,在A链一层循环基础上让B链进行第二层循环遍历,对值进行判断。

    并集:将A和B链表传入合并函数hebing (head1,head2),还是用到3个指针变量,在A链一层循环基础上让B链进行第二层循环遍历是,一遇相等的值,直接break跳出,标记flag=1;进行下一次。当遍历B链表完之后,没有相等的值,那么给m开辟空间,利用头插法插在B链表的前面,最后返回head2.

   交集:将A和B链表传入交集函数jiaoji (head1,head2),对于这个函数,我重新开辟了一个链表,继续两层循环,和上面的思路一样,如果遇到相等的,就把该值放在新开辟的链表中。

五. 总结

    我这次做的集合只考虑数字,用int而没有用char,如果出现字母我的就不能运行了,我也想过用字符做,我认为最应该考虑的地方输入问题,字符是一个一个读入的,连续输入就能出现95,110等多位数字,这就需要用到空格隔开处理,其他的做法和整型一样的。