编程珠玑第13章 搜索

一、用数组来表示集合

其中在插入函数中先调用二分查找,找出要插入的元素t,如果不存在就返回其前一个位置,先将其+1然后进行插入。

#include<iostream>
using namespace std;

class IntSet
{
	int Size;
	int *A;
public:
	IntSet(int n,int _Size=0)
	{
		A=new int[n];
		Size=_Size;
	}
	void insert(int t);
	int find(int t);
	int size(){return Size;};
	void report(int *r);
};

void IntSet::insert(int t)
{
	int i=find(t);
	if(i>=0 && A[i]==t)
		return ;
	i++;//插入在第一比当前值小的位置的后面
	for(int j=Size;j>i;j--)
		A[j]=A[j-1];
	A[i]=t;
	Size++;
}

int IntSet::find(int t)
{
	int lo=-1,hi=Size;
	while(lo+1!=hi)
	{
		int mid=lo+((hi-lo)>>1);
		if(A[mid]<=t)
			lo=mid;
		else
			hi=mid;
	}
	return lo;
}

void IntSet::report(int *r)
{
	for(int i=0;i<Size;i++)
		r[i]=A[i];
}

int main()
{
	int N=100,M=10;
	IntSet s(M); 
	while(s.size()<M)
		s.insert(rand()%N);
	int *report=new int[M];
	s.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	system("pause");
	return 0;
}
二、用链表来表示集合:

在程序中,用了3个insert函数,第一个是普通的插入,第二个是用指向指针的指针来进行插入,练习题中第4题就是这样来做,可以很明显看出代码缩短了,判断语句大大减少。第三个是用一次new来代替多次new,如练习题中第5题就是这样做的。下面一片博文我会分析在动态分配内存时,分配相同内存,但是用不同次数分配的消耗时间的差异。

#include<iostream>
using namespace std;

struct Node
{
	int val;
	Node *next;
	Node(int _val=0,Node *_next=NULL):val(_val),next(_next){}
};

class IntSet
{
	int Size;
	Node *head;
	Node *freeNode;
public:
	IntSet(int n=0)
	{
		head=new Node(INT_MAX);
		Size=0;
		freeNode=new Node[n];
	}
	void insert(int t);
	void insert1(int t);
	void insert2(int t);
	int size(){return Size;};
	void report(int *r);
};

void IntSet::insert(int t)
{
	if(head->val==t)
		return ;
	Node *curr=head;
	if(head->val>t)
	{
		head=new Node(t,curr);
		Size++;
		return;
	}
	for(;curr->next->val<t;curr=curr->next);
	if(curr->next->val==t)
		return;
	Node *temp=curr->next;
	curr->next=new Node(t,temp);
	Size++;
}

void IntSet::insert1(int t)
{
	Node **p=&head;
	for(;(*p)->val<t;p=&((*p)->next));
	if((*p)->val==t)
		return ;
	*p=new Node(t,*p);
	Size++;
}

void IntSet::insert2(int t)
{
	Node **p=&head;
	for(;(*p)->val<t;p=&((*p)->next));
	if((*p)->val==t)
		return ;
	Node *temp=*p;
	*p=freeNode++;
	(*p)->val=t;
	(*p)->next=temp;
	Size++;
}

void IntSet::report(int *r)
{
	Node *curr=head;
	for(int i=0;i<Size;i++)
	{
		r[i]=curr->val;
		curr=curr->next;
	}
}

int main()
{
	int N=100,M=10;
	IntSet s(M); 
	while(s.size()<M)
		s.insert(rand()%N);
	int *report=new int[M];
	s.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	IntSet s1; 
	while(s1.size()<M)
		s1.insert1(rand()%N);
//	int *report=new int[M];
	s1.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	IntSet s2(M); 
	while(s2.size()<M)
		s2.insert2(rand()%N);
//	int *report=new int[M];
	s2.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	system("pause");
	return 0;
}
三、二分搜索树

用了三个版本的插入,同上所述,一个是每次插入都用一次new,另一个是用一次new把空间全开出来,最后一个插入了一个哨兵,并且将每一个节点的为空的left或right指针,指向哨兵,用指向指针的指针来遍历,减少了代码长度,如下可以清晰看出代码长度的变化。

#include<iostream>
using namespace std;

struct Node
{
	int val;
	Node *left,*right;
	Node(int _val=0,Node *_left=NULL,Node *_right=NULL)
	{
		val=_val;
		left=_left;
		right=_right;
	}
};

class IntSet
{
	int Size;
	Node *root;
	Node *freeNode;
	Node *sharedNode;
public:
	IntSet(int n=0)
	{
		Size=0;
		freeNode=new Node[n];
		sharedNode=new Node;
		root=sharedNode;
	}
	void insert(int t);
	void insert1(int t);
	void insert2(int t);
	int size(){return Size;};
	void report(int *r);
	void traverse(Node *p,int *r,int &i);
	void report_share(int *r);
	void traverse_share(Node *p,int *r,int &i);

};

void IntSet::insert2(int t)
{
	sharedNode->val=t;
	Node **curr=&root;
	while((*curr)->val!=t)
	{
		if((*curr)->val>t)
			curr=&((*curr)->left);
		else
			curr=&((*curr)->right);
	}
	if((*curr)!=sharedNode)
		return ;
	*curr=freeNode++;
	(*curr)->val=t;
	(*curr)->left=sharedNode;
	(*curr)->right=sharedNode;
	Size++;
}

