这一章详细研究一个搜索问题:在没有其他相关数据的情况下,如何存储一组数据。(这个问题的搜索特性主要体现在向数据集合容器中插入新的数据,但还需要保证容器中没有相同的数据,这就涉及到了搜索容器的问题。)具体的问题定义是这样的:生成[0, maxval)范围内m个随机整数的序列,不允许重复。
根据上一张的内容,解决这个问题主要任务是实现如下伪代码:
initialize set S to empty
size = 0
while size < m do
t = bigrand() % maxval
if t is not in S
insert t into S
size++
print the elements of S in sorted order
文中将待生成的数据结构成为IntSet,指整数的集合。文中把这个接口定义为C++类:
class IntSetImp
{
private:
public:
IntSetImp(int maxelements, int maxval);
void insert(int t);
int size();
void report(int *v);
}
1.IntSetSTL:开始时程序用C++标准模板库中的set模板实现数据集合,因为set容器中已经默认数据是无重复且按序排列的,并且提供了插入insert()函数和容器大小的size()函数,故实现起来很方便。
2.IntSetArr, IntSetList, IntSetList2:后来书中给出了“线性结构”的两个例子,分别是数组和单链表。数组和单链表的实现中都用到了哨兵节点sentinel来简化代码提速。两种实现的区别在私有数据中体现得有所不同,虽然都有记录数据个数的n,但链表中需要自己定义节点类型struct node,而在数组中有定义一维数组(并在构造函数中申请足够的空间为了数据存储做准备,但是在链表开始的实现中由于结点是单独动态申请的故初始化函数没有申请这部分)。后来对链表结构进行了改进:一是消除了insert函数的递归,这其中对链表的头结点需要格外的注意,因为要保持对插入节点后的链表的搜索,第二个方法用“指向指针的指针”来消除这个问题。二是将内存分配一次性地写到初始化中,这样可以减少空间和时间的消耗。
3.IntSetBST,IntSetBST2:利用二分搜索树的结构,可以降低搜索的时间复杂度,实现方法类似链表。开始的实现没有哨兵结点,后来设置哨兵结点,同样也在消除递归和一次内存分配处做出改进。
4.IntSetBitVec, IntSetBins, IntSetBins2:利用整数的特性可以使用位图的结构和算法来实现,这在第一章中已经利用过,这个方法的特点是在maxelement接近maxval,并且maxval比较小可以全部装入内存时比较高效,但是当maxval很大而maxelement又小的时候则是浪费了很多的空间,因为其初始化中的内存分配是按照maxval来的(这一点与其他结构不同)。而箱的算法结合了链表和位向量的优点,箱可以看做一种散列,而箱中的整数用一个有序链表表示,由于整数是均匀分布,所以每个链表的期望长度都为1。改进的方法还是在消除递归和一次性内存分配。
5.genfloyd:在m接近n时,伪代码过程生成的很多随机数都被丢弃,这时用到了floyd的算法,在上一章中在set容器中很容易实现,因为set自备find()函数,在本章的数据结构中,没有自定义的find()函数,但还是可以用size()对算法加以实现!
最后截个图和附加代码:
“若m相对n比较小,这些结构的平均性能如下所示(b表示每个元素的位数)”:
// set implementations on random numbers
#include <iostream>
#include <set>
using namespace std;
class IntSetSTL
{
private:
set<int> S;
public:
IntSetSTL(int maxelement, int maxval){}
void insert(int t){ S.insert(t);}
int size(){return S.size();}
//set<int>::iterator find(int i){return S.find(i);}
void report(int *v)
{
int i=0;
set<int>::iterator iter;
for(iter=S.begin();iter!=S.end();++iter)
v[i++]=*iter;
}
};
class IntSetArr
{
private:
int n, *x;
public:
IntSetArr(int maxelement, int maxval)
{
int i;
x = new int[maxelement+1];
//for(i=0;i<maxelement;i++)
// x[i]=0; // set empty
x[0]=maxval;
n=0;
}
void insert(int t)
{
int i, j;
for(i=0;x[i]<t;i++)
;
if(x[i]==t)
return;
else
{
for(j=n;j>=i;j--) // j>=i
x[j+1]=x[j];
x[i]=t;
n++;
}
}
int size(){return n;}
void report(int *v)
{
int i;
for(i=0;i<n;i++)
v[i]=x[i];
}
};
class IntSetList
{
private:
int n;
struct node
{
int val;
node *next;
node(int a, node *p){val=a; next=p;}
}; //
node *head, *sentinel;
node *rinsert(node *p, int t)
{
if(p->val<t)
p->next=rinsert(p->next, t);
else if(p->val>t)
{
p=new node(t, p);
n++;
}
return p;
}
public:
IntSetList(int maxelements, int maxval)
{
sentinel=head=new node(maxval, 0);
n=0;
}
void insert(int t){head=rinsert(head, t);}
// non-recursive
void insert2(int t)
{
node *p;
if(head->val==t)
return;
else if(head->val>t)
{
head = new node(t, head);
n++;
return;
}
for(p=head; p->next->val<t; p=p->next)
;
if(p->next->val==t)
return;
else
{
p->next=new node(t, p->next);
n++;
}
}
// non-recursive with pointer to pointer to avoid the problem of "head node handling"
void insert3(int t)
{
node **p;
for(p=&head; (*p)->val<t; p=&((*p)->next))
;
if((*p)->val==t)
return;
*p=new node(t, *p);
n++;
}
int size(){return n;}
void report(int *v)
{
int i=0;
node *q;
for(q=head;q!=sentinel;q=q->next)
v[i++]=q->val;
}
};
// Improved List: change from new per node to one new at init (non-recursive call)
class IntSetList2
{
private:
int n;
struct node
{
int val;
node *next;
//node(int a, node *p){val=a; next=p;} no construct funcion
};
node *head, *sentinel, *freenode;
public:
IntSetList2(int maxelements, int maxval)
{
freenode=new node[maxelements];
sentinel=head=new node;
sentinel->val=head->val=maxval;
n=0;
}
void insert(int t)
{
node **p;
for(p=&head; (*p)->val<t; p=&((*p)->next))
;
if((*p)->val==t)
return;
//*p=new node(t, *p);
freenode->val=t;
freenode->next=*p;
//freenode++;
*p=freenode++;
n++;
}
int size(){return n;}
void report(int *v)
{
int i=0;
node *q;
for(q=head;q!=sentinel;q=q->next)
v[i++]=q->val;
}
};
class IntSetBST
{
private:
int n, i, *x; //
struct node
{
int val;
node *lchild, *rchild;
node(int a){val=a; lchild=rchild=0;}
};
node *root;
node *rinsert(node *p, int t)
{
if(p==0)
{
p=new node(t);
n++;
}
else if(p->val<t)
p->rchild=rinsert(p->rchild, t);
else if(p->val>t)
p->lchild=rinsert(p->lchild, t);
return p;
}
void traverse(node *p) // how
{
if(p==0)
return;
traverse(p->lchild);
x[i++]=p->val;
traverse(p->rchild);
}
public:
IntSetBST(int maxelements, int maxval){root=0; n=0;}
void insert(int t){root=rinsert(root, t);}
int size(){return n;}
//void report(int *v){traverse(root);}
void report(int *v){x=v; i=0; traverse(root);} //
};
// Improved BST: one new at init for recursive call and non-recursive call(with sentinel)
class IntSetBST2
{
private:
int n, i, *x; //
struct node
{
int val;
node *lchild, *rchild;
//node(int a){val=a; lchild=rchild=0;}
};
node *root, *sentinel, *freenode;
node *rinsert(node *p, int t)
{
/*
if(p==0)
{
freenode->val=t;
freenode->lchild=freenode->rchild=0;
p=freenode++;
n++;
}
*/
if(p==sentinel)
{
p=freenode++;
p->lchild=p->rchild=sentinel;
p->val=t;
n++;
}
else if(p->val>t)
p->lchild=rinsert(p->lchild, t);
else if(p->val<t)
p->rchild=rinsert(p->rchild, t);
return p;
}
void traverse(node *p) // how
{
//if(p==0)
if(p==sentinel)
return;
traverse(p->lchild);
x[i++]=p->val;
traverse(p->rchild);
}
public:
// 把以前的null指针都指向哨兵结点,哨兵在构造函数中进行初始化
IntSetBST2(int maxelements, int maxval)
{
sentinel=root=new node;
freenode = new node[maxelements];
n=0;
}
void insert(int t){root=rinsert(root, t);}
// 非递归,有哨兵节点
void insert2(int t)
{
sentinel->val=t;
node **p=&root;
while((*p)->val!=t) // 不能设置条件为 *p!=sentinel
{
if((*p)->val<t)
p=&((*p)->rchild);
else
p=&((*p)->lchild);
}
if(*p==sentinel) // 到达了哨兵节点
{
freenode->val=t;
freenode->lchild=freenode->rchild=sentinel;
*p=freenode++;
n++;
}
/*
{
*p=freenode++;
(*p)->val=t;
(*p)->lchild=(*p)->rchild=sentinel;
n++;
}
*/
}
int size(){return n;}
void report(int *v){x=v; i=0; traverse(root);} //
};
class IntSetBitVec
{
private:
//enum{BITSPERWORD=32};
enum{BITSPERWORD=32, SHIFT=5, MASK=0x1f };
int n, hi, *x; // use hi to restore maxval
void set(int i) {x[i>>SHIFT] |= (1<<(i & MASK));}
//void set(int i) { x[i/BITSPERWORD] |= (1<<(i%BITSPERWORD));}
//void clr(int i) { x[i/BITSPERWORD] &= (0<<(i%BITSPERWORD));} // this is not correct
//void clr(int i) { x[i/BITSPERWORD] &= ~(1<<(i%BITSPERWORD));}
void clr(int i) {x[i>>SHIFT] &= ~(1<<(i & MASK));}
//int test(int i) { return x[i/BITSPERWORD] & (1<<(i%BITSPERWORD));}
int test(int i) { return x[i>>SHIFT] & (1<<(i & MASK));}
public:
IntSetBitVec(int maxelement, int maxval)
{
hi=maxval;
x = new int[1+hi/BITSPERWORD];
for(int i=0;i<hi;i++)
clr(i);
n=0;
}
void insert(int t)
{
if(test(t))
return;
else
{
set(t);
n++;
}
}
int size(){return n;}
void report(int *v)
{
int j=0;
for(int i=0;i<hi;i++)
{
if(test(i))
v[j++]=i;
}
}
};
class IntSetBins
{
private:
int n, bins, hi;
struct node
{
int val;
node *next;
node(int a, node *p){val=a; next=p;}
};
node **bin, *sentinel;
node *rinsert(node *p, int t)
{
if(p->val<t)
p->next=rinsert(p->next, t);
if(p->val>t)
{
p=new node(t, p);
n++;
}
return p;
}
public:
IntSetBins(int maxelements, int maxval)
{
hi=maxval;
bins=maxelements;
bin = new node*[bins];
sentinel = new node(hi, 0);
for(int i=0;i<bins;i++)
bin[i]=sentinel;
n=0;
}
void insert(int t)
{
int i=t/(hi/bins);
bin[i]=rinsert(bin[i],t);
}
int size(){return n;}
void report(int *v)
{
int j=0;
for(int i=0; i<bins; i++)
{
for(node* p=bin[i]; p!=sentinel; p=p->next)
v[j++]=p->val;
}
}
};
class IntSetBins2
{
private:
int n, bins, hi;
struct node
{
int val;
node *next;
//node(int a, node *p){val=a; next=p;}
};
node **bin, *sentinel, *freenode;
node *rinsert(node *p, int t)
{
if(p->val<t)
p->next=rinsert(p->next, t);
if(p->val>t)
{
//p=new node(t, p);
freenode->next=p;
freenode->val=t;
p=freenode++;
n++;
}
return p;
}
public:
IntSetBins2(int maxelements, int maxval)
{
hi=maxval;
bins=maxelements;
freenode = new node[bins];
bin = new node*[bins];
//sentinel = new node(maxval, 0);
sentinel = new node;
sentinel->val = hi;
for(int i=0;i<bins;i++)
bin[i]=sentinel;
n=0;
}
void insert(int t)
{
int i=t/(hi/bins);
bin[i]=rinsert(bin[i],t);
}
// non-recursive
void insert2(int t)
{
int i=t/(hi/bins);
node **p;
for(p=&bin[i];(*p)->val<t;p=&((*p)->next))
;
if((*p)->val==t)
return;
else
{
freenode->val=t;
freenode->next=*p;
*p=freenode++;
n++;
}
}
int size(){return n;}
void report(int *v)
{
int j=0;
for(int i=0; i<bins; i++)
{
for(node* p=bin[i]; p!=sentinel; p=p->next)
v[j++]=p->val;
}
}
};
int bigrand()
{
//return srand()+srand()*RAND_MAX;
return rand()+RAND_MAX*rand();
}
int gensets(int m, int maxval)
{
int i;
int *v = new int[m];
srand((unsigned)time(NULL));
//cout<<"Using STL:"<<endl;
//IntSetSTL S(m, maxval);
//cout<<"Using Array:"<<endl;
//IntSetArr S(m, maxval);
//cout<<"Using List(Normal):"<<endl;
//IntSetList S(m, maxval);
//cout<<"Using List(Improved):"<<endl;
//IntSetList2 S(m, maxval);
//cout<<"Using BST(Binary Search Tree):"<<endl;
//IntSetBST S(m, maxval);
//cout<<"Using BST(Improved):"<<endl;
//IntSetBST2 S(m, maxval);
//cout<<"Using BitVector:"<<endl;
//IntSetBitVec S(m, maxval);
//cout<<"Using Bins:"<<endl;
//IntSetBins S(m, maxval);
cout<<"Using Bins(Improved):"<<endl;
IntSetBins2 S(m, maxval);
while(S.size()<m)
//S.insert(bigrand() % maxval);
S.insert2(bigrand() % maxval);
//S.insert3(bigrand() % maxval);
S.report(v);
for(i=0;i<m;i++)
cout<<v[i]<<endl;
}
// floyd algorithm
void genfloyd(int m, int maxval)
{
int *v = new int[m];
srand((unsigned)time(NULL));
cout<<"Using Floyd:"<<endl;
IntSetSTL S(m, maxval);
for(int k=maxval-m; k<maxval; ++k)
{
int j = bigrand()%(k+1);
//if(S.find(j)==S.end()) IntSetSTL类没有提供find()函数
// 用size()来判断是否插入成功
int oldsize=S.size();
S.insert(j);
if(oldsize==S.size())
S.insert(k);
}
S.report(v);
for(int i=0;i<m;i++)
cout<<v[i]<<endl;
}
int main(int argc, char *argv[])
{
int m=atoi(argv[1]); // atoi();
int n=atoi(argv[2]);
gensets(m, n);
//genfloyd(m, n);
return 0;
}