一.集合运算目的
通过对集合交集,并集,差集运算来进一步熟悉和掌握链表的创建,删除,插入等一些基本的操作。
二.集合运算内容
定义两个集合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等多位数字,这就需要用到空格隔开处理,其他的做法和整型一样的。