散列,桶排序

一、散列原理

      理想的散列结构不过是一个包含关键字的具有固定大小的数组,散列表的长度是数据结构的一部分。散列中的关键字不需要支持比较大小,只要能够支持判等操作就行了。每个关键字按照一个散列函数被映射到0到TableSize-1范围内的桶单元。当不同的关键字被映射到相同的单元时,称作冲突(conflict)。散列的插入,查找,删除都是常数时间复杂度。
      常见的散列方法有除余法hash(key)=key%M,字符串为key时也常对字符求和作为散列值。

二、闭路定址


      散列表的值为一个指向链表节点的指针,当对应的桶单元被占用时,将对应的值插入到对应的链表中(比如插入到链表的

头结点),这种方式节省空间,删除操作简单,但是需要动态的申请节点内存,影响速度,并且空间分布不连续,系统缓存

失效,I/O次数增多。

//结构定义
struct ListNode;
typedef struct ListNode* Position;
struct HashTbl;
typedef struct HashTbl *HashTable;
HashTable InitializeTable(int TableSize);
void DestroyTable(HashTable H);
Position Find(ElementType Key,HashTable H);
void Insert(ElementType Key,HashTable H);
ElementType Retrieve(Position P);
struct ListNode
{
    ElementType Element;
    Position Next;
}
typedef Position List;
struct HashTbl
{
    int TableSize;
    List* TheLists;//二级指针,表示存放节点地址的数组;
}
<textarea readonly="readonly" name="code" class="c++">

//InitializeTable()
HashTable InitializeTable(int TableSize)
{
	HashTable H;
	if(TableSize<MinTableSize)
	{
		Error("Table size too small");
		return NULL;
	}
	HashTable=malloc(sizeof(struc HashTbl));
	if(HashTable==NULL)
	{
		FatalError("Out of space!");
	}
	H->TabelSize=nextPrim(Tablesize);
	H->TheList=malloc(sizeof(ListNode*)*TabelSize);
	if(H->TheList==NULL)
	{
		FatalError("Out of space!");
	}
	int i=0;
	for(i;i<TableSize;++i)
	{
		H->TheList[i]=malloc(sizeof(struct ListNode));
		if(H->TheList[i]==NULL)
		{
			FatalError("Out of space!");
		}
		else
		{
			H->TheList[i]->Next=NULL;
		}
	}
}


//Find()
Position Find(ElementType Key,HashTable H)
{
	Positon start=H->TheLists[Hash(Key),H->TableSize)];
	while(start!=NULL && start->Element!=Key)
	{
		start=start->Next;
	}
	return start;
}


//Insert()
void Insert(ElementType Key,HashTable H)
{
	Position p=Find(Key,H);
	if(p==NULL)
	{
		Position NewCell=malloc(sizeof(struct ListNode));
		if(NewCell==NULL)
		{
			FatalError("Out of space!");
		}
		else
		{
			Position L=H->TheList[Hash(Key,H->TableSize)];
			NewCell->Next=L->Next;
			NewCell.Element=Key;
			L->Next=NewCell;
		}
	}
}

三、开放定址


(1)线性试探

线性试探指的是,当发生散列冲突时,从当前位置出发,逐一的往前试探,直到找到一个空的位置。


其优点是空间在初始时就分配好,无需动态分配,提高了效率,并且由于数据临近,具有良好的局部性,充分利用了系统的IO性能,但是会导致更多的冲突。插入时若新词条尚不存在,则存入直接查找终止处的空桶,若存在,则往后逐一试探,直至找到空桶。删除时由于存在查找链,直接清除命中的桶会导致随后的查找链被切断,因此可做惰性删除。

(2)平方试探

以平方数为距离,确定下一个试探桶单元。


只有当表长为素数,切装填因子不超过0.5时,才一定能找出一个空桶。

(3)双平方试探

从冲突位置开始,依次向后试探。



四、桶排序

桶排序适用于元素分布范围固定,元素分布随机的情况,例如下面是输入最小值,最大值,和散列长度进行桶排序的代码,每个通采用链表组织,桶内部用STL里面list的sort()函数。

vector<int> BucketSort(vector<int>iVec,int min_elem,int max_elem,int bucketSize)
{
	vector<int>sortedVec;
	list<int>* bucketTbl = new list<int>[bucketSize];
	list<int>empty_lis;
	for (int i = 0; i < bucketSize;++i)
	{
		bucketTbl[i] = empty_lis;
	}
	int perBucket = (max_elem - min_elem )/ bucketSize + 1;
	for (int j = 0; j < iVec.size();++j)
	{
		bucketTbl[(iVec[j] - min_elem) / perBucket].push_back(iVec[j]);
	}
	for (int k = 0; k < bucketSize;++k)
	{
		bucketTbl[k].sort();
		copy(bucketTbl[k].begin(), bucketTbl[k].end(), back_inserter(sortedVec));
	}
	delete[]bucketTbl;
	return sortedVec;
}
int main()
{
	vector<int>input_vec = { 11, 23, 32, 77, 43, 34,83, 55, 99, 47,67, 20,5, 101, 120, 130 };
	copy(input_vec.begin(), input_vec.end(), ostream_iterator<int>(std::cout, " "));
	vector<int>sorted_vec = BucketSort(input_vec, 11,130,11);
	cout << endl;
	copy(sorted_vec.begin(), sorted_vec.end(), ostream_iterator<int>(std::cout, " "));
	system("pause");
	return 0;
}
桶排序适用于当输入范围有限,并且输入值随机分布的情况,是最耗费空间的排序方法,但是能获得线性的复杂度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值