void IntSet::insert1(int t)
{
	if(root==NULL)
	{
		root=new Node(t);
		Size++;
		return;
	}
	Node *curr=root;
	while(curr!=NULL)
	{
		if(curr->val==t)
			return ;
		if(curr->val>t)
		{
			if(curr->left==NULL)
			{
				Size++;
				curr->left=freeNode++;
				curr->left->val=t;
				return;
			}
			curr=curr->left;
		}
		else
		{
			if(curr->right==NULL)
			{
				Size++;
				curr->right=freeNode++;
				curr->right->val=t;
				return;
			}
			curr=curr->right;
		}
	}
}

void IntSet::insert(int t)
{
	if(root==NULL)
	{
		root=new Node(t);
		Size++;
		return;
	}
	Node *curr=root;
	while(curr!=NULL)
	{
		if(curr->val==t)
			return ;
		if(curr->val>t)
		{
			if(curr->left==NULL)
			{
				Size++;
				curr->left=new Node(t);
				return;
			}
			curr=curr->left;
		}
		else
		{
			if(curr->right==NULL)
			{
				Size++;
				curr->right=new Node(t);
				return;
			}
			curr=curr->right;
		}
	}
}

void IntSet::report(int *r)
{
	int i=0;
	traverse(root,r,i);
}

void IntSet::traverse(Node *p,int *r,int &i)//此处的i要用引用
{
	if(p->left!=NULL)
		traverse(p->left,r,i);
	r[i++]=p->val;
	if(p->right!=NULL)
		traverse(p->right,r,i);
}

void IntSet::report_share(int *r)
{
	int i=0;
	sharedNode->val=INT_MAX;
	traverse_share(root,r,i);
}

void IntSet::traverse_share(Node *p,int *r,int &i)//此处的i要用引用
{
	if(p->left->val!=INT_MAX)
		traverse_share(p->left,r,i);
	r[i++]=p->val;
	if(p->right->val!=INT_MAX)
		traverse_share(p->right,r,i);
}

int main()
{
	int N=100,M=10;
	IntSet s(M); 
	while(s.size()<M)
		s.insert2(rand()%N);
	int *report=new int[M];
	s.report_share(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	IntSet s1; 
	while(s1.size()<M)
		s1.insert(rand()%N);
//	int *report=new int[M];
	s1.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	system("pause");
	return 0;
}


四、位向量操作

在此处一定要注意初始化,当给A开辟空间后,进行初始化为0。

#include<iostream>
using namespace std;

class IntSet
{
	int Size;
	int *A;
	int N;
public:
	enum{ BITSPERWORD=32,SHIFT=5,MASK=0x1F};
	IntSet(int n=0)
	{
		Size=0;
		N=n;
		A=new int[1+(n>>SHIFT)];
		for(int i=0;i<1+(n>>SHIFT);i++)
			A[i]=0;//注意这里必须要先初始化
	}
	void setValue(int t)
	{
		A[t>>SHIFT]|=1<<(t&MASK);
		Size++;
	}
	void clrValue(int t)
	{
		A[t>>SHIFT]&=~(1<<(t&MASK));
		Size--;
	}
	bool test(int t)
	{
		return A[t>>SHIFT]&(1<<(t&MASK));
	}
	void report(int *r)
	{
		int k=0;
		for(int i=0;i<N;i++)
		{
			if(test(i))
				r[k++]=i;
		}
	}
	int size(){return Size;}
};

int main()
{
	int N=100,M=10;
	IntSet s(N);//使用了1+n/32的内存 
	while(s.size()<M)
		s.setValue(rand()%N);
	int *report=new int[M];
	s.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	system("pause");
	return 0;
}
五、箱操作

其实这就是一种简单的散列,和求模的散列类似,只是这里往箱子中插入数据时要保证数据的顺序性,这样在输出时候才能保证是顺序的随机数。

#include<iostream>
using namespace std;

struct Node
{
	int val;
	Node *next;
	Node(int _val=0,Node *_next=NULL):val(_val),next(_next){}
};

class IntSet
{
	int Size;
	Node **A;
	int nBox;
	int maxVal;
	int minVal;
public:
	IntSet(int _maxVal,int _minVal=0)
	{
		maxVal=_maxVal;
		minVal=_minVal;
		Size=0;
		nBox=1+(maxVal-minVal)/10;
		A=new Node*[nBox];
		for(int i=0;i<nBox;i++)
			A[i]=new Node(INT_MAX);
	}
	void insert(int t)
	{
		int boxNum=(t-minVal)/nBox;
		Node **curr=&A[boxNum];
		while((*curr)->val<t)
			curr=&((*curr)->next);
		*curr=new Node(t,*curr);
		Size++;
	}

	void report(int *r)
	{
		int k=0;
		for(int i=0;i<nBox;i++)
		{
			Node *curr=A[i];
			while(curr->val!=INT_MAX)
			{
				r[k++]=curr->val;
				curr=curr->next;
			}
		}
	}
	int size(){return Size;}
};

int main()
{
	int N=100,M=10;
	IntSet s(100);
	while(s.size()<M)
		s.insert(rand()%N);
	int *report=new int[M];
	s.report(report);
	for(int i=0;i<M;i++)
		cout<<report[i]<<endl;

	system("pause");
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值