有关集合算法的实现一些学习笔记

前言:手机太烂不好意思在地铁上拿出来玩,于是看了会算法与数据结构,想看看以前在学校没好好学的一些东西,于是乎今天看了集合这一块,以前没好好学,工作后也没用到,最近有个想法想做个东西出来,貌似要用到集合。不多说,讲正题。
(欢迎转载,转载请注明出处。谢谢。)
    一、基本概念
        这里集合的概念与我们数学中所学的集合一样,主要运算也是并、交、差、子、相等。数学中怎么理解后面相关概念就怎么理解,不懂得回去读高中。
二、集合的抽象数据类型
ADT Set is
operations
    Set createEmptySet();//创建一个空集合
    int member(set A,DataType x);//当x属于A时返回真值,否则返回假值
    int insert(Set A,DataType x);//使x成为A的一个成员,如果x本来就是A的成员,则A不变
    int delete(Set A,DataType x);//将x从A中删除,如果x本来就不在A中,则A不变
    int union(Set A,Set B,Set C);//求集合A和B的并集,结果放在集合C中
    int intersection(Set A,Set B,Set C);//求集合A与B的交集,结果放在C中
    int difference(Set A,Set B,Set C);//求集合A与B的差集,结果放在C中
    int subset(Set A,Set B);//当且仅当A是B的子集时,函数才为真
end ADT Set
三、集合的位向量表示
    位向量即用一个二进制位来表示一个元素存不存在某个集合中,当所表示的集合存在某个不太大的公共超集时,采用位向量的方式来表示这种集合往往很有效。
    一个二进制位只有两种状态,是0或者是1,他能够表示的信息非常简单。例如能够表示一扇门开或者关,一个条线路的通路或者断路。同样,也可以用一个位来表示某个东西存在或不存在。在位向量里每个二进制位都有一个固定的位置,不同的位置的二进制位可以用来表示不同的东西。
    假设需要表示的集合的公共超集中,共有n个不同的元素(n是一个不大的正整数),为叙述方便,可以将这n个元素与整数0,1,2,···,n-1建立一一对应的关系。每个集合可以用有n位的位向量来表示。若整数i是这个集合的一个元素,则位向量中的下标为i的位为1(真),否则为0(假)。这种表示使得元素的整数值就不需要存放了,大大节省了空间。
    在C语言中无法直接定义位数组。于是我们采用长度为n/8的字符数组表示长度为n的字符数组。一个字符占8位二进制编码。
    假设n是位向量的位数,因为n不一定是8的整数倍,所以用(n+7)/8的值来决定能容下所有位向量的最少字节数。
位向量表示的存储结构
typedef struct{
    int size;                    /*字符数组长度*/
    char* array;             /*位向量空间,每一数组元素保存8位*/   
}BitSet;
    还有一个细节需要考虑:在一个字符空间中,位向量的下标应该如何排列?一种自然的想法是,字符的8位从左自右下标递增排列。但这种排列给集合类型许多操作带来不便,所以选择了相反的排列。
    图1(a)给出了一个公共超集是从0到9的整数集合,采用位向量表示的存储结构从途中可以看出每个整数所对应的二进制的位置。当集合S={1,3,5,7,9}时,它的实际存储状态如图1(b)所示。
图1(a)

 

 

图片

 

 

图1(b)

 

图片

 

    用位向量表示集合时,所占空间大小与公共超集的大小n成正比,而与要表示的集合大小无关。
算法的实现
算法1. 创建空集合
BitSet* createEmptySet(int n){                /*创建n位的位向量 000...0*/
    int i;
    BitSet* s=(BitSet*)malloc(sizeof(Bitset));
    if(s!=NULL){
        s->size = (n+7)/8;
        s->array = (char*)malloc(s-<size * sizeof(char));
        if(s->array != NULL){
            for(i=0;i<s->size;i++)s->array[i] = '\0';
            return s;
            }
    }
    return NULL;
}
 
算法2. 将整数index的元素插入集合
int insert(BitSet* s,int index){
    if(index >=0 && index>>3 < s->size)
        {s->array[index>>3] |= (1<< (index & 7) );return 1}
    return 0;
}
 
算法3. 将整数index的元素从集合中删除
int delete(BitSet* s,int index){
    if(index>=0 && index>>3 <s->size)
    {s->array[index >> 3] & =~(1 << (index & 7));return 1}
    return 0;
}
 
算法4. 判断整数index的元素是否属于集合
int member(BitSet* s,int index){
    if( index>=0 && index>>3 <s->size && (s->array[index>>3] & (1<<(index&7) ) ))
    return 1;
    return 0;
}
 
算法5. 集合与集合的并
int union(BitSet* s0,BitSet* s1,BitSet* s2){
    int i;
    if(s0->size != s1->size || s2->size != s1->size)return 0;
    for(i =0 ;i<s1->size;i++)
        s2->array[i] = s0->array[i] | s1->array[i];
    return 1;
}
 
算法6. 集合与集合的交
int intersection(BitSet* s0, BitSet *s1, BitSet *s2){
    int i;
    if(s0->size != s1->size || s2->size != s1->size)return 0;
    for(i =0 ;i<s1->size;i++)
        s2->array[i] = s0->array[i] & s1->array[i];
    return 1;
}
 
算法7. 集合与集合的差
int difference(BitSet* s0, BitSet *s1, BitSet *s2){
    int i;
    if(s0->size != s1->size || s2->size != s1->size)return 0;
    for(i =0 ;i<s1->size;i++)
        s2->array[i] = s0->array[i] & ~s1->array[i];
    return 1;
}
 
四、集合的单链表表示
    用单链表来表示集合时我们每个节点存放元素实际的值,而不是是否属于集合的标记。
    存储结构
struct Node;
typedef struct Node* PNode;
struct Node{
    DataType info;
    PNode link;
};
typedef struct Node* LinkSet;
    因为我们讨论的是有序集,如果将所有元素按"<"关系排序构造有序链表,会给某些运算带来方便。
算法8. 求单链表表示集合的交集
int intersectionLink(LinkSet s0,LinkSet s1,LinkSet s2){
    PNode x;
    if(s0 == NULL || s1 == NULL || s2 ==NULL){printf("no head node error");return 0;}
    s2->link =NULL;
    s0=s0->link;s1=s1->link;
    while(s0!NULL && s1 !=NULL)
        if(s0->info < s1->info)s1 = s1->link;
        else if(s0->info == s1->info)s0 = s0->link;
        else if(s0->info == s1->info)
        {
            x = (PNode)malloc(sizeof(struct Node));
            if(x==NULL){printf("Out of space!");return 0;}
            x->info = s0->info;x->link = NULL; s2=s2->link;
        }
    return 1;
}
 
算法9. 集合的赋值
int assignLink(LinkSet s0,LinkSet s1){
    PNode x;
     if(s0 == NULL || s1 == NULL ){printf("no head node error");return 0;}
     s0->link =NULL;
     s1=s1->link;
    while(s1!=NULL){
           x = (PNode)malloc(sizeof(struct Node));
            if(x==NULL){printf("Out of space!");return 0;}
            x->info = s1->info;x->link = NULL; s0->link=x;
            s1=s1->link; s0=s0->link;
    }
    return 1;
}
 
算法10. 有序链表表示的集合中的插入操作
int insetLink(LinkSet s0, DataType x){
    PNode temp;
     if(s0 == NULL ){printf("no head node error");return 0;}
     temp = (PNode)malloc(sizeof(struct Node));
    if(temp == NULL)){printf("Out of space!");return 0;}
     while(s0->link!=NULL){
            if(s0->link->info == x){printf("data already exist !");return 1;}
            else if(s0->link->info < x)s0 = s0->link;
             else if(s0->link->info > x){
                temp->info =x; temp->link = s0->link;
                s0->link =temp;return 1;
            }
            if(s0->link==NULL){
                temp->info =x; temp->link = s0->link;
                s0->link =temp;return 1;
            }
    }
}
 
//THE END
好晚了,睡觉觉了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值