当前位置:首页 » 编程语言 » 拉链法哈希函数c语言
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

拉链法哈希函数c语言

发布时间: 2023-05-23 01:13:07

① hash函数的构造方法

常用的构造哈希(hash)函数的方法有:直接寻址法、数字分析法、平方取中法、折叠法、除留余数发、随机数法。

1、直接寻址法

取关键字或关键字的某个线性函数值为哈希地址。即:H(key)=key或H(key)=akey+b。其中a和b为常数(这种哈希函数叫做自身函数)。

2、数字分析法

假设关键字是以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。

3、平方取中法

取关键字平方后的中间几位为哈希地址。这是一种较常用的构造哈希函数的方源旅法。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关。

4、折叠法

将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法(folding)。关键字位数很多,而且关键字中每一-位上数字分布大致均匀时,可以采用折叠法得到哈希地粗裂芹址。

5、除留余数发

取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。即H(key) = key MOD p, pm。这是一种最简单,也最常用的构造哈希函数的方法。它不仅可以对关键字直接取模(MOD),也可在折叠,平方取中等运算之后取模。

6、随机数法

选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)=random(key),其中random为随机函数。通常﹐当关键字长度不等时采用此法构造哈希函数较恰当。

冲突的处理:

哈希表中,不同的关键字值对应到同一个存储位置的现象。即关键字K1≠K2,但H(K1)=H(K2)。均匀的哈希函数可以减少冲突,但不能避免冲突。发生冲突后,必须解决;也即必须寻找下一个可用地址,解决冲突的方法:

1、链接法(拉链法):将具有同一散列地址的记录存储在一条线性链表中。例,除留余数法中,设关键字为(18,14,01,68,27,55,79),除数为13,散列地址为(5,1,1,3,1,3,1)。

2、开放寻址法岩毕:如果h(k)已经被占用,按如下序列探查:(h(k)+p⑴)%TSize,(h(k)+p⑵)%TSize,…,(h(k)+p(i))%TSize,…其中,h(k)为哈希函数,TSize为哈希表长,p(i)为探查函数。

在h(k)+p(i-1))%TSize的基础上,若发现冲突,则使用增量p(i)进行新的探测,直至无冲突出现为止。

根据探查函数p(i)的不同,开放寻址法又分为线性探查法(p(i) = i : 1,2,3,…),二次探查法(p(i)=(-1)^(i-1)*((i+1)/2)^2,探查序列依次为:1, -1,4, -4, 9…)。

随机探查法(p(i):随机数),双散列函数法(双散列函数h(key),hp (key)若h(key)出现冲突,则再使用hp (key)求取散列地址。探查序列为:h(k),h(k)+ hp(k),…,h(k)+ i*hp(k))。

3、桶寻址法:桶为一片足够大的存储空间。桶寻址为表中的每个地址关联一个桶。如果桶已经满了,可以使用开放寻址法来处理。例如,插入A5,A2,A3,B5,A9,B2,B9,C2,采用线性探查法解决冲突。

c语言 数据结构中解决冲突的方法是什么

可以参考如下方法:

1 基本原理
使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。
但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。
总的来说,"直接寻址"与"解决冲突"是哈希表的两大特点。

2 函数构造
构造函数的常用方法(下面为了叙述简洁,设 h(k) 表示关键字为 k 的元素所对应的函数值):
a) 除余法:
选择一个适当的正整数 p ,令 h(k ) = k mod p
这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。
b) 数字选择法:
如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。

3 冲突处理
线性重新散列技术易于实现且可以较好的达到目的。令数组元素个数为 S ,则当 h(k) 已经存储了元素的时候,依次探查 (h(k)+i) mod S , i=1,2,3…… ,直到找到空的存储单元为止(或者从头到尾扫描一圈仍未发现空单元,这就是哈希表已经满了,发生了错误。当然这是可以通过扩大数组范围避免的)。

4 支持运算
哈希表支持的运算主要有:初始化(makenull)、哈希函数值的运算(h(x))、插入元素(insert)、查找元素(member)。
设插入的元素的关键字为 x ,A 为存储的数组。
初始化比较容易,例如
const empty=maxlongint; // 用非常大的整数代表这个位置没有存储元素
p=9997; // 表的大小
procere makenull;
var i:integer;
begin
for i:=0 to p-1 do
A[i]:=empty;
End;
哈希函数值的运算根据函数的不同而变化,例如除余法的一个例子:
function h(x:longint):Integer;
begin
h:= x mod p;
end;
我们注意到,插入和查找首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位置,因此加入一个定位的函数 locate
function locate(x:longint):integer;
var orig,i:integer;
begin
orig:=h(x);
i:=0;
while (i<S)and(A[(orig+i)mod S]<>x)and(A[(orig+i)mod S]<>empty) do
inc(i);
//当这个循环停下来时,要么找到一个空的存储单元,要么找到这个元
//素存储的单元,要么表已经满了
locate:=(orig+i) mod S;
end;
插入元素
procere insert(x:longint);
var posi:integer;
begin
posi:=locate(x); //定位函数的返回值
if A[posi]=empty then A[posi]:=x
else error; //error 即为发生了错误,当然这是可以避免的
end;
查找元素是否已经在表中
procere member(x:longint):boolean;
var posi:integer;
begin
posi:=locate(x);
if A[posi]=x then member:=true
else member:=false;
end;
这些就是建立在哈希表上的常用基本运算。

4.1 应用的简单原则
什么时候适合应用哈希表呢?如果发现解决这个问题时经常要询问:"某个元素是否在已知集合中?",也就是需要高效的数据存储和查找,则使用哈希表是最好不过的了!那么,在应用哈希表的过程中,值得注意的是什么呢?
哈希函数的设计很重要。一个不好的哈希函数,就是指造成很多冲突的情况,从前面的例子已经可以看出来,解决冲突会浪费掉大量时间,因此我们的目标就是尽力避免冲突。前面提到,在使用"除余法"的时候,h(k)=k mod p ,p 最好是一个大素数。这就是为了尽力避免冲突。为什么呢?假设 p=1000 ,则哈希函数分类的标准实际上就变成了按照末三位数分类,这样最多1000类,冲突会很多。一般地说,如果 p 的约数越多,那么冲突的几率就越大。
简单的证明:假设 p 是一个有较多约数的数,同时在数据中存在 q 满足 gcd(p,q)=d >1 ,即有 p=a*d , q=b*d, 则有 q mod p= q - p* [q div p] =q - p*[b div a] . ① 其中 [b div a ] 的取值范围是不会超过 [0,b] 的正整数。也就是说, [b div a] 的值只有 b+1 种可能,而 p 是一个预先确定的数。因此 ① 式的值就只有 b+1 种可能了。这样,虽然mod 运算之后的余数仍然在 [0,p-1] 内,但是它的取值仅限于 ① 可能取到的那些值。也就是说余数的分布变得不均匀了。容易看出, p 的约数越多,发生这种余数分布不均匀的情况就越频繁,冲突的几率越高。而素数的约数是最少的,因此我们选用大素数。记住"素数是我们的得力助手"。
另一方面,一味的追求低冲突率也不好。理论上,是可以设计出一个几乎完美,几乎没有冲突的函数的。然而,这样做显然不值得,因为这样的函数设计很浪费时间而且编码一定很复杂,与其花费这么大的精力去设计函数,还不如用一个虽然冲突多一些但是编码简单的函数。因此,函数还需要易于编码,即易于实现。
综上所述,设计一个好的哈希函数是很关键的。而"好"的标准,就是较低的冲突率和易于实现。

③ 哈希查找算法

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。

通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。

Hash是一种典型以空间换时间的算法,比如原来一个长度为100的数组,对其查找,只需要遍历且匹配相应记录即可,从空间复杂度上来看,假如数组存储的是byte类型数据,那么该数组占用100byte空间。现在我们采用Hash算法,我们前面说的Hash必须有一个规则,约束键与存储位置的关系,那么就需要一个固定长度的hash表,此时,仍然是100byte的数组,假设我们需要的100byte用来记录键与位置的关系,那么总的空间为200byte,而且用于记录规则的表大小会根据规则,大小可能是不定的。

通过哈希函数,我们可以将键转换为数组的索引(0-M-1),但是对于两个或者多个键具有相同索引值的情况,我们需要有一种方法来处理这种冲突。

一种比较直接的办法就是,将大小为M 的数组的每一个元素指向一个链表,链表中的每一个节点都存储散列值为该索引的键值对,这就是拉链法。下图很清楚的描述了什么是拉链法。

“John Smith”和“Sandra Dee” 通过哈希函数都指向了152 这个索引,该索引又指向了一个链表, 在链表中依次存储了这两个字符串。

单独链表法:将散列到同一个存储位置的所有元素保存在一个链表中(聚集),该方法的基本思想就是选择足够大的M,使得所有的链表都尽可能的短小,以保证查找的效率。当链表过长、大量的键都会映射到相同的索引上,哈希表的顺序查找会转变为链表的查找,查找时间将会变大。对于开放寻址会造成性能的灾难性损失。

实现基于拉链表的散列表,目标是选择适当的数组大小M,使得既不会因为空链表而浪费内存空间,也不会因为链表太而在查找上浪费太多时间。拉链表的优点在于,这种数组大小M的选择不是关键性的,如果存入的键多于预期,那么查找的时间只会比选择更大的数组稍长。另外,我们也可以使用更高效的结构来代替链表存储。如果存入的键少于预期,索然有些浪费空间,但是查找速度就会很快。所以当内存不紧张时,我们可以选择足够大的M,可以使得查找时间变为常数,如果内存紧张时,选择尽量大的M仍能够将性能提高M倍。

线性探测法是开放寻址法解决哈希冲突的一种方法,基本原理为,使用大小为M的数组来保存N个键值对,其中M>N,我们需要使用数组中的空位解决碰撞冲突。如下图所示:

对照前面的拉链法,在该图中,“Ted Baker” 是有唯一的哈希值153的,但是由于153被“Sandra Dee”占用了。而原先“Snadra Dee”和“John Smith”的哈希值都是152的,但是在对“Sandra Dee”进行哈希的时候发现152已经被占用了,所以往下找发现153没有被占用,所以索引加1 把“Sandra Dee”存放在没有被占用的153上,然后想把“Ted Baker”哈希到153上,发现已经被占用了,所以往下找,发现154没有被占用,所以值存到了154上。

单纯论查找复杂度:对于无冲突的Hash表而言,查找复杂度为O(1)。

原文: 哈希查找 - 卖贾笔的小男孩 - 博客园 (cnblogs.com)

④ C语言中的hash函数

Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
HASH主要用于信息安全领域中加密算法,它把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值. 也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系。Hash算法在信息安全方面的应用主要体现在以下的3个方面:文件校验、数字签名、鉴权协议
程程序实现
// 说明:Hash函数(即散列函数)在程序设计中的应用目标 ------ 把一个对象通过某种转换机制对应到一个
//size_t类型(即unsigned long)的整型值。
// 而应用Hash函数的领域主要是 hash表(应用非常广)、密码等领域。
// 实现说明:
// ⑴、这里使用了函数对象以及泛型技术,使得对所有类型的对象(关键字)都适用。
// ⑵、常用类型有对应的偏特化,比如string、char*、各种整形等。
// ⑶、版本可扩展,如果你对某种类型有特殊的需要,可以在后面实现专门化。
// ⑷、以下实现一般放在头文件中,任何包含它的都可使用hash函数对象。
//------------------------------------实现------------------------------------------------
#include <string>
using std::string;
inlinesize_thash_str(const char* s)
{
unsigned long res = 0;
for (; *s; ++s)
res = 5 * res + *s;
returnsize_t(res);
}
template <class Key>
struct hash
{
size_toperator () (const Key& k) const;
};
// 一般的对象,比如:vector< queue<string> >;的对象,需要强制转化
template < class Key >
size_thash<Key>::operator () (const Key& k) const
{
size_tres = 0;
size_tlen = sizeof(Key);
const char* p = reinterpret_cast<const char*>(&k);
while (len--)
{
res = (res<<1)^*p++;
}
return res;
}
// 偏特化
template<>
size_thash< string >::operator () (const string& str) const
{
return hash_str(str.c_str());
}
typedef char* PChar;
template<>
size_thash<PChar>::operator () (const PChar& s) const
{
return hash_str(s);
}
typedef const char* PCChar;
template<>
size_thash<PCChar>::operator () (const PCChar& s) const
{
return hash_str(s);
}
template<> size_t hash<char>::operator () (const char& x) const { return x; }
template<> size_t hash<unsigned char>::operator () (const unsigned char& x) const { return x; }
template<> size_t hash<signed char>::operator () (const signed char& x) const { return x; }
template<> size_t hash<short>::operator () (const short& x) const { return x; }
template<> size_t hash<unsigned short>::operator () (const unsigned short& x) const { return x; }
template<> size_t hash<int>::operator () (const int& x) const { return x; }
template<> size_t hash<unsigned int>::operator () (const unsigned int& x) const { return x; }
template<> size_t hash<long>::operator () (const long& x) const { return x; }
template<> size_t hash<unsigned long>::operator () (const unsigned long& x) const { return x; }
// 使用说明:
//
// ⑴、使用时首先由于是泛型,所以要加上关键字类型。
//
// ⑵、其次要有一个函数对象,可以临时、局部、全局的,只要在作用域就可以。
//
// ⑶、应用函数对象作用于对应类型的对象。
//----------------------- hash函数使用举例 -------------------------
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> vstr⑵;
vstr[0] = "sjw";
vstr[1] = "suninf";
hash<string> strhash; // 局部函数对象
cout << " Hash value: " << strhash(vstr[0]) << endl;
cout << " Hash value: " << strhash(vstr[1]) << endl;
cout << " Hash value: " << hash< vector<string> >() (vstr) << endl;
cout << " Hash value: " << hash<int>() (100) << endl; // hash<int>() 临时函数对象
return 0;
}

⑤ C++数据结构设计程序

苦逼的吉大孩子,你几班的啊,

#include<iostream>
#include<stdlib.h>
#include<time.h>
#include<fstream>
//#include<math>
usingnamespacestd;
voidsearch(int,int,int*,int*);
template<classT>

intFind_s(Tdata[],intn,Tkey,int&icmp)
//顺序查找(从n维数组中查找key,并且给出比较的次数icmp
{
icmp=0;
for(inti=0;i<n;i++)
{
icmp++;
if(data[i]==key)
returni;
}
return-1;
}
///以下是二叉查找树查找法
template<classT>
classBintreeNode
{
public:
Tdata;
BintreeNode*left;
BintreeNode*right;

BintreeNode():left(0),right(NULL){}
BintreeNode(Titem):data(item),left(NULL),right(NULL){}
~BintreeNode(){
if(left!=0)
deleteleft;
if(right!=0)
deleteright;
}
};
template<classT>
classBintree
{
public:
intnum;
BintreeNode<T>*root;
BintreeNode<T>*Find_bt(Tkey,int&icmp,intitype=0)//一个二叉树查找算法
{
icmp=0;
if(root==0)
{
icmp++;
if(itype==0)//itype=1时有插入功亮纯能(不同的值时)
returnNULL;
else
{
num++;
returnroot=newBintreeNode<T>(key);
}
}
else
{
BintreeNode<T>*ptr=root,*p;
敬搭咐while(ptr!=NULL)
{
icmp++;
p=ptr;
if(ptr->data==key)
returnptr;
else
{
if(ptr->data>key)
ptr=ptr->left;
else
ptr=ptr->right;
}
}
if(itype)
{
num++;
if(p->data>key)
枝旦returnp->left=newBintreeNode<T>(key);
else
returnp->right=new(BintreeNode<T>)(key);
}
returnNULL;
}//else
}//Find_bt
Bintree():root(0),num(0){}
~Bintree(){deleteroot;}
};
/*intcompare(constvoid*a,constvoid*b)
{
return*(int*)a-*(int*)b;
}*/
template<classT>
classLink
{
public:
Tdata;
Link<T>*next;
Link<T>():next(0){}
Link<T>(Titem):data(item),next(0){}
~Link<T>(){if(next)deletenext;}
};
template<classT>
classLinkList
{
public:
Link<T>*first;
LinkList<T>():first(0){}
~LinkList<T>(){if(first)deletefirst;}
Link<T>*Find_hash(Tkey,int&icmp,intntype=0)//查找与插入
{
icmp=0;
if(first==0)
{
icmp++;
if(ntype)
returnfirst=newLink<T>(key);
else
returnNULL;
}
else
{
Link<T>*ptr=first,*p;
while(ptr!=NULL)
{
icmp++;
p=ptr;
if(ptr->data==key)
returnptr;
ptr=ptr->next;
}
if(ntype)
returnp->next=newLink<T>(key);
returnNULL;
}
}
};
template<classT>
classHash
{
public:
LinkList<T>*hashlist;
intsize;
Hash<T>(intn=113):size(n)
{
hashlist=newLinkList<T>[n];
}
~Hash<T>(){delete[]hashlist;}
voidinit_hash(Tdata[],intn)//初始化哈希查找表(拉链法)
{
intt;
for(inti=0;i<n;i++)
{
intpos=data[i]%size;
hashlist[pos].Find_hash(data[i],t,1);
}
}
Link<T>*Find_hash(Tkey,int&icmp)//查找关键词除法杂凑函数
{
intpos=key%size;
returnhashlist[pos].Find_hash(key,icmp);
}
};
intcompare1(constvoid*a,constvoid*b)
{
return*(int*)a-*(int*)b;
}
intcompare2(constvoid*a,constvoid*b)
{
return*(int*)b-*(int*)a;
}
//主函数
intmain()
{
intp;
intm;
intn;
while(true)
{
cout<<"请选择数据类型:1.正序排列2.逆序排列3.随机排列"<<endl;
cin>>p;
switch(p){
case1:
{
FILE*fp;
char*filename="data.in";
if((fp=fopen(filename,"w+"))==NULL)
{
printf("cannotopenthisflie ");
exit(0);
}


srand(time(0));
cout<<"请输入数据规模n"<<endl;
cin>>n;
fprintf(fp," %d ",n);
cout<<"请输入查找比较的操作次数m"<<endl;
cin>>m;
fprintf(fp," %d ",m);

int*a=newint[n];
for(inti=0;i<n;i++)
{
a[i]=rand()%1000000001;
fprintf(fp," %d ",a[i]);
}
qsort(a,n,sizeof(int),compare1);//对数组进行升序排序
qsort(a,n,sizeof(int),compare2);//对数组进行降序排序
//随机生成m个随机数,并存入data.in
int*b=newint[m];
for(intk=0;k<m;k++)
{
b[k]=rand()%1000000001;
fprintf(fp," %d ",b[k]);
}
fclose(fp);//关闭文件
search(m,n,a,b);
}
break;
case2:
{
FILE*fp;
char*filename="data.in";
if((fp=fopen(filename,"w+"))==NULL)
{
printf("cannotopenthisflie ");
exit(0);
}


srand(time(0));
cout<<"请输入数据规模n"<<endl;
cin>>n;
fprintf(fp," %d ",n);

cout<<"请输入查找比较的操作次数m"<<endl;
cin>>m;
fprintf(fp," %d ",m);

int*a=newint[n];
for(inti=0;i<n;i++)
{
a[i]=rand()%1000000001;
fprintf(fp," %d ",a[i]);
}
qsort(a,n,sizeof(int),compare2);//对数组进行降序排序
//随机生成m个随机数,并存入data.in
int*b=newint[m];
for(intk=0;k<m;k++)
{
b[k]=rand()%1000000001;
fprintf(fp," %d ",b[k]);
}
fclose(fp);//关闭文件
search(m,n,a,b);
}
break;
case3:
{
FILE*fp;
char*filename="data.in";
if((fp=fopen(filename,"w+"))==NULL)
{
printf("cannotopenthisflie ");
exit(0);
}


srand(time(0));
//输入n,并存入data.in
//intn;
cout<<"请输入数据规模n"<<endl;
cin>>n;
fprintf(fp," %d ",n);
//输入m,并存入data.in
cout<<"请输入查找比较的操作次数m"<<endl;
//intm;
cin>>m;
fprintf(fp," %d ",m);
//随机生成n个随机数,并存入data.in
int*a=newint[n];
for(inti=0;i<n;i++)
{
a[i]=rand()%1000000001;
fprintf(fp," %d ",a[i]);
}
//随机生成m个随机数,并存入data.in
int*b=newint[m];
for(intk=0;k<m;k++)
{
b[k]=rand()%1000000001;
fprintf(fp," %d ",b[k]);
}
fclose(fp);//关闭文件
search(m,n,a,b);
}
}

}
return0;
}
//search函数
voidsearch(intm,intn,int*a,int*b){
FILE*fp;
char*filename="data.out";
if((fp=fopen(filename,"w+"))==NULL)
{
printf("cannotopenthisflie ");
exit(0);
}


clock_tstart1=clock();
for(inth=0;h<m;h++)
{
intkey=b[h];
inticmp;
intj=Find_s(a,n,key,icmp);//使用顺序查找法查找key值的位置
if(j==-1)
{
cout<<"数据总数:"<<n<<" 查找关健字key:"<<key<<
" 查找比较次数:icmp 顺序查找法: 是否找到:"
<<"no"<<"icmp:"<<icmp<<endl;
fprintf(fp," %s ","no");
}
else
{

cout<<"数据总数:"<<n<<" 查找关健字key:"<<key<<
" 查找比较次数:icmp 顺序查找法: 是否找到:"
<<"yes"<<"icmp:"<<icmp<<endl;
fprintf(fp," %s ","yes");
}
}
clock_tend1=clock();
clock_tstart2=clock();
for(intf=0;f<m;f++)
{
intkey=b[f];
inticmp;
Bintree<int>btree;
for(inti=0;i<n;i++)
btree.Find_bt(a[i],icmp,1);//未排序之前插入到二叉查找树中
qsort(a,n,sizeof(int),compare1);//对数组进行升序排序
cout<<"二叉查找树:"<<endl;
intp=(btree.Find_bt(key,icmp)==NULL?-1:btree.Find_bt
(key,icmp)->data);
if(p==-1)
{

cout<<"数据总数:"<<n<<" 查找关健字key:"<<key<<
" 查找比较次数:icmp 二叉树查找法: 是否找到:"
<<"no"<<"icmp:"<<icmp<<endl;
fprintf(fp," %s ","no");

}
else
{
cout<<"数据总数:"<<n<<"	查找关健字key:"<<key<<
" 查找比较次数:icmp 二叉树查找法: 是否找到:"
<<"no"<<"icmp:"<<icmp<<endl;
fprintf(fp," %s ","no");
}
}
clock_tend2=clock();
clock_tstart3=clock();
for(intg=0;g<m;g++)
{
intkey=b[g];
inticmp;
Hash<int>hash(1000);
hash.init_hash(a,n);
cout<<"哈希表查找:"<<endl;
intq=((hash.Find_hash(key,icmp)==NULL)?-1:(hash.Find_hash
(key,icmp))->data);
if(q==-1){
cout<<"数据总数:"<<n<<" 查找关健字key:"<<key<<
" 查找比较次数:icmp 哈希查找法: 是否找到:"
<<"no"<<"icmp:"<<icmp<<endl;
fprintf(fp," %s ","no");
}
else{
cout<<"数据总数:"<<n<<"	查找关健字key:"<<key<<
" 查找比较次数:icmp 二叉树查找法: 是否找到:"
<<"yes"<<"icmp:"<<icmp<<endl;
fprintf(fp," %s ","yes");
}
}
clock_tend3=clock();
cout<<"顺序查找时间:"<<(double)(end1-start1)/CLOCKS_PER_SEC<<endl<<endl;//输出时间
cout<<"二叉树查找时间:"<<(double)(end2-start2)/CLOCKS_PER_SEC<<endl<<endl;//输出时间
cout<<"哈希查找时间:"<<(double)(end3-start3)/CLOCKS_PER_SEC<<endl<<endl;//输出时间
fclose(fp);//关闭文件
}

不过 运行结果 不能排序 只能是随机的,你看看能改好吗?改好了 说给我 我11级12班的

⑥ 线性表---解决冲突(c语言)

开放地址法
1.冲突处理方法一---开放地址法
当发生地址冲突后,求解下一个地址用:
ND
=(
D+di)%mi=1,2,…,k(k<=
m-1)
其中:
m为哈希表长度,di为增量序列。增量序列的不同取法,又构成不同的开放地址法。
(1)线性探测再散列
D
=
H(key);
ND
=
(D+di)%m;di取1,2,3,……,m-1
线性探测再散列处理冲突的基本思想:若数据元素在存储地址D发生冲突,则放到存储地址(D+1)%m;若又发生冲突则放到存储地址(D+2)%m;若再发生冲突则放到存储地址(D+3)%m;…禅销…;直到碰到第一个为空的存储地址(D+i)%m,则将数据元素存放在该存储空间。
(2)二次探测再散列
D
=
H(key);
ND
=
(D+di)%m;di取1*1,-1*1,2*2,-2*2,……,K*K,-K*K
(K≤m/2)
(3)双散列法
首先使用第一个散列函数H1(key)及逆行那个散列,一旦发生冲突,则使用第二个函数H2(key)计算改项到达下一个存储地址的增量,其取值p和key有关,范围在1和m之间,并且与m互质的正整数。
D
=
H1(key);
p
=
H2(key);
ND
=
(D+p)%m;
值得提醒的是,对利用开放地址法查了冲突所产生的哈希表中删除一个元素,不能简单地直接删除,因为这样将截断其它具棚亮有相同哈希地址的元素的查找地址,所以应设定一个特殊的标志链袭宽以表明该元素已被删除。
不知道对你是否有用

⑦ 有关数据结构哈希表的问题

Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

hashing定义了一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来在数据库中建立索引并进行搜索,同时还用在各种解密算法中。

设所有可能出现的关键字集合记为u(简称全集)。实际发生(即实际存储)的关键字集合记为k(|k|比|u|小得多)。|k|是集合k中元素的个数。
散列方法是使用函数hash将u映射到表t[0..m-1]的下标上(m=o(|u|))。这样以u中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。从而达到在o(1)时间内就可完成查找。
其中:
① hash:u→{0,1,2,…,m-1} ,通常称h为散列函数(hash function)。散列函数h的作用是压缩待处理的下标范围,使待处理的|u|个值减少到m个值,从而降低空间开销。
② t为散列表(hash table)。
③ hash(ki)(ki∈u)是关键字为ki结点存储地址(亦称散列值或散列地址)。
④ 将结点按其关键字的散列地址存储到散列表中的过程称为散列(hashing).
比如:有一组数据包括用户名字、电话、住址等,为了快速的检索,我们可以利用名字作为关键码,hash规则就是把名字中每一个字的拼音的第一个字母拿出来,把该字母在26个字母中的顺序值取出来加在一块作为改记录的地址。比如张三,就是z+s=26+19=45。就是把张三存在地址为45处。
但是这样存在一个问题,比如假如有个用户名字叫做:周四,那么计算它的地址时也是z+s=45,这样它与张三就有相同的地址,这就是冲突,也叫作碰撞!
冲突:两个不同的关键字,由于散列函数值相同,因而被映射到同一表位置上。该现象称为冲突(collision)或碰撞。发生冲突的两个关键字称为该散列函数的同义词(synonym)。
冲突基本上不可避免的,除非数据很少,我们只能采取措施尽量避免冲突,或者寻找解决冲突的办法。影响冲突的因素
冲突的频繁程度除了与h相关外,还与表的填满程度相关。
设m和n分别表示表长和表中填人的结点数,则将α=n/m定义为散列表的装填因子(load factor)。α越大,表越满,冲突的机会也越大。通常取α≤1。
散列函数的构造方法:
1、散列函数的选择有两条标准:简单和均匀。
简单指散列函数的计算简单快速;
均匀指对于关键字集合中的任一关键字,散列函数能以等概率将其映射到表空间的任何一个位置上。也就是说,散列函数能将子集k随机均匀地分布在表的地址集{0,1,…,m-1}上,以使冲突最小化。
2、常用散列函数
(1)直接寻址法:比如在一个0~100岁的年龄统计表,我们就可以把年龄作为地址。
(2)平方取中法
具体方法:先通过求关键字的平方值扩大相近数的差别,然后根据表长度取中间的几位数作为散列函数值。又因为一个乘积的中间几位数和乘数的每一位都相关,所以由此产生的散列地址较为均匀。
(3)除留余数法
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。该方法的关键是选取m。选取的m应使得散列函数值尽可能与关键字的各位相关。m最好为素数(4)随机数法
选择一个随机函数,取关键字的随机函数值为它的散列地址,即
h(key)=random(key)
其中random为伪随机函数,但要保证函数值是在0到m-1之间。
处理冲突的方法:
1、开放寻址法
hi=(h(key)+di) mod m i=1,2,...,k(k<=m-1)
其中m为表长,di为增量序列
如果di值可能为1,2,3,...m-1,称线性探测再散列。
如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2)
称二次探测再散列。
如果di取值可能为伪随机数列。称伪随机探测再散列。开放地址法堆装填因子的要求
开放寻址法要求散列表的装填因子α≤l,实用中取α为0.5到0.9之间的某个值为宜。
②二次探查法(quadratic probing)
二次探查法的探查序列是:
hi=(h(key)+i*i)%m 0≤i≤m-1 //即di=i2
即探查序列为d=h(key),d+12,d+22,…,等。
该方法的缺陷是不易探查到整个散列空间。
③双重散列法(double hashing)
该方法是开放寻址法中最好的方法之一,它的探查序列是:
hi=(h(key)+i*h1(key))%m 0≤i≤m-1 //即di=i*h1(key)
即探查序列为:
d=h(key),(d+h1(key))%m,(d+2h1(key))%m,…,等。
该方法使用了两个散列函数h(key)和h1(key),故也称为双散列函数探查法。
2、拉链法
拉链法解决冲突的方法
拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组t[0..m-1]。凡是散列地址为i的结点,均插入到以t为头指针的单链表中。t中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。
3、建立一个公共溢出区
假设哈希函数的值域为[0,m-1],则设向量hashtable[0..m-1]为基本表,另外设立存储空间向量overtable[0..v]用以存储发生冲突的记录。
性能分析
插入和删除的时间均取决于查找,故下面只分析查找操作的时间性能。
虽然散列表在关键字和存储位置之间建立了对应关系,理想情况是无须关键字的比较就可找到待查关键字。但是由于冲突的存在,散列表的查找过程仍是一个和关键字比较的过程,不过散列表的平均查找长度比顺序查找、二分查找等完全依赖于关键字比较的查找要小得多。
(1)查找成功的asl
散列表上的查找优于顺序查找和二分查找。
(2) 查找不成功的asl
对于不成功的查找,顺序查找和二分查找所需进行的关键字比较次数仅取决于表长,而散列查找所需进行的关键字比较次数和待查结点有关。因此,在等概率情况下,也可将散列表在查找不成功时的平均查找长度,定义为查找不成功时对关键字需要执行的平均比较次数。
注意:
①由同一个散列函数、不同的解决冲突方法构造的散列表,其平均查找长度是不相同的。
②散列表的平均查找长度不是结点个数n的函数,而是装填因子α的函数。因此在设计散列表时可选择α以控制散列表的平均查找长度。
③ α的取值
α越小,产生冲突的机会就小,但α过小,空间的浪费就过多。只要α选择合适,散列表上的平均查找长度就是一个常数,即散列表上查找的平均时间为o(1)。
④ 散列法与其他查找方法的区别
除散列法外,其他查找方法有共同特征为:均是建立在比较关键字的基础上。其中顺序查找是对无序集合的查找,每次关键字的比较结果为"="或"!="两种可能,其平均时间为o(n);其余的查找均是对有序集合的查找,每次关键字的比较有"="、"<"和">"三种可能,且每次比较后均能缩小下次的查找范围,故查找速度更快,其平均时间为o(lgn)。而散列法是根据关键字直接求出地址的查找方法,其查找的期望时间为o(1)。
例子:例子:选取哈希函数h(k)=(3k)%11,用线性探测再散列法处理冲突。
试在0~10的散列地址空间中,对关键序列22,41,53,46,30,13,01,67构造哈希表,并求等概率情况下查找不成功的平均查找长度asl。

⑧ 数据结构课程设计--学生成绩管理系统C语言

一、需求分析
1. 系统菜单的主要功能
(1)输入若干条记录
(2)查找并修改记录
(3)查找并删除记录
(4)成绩排序
(5)查找并显示一条记录
(6)将数据载入内存中
(7)将所有数据写入文件中
(0)退出程序
2. 功能分析
功能1为输入一条记录到结构体中去。
功能2、3和5算法相似,都是先查找成绩,2、3在此基础上再对查找出的信息进行操作。因为学生姓名定义成了字符数组的形式,因此在运用选择法进行排序的时候,要用到strcmp,strcpy等函数
功能4为成绩排序,可按学生学号排序或某单科成绩或总分排序,并输出相关的学生信息等。
功能6和7是对文件的操作,提前准备好数据。
二、总体设计
“学生成绩管理系统”包括:成绩录入、修改、删除、成绩统计和信息保存、载入这几个模块。
1、 主函数 main()
利用无限次循环while(1)和swithch()实现各函数的调用,系统根据输入的数字燃掘选项来调用相应的函数。
2、 菜单选择函数showmenu ();
这是一个无参函数,主要实现“功能选择”的界面,在这个界面里有显示系统的各项功能选单,根据每个功能前面的序号进行选择。等执行完每一个函数功能后,按任一键回到主界面也要通过这个函数来实现!3、 输入记录函数addstudent (stu *s) 这是一个带参函数,用来执行第学生成绩记录的输入,当学生为0时停止输入。
算法:利用函数参数s,并将s->next设为NULL。每输入一个数据就声明一个新节点p,把p->next设为NULL,并且链接到之前列表的尾端。4、 显示记录函数showstudent (stu *s) 这是一个不返回值的有参函数,形参为“链表头的指针”,负责对全部学生成绩记录的输出,不足之处就是不能对学生成绩进行分页显示。 算法:先将p结点的指针指向第一个结点,将p结点(即第一个结点)的数据输出。然后再将p结点的指针指向p指针的的指针(即下一结点),将p结点(即第一结点)的数据输出。重复执行此步聚直到p指针指向NULL为止。5、 查找记录函数findstudent (stu *s) 这是一个不返回值的有参函数,形参为“链表头的指针”,实现按学号对某个学生进行查找,并显示所查找到的记录。 算法:采用线性查找法往下一个节点查找。输入所要查找的学生的学号s,设一个指针变量p,先指向第一个结点,当strcmp(p->name,s) && p != NULL时,使p后移一个结点,如果p!=NULL,输出p所指的结点。6、 删除记录函数delstudent (stu *s) 这是一个有参函数,形参为“链表头的指针”,先输入要删除的学生记录的学号,找到后显示该学生信息,等确认后便可按“Y”进行删除。 算法:从p指向的第一个结点开始,检查该结点中的num值是否等于输入的要求删除的那个学号。如果相等就将该结点删除,如不相等,就将p后移一个结点,再如此进行下去,直到遇到表尾为止。7、保存数据到文件函数savestudent (stu *s) 这是一个不返回值的有参函数,形参为“链表头的指针”,可以把学生记录保存在电脑上由自己任意命名的二进制文件。8、从文件读数据函数loadstudent (stu *s) 这是一个不返回值的有参函数,形参为“链表头的指针”,根据输入的文件地址进行读取。

三、程序流程图
1)成绩统计:

2)厅段薯信息录入:
3)信息修改:
4)信息删除:
4)信息查询:

4)信息保存:5)主函数:
四、程序调试及体会
1)程序演示:

2)程序调试:
(1)刚开始没有那个初始化函数,程序运行后,没有输入任何数据就试得去执行显示功能,结果显示的是一些乱码!加入初始化函数后,这种现象也随之消失。
(2)刚开始执行输入函数,按学号顺序输入十个学生的成绩,输完后执行显示功能,学生成绩记录是按学号的反顺序显示的,试着在其中增加一些语句,希望能把学号按正常顺序显示,但暂时没有成功,所以在输入成绩时只能按学号反顺序输入,最后就按学号正常顺序输出了。
(3)刚开始时,先把扮者成绩按平均分排序,再插入一个学生的成绩,执行显示功能,虽然插入的学生的成绩能正常插入,但该学生的名次为0。后来,在插入成绩之后,调用排序函数,把所有成绩重新排序一次。
(4)在输入函数中设了一个无限循环,可以输入无数个学生的成绩信息,当学号为0的时候则停止输入。
(5)输入太多个学生的成绩时,屏幕显示不能控制为一页一页显示,所以为了方便起见,不要输入太多记录,十七左右为最佳。
(6)在没有输入任何信息的情况下,去执行排序功能,最后显示有一个记录,学号、姓名为空白,成绩都为0,名次为1。
(7)在输入选项时不能输入字母,否则会死循环,建议不要乱输字母。

3)心得体会:
经过一个星期的课程设计,感觉自己收获不少!
首先是:链表是数据结构的基本体现,所以这个课程设计里面主要都是用链表,而已要达到这样的功能,使用链表相当方便,但不容易理解,所以在这方面我很了很多的时间看课本和参考课外书,使C语言的知识强化了不少。
其次,在做课程设计的过程中,发现了平时很多没有注意到的问题,例如:返回值函数和不返回值函数两者在主函数中的调用是不同的…………
更重要的是,这次课程设计虽然花了我不少时间,但正是这些时间,让我见识到了要学好C语言,数据的处理是相当重要的。这个学生成绩管理系统都是在自己知识范围内完成的,所以界面清晰简单,可能不是很好看,但绝对实用!
从这里我也得到一个体会,做一个程序,或者开发一个软件,应该着重从它的后台制作入手,不能做出一个中看不中用的程序或者软件。
相信这次的课程设计为我以后继续从事计算机工作打了一个小小的开头。

参考书目;

[1]谭浩强. C程序设计(第三版) . 北京:清华大学出版社, 2006
[2]谭浩强. C程序设计上机指导(第三版) . 北京:清华大学出版社, 2005
[3]严蔚敏、吴伟民. 数据结构(C语言版) . 北京:清华大学出版社, 2006
计算机科学与技术系课程设计评分表

⑨ c语言数据结构(考题,测试你的能力)--编写源代码

P88 稀疏矩阵十字链表相加算法如下:
/*假设ha为A稀疏矩阵十字链表的头指针,hb为B稀疏矩阵十字链表的头指针*/
#include<stdio.h>
#define maxsize 100
struct linknode
{ int i,j;
struct linknode *cptr,*rptr;
union vnext
{ int v;
struct linknode *next;} k;
};

struct linknode creatlindmat( ) /*建立十字链表*/
{ int x, m, n, t, s, i, j, k;
struct linknode *p , *q, *cp[maxsize],*hm;
printf("请输入稀疏矩阵的行、列数及非零元个数\n");
scanf("%d%d%d",&m,&n,&t);
if (m>n) s=m; else s=n;
hm=(struct linknode*)malloc(sizeof(struct linknode)) ;
hm->i=m; hm->j=n;
cp[0]=hm;
for (i=1; i<=s;i++)
{ p=(struct linknode*)malloc(sizeof(struct linknode)) ;
p->i=0; p->j=0;
p->rptr=p; p->cptr=p;
cp[i]=p;
cp[i-1]->k.next=p;
}
cp[s]->k.next=hm;
for( x=1;x<=t;x++)
{ printf("请输入一个三元组(i,j,v)\n");
scanf("%d%d%d",&i,&j,&k);
p=(struct linknode*)malloc(sizeof(struct linknode));
p->i=i; p->j=j; p->k.v=k;
/*以下是将p插入到第i行链表中 */
q=cp[i];
while ((q->rptr!=cp[i]) &&( q->rptr->j<j))
q=q->rptr;
p->rptr=q->rptr;
q->rptr=p;
/*以下是将P插入到第j列链表中*/
q=cp[j];
while((q->cptr!=cp[j]) &&( q->cptr->i<i))
q=q->cptr;
p->cptr=q->cptr;
q->cptr=p;
}
return hm;
}
/* ha和hb表示的两个稀疏矩阵相加,相加的结果放入ha中*/
struct linknode *matadd(struct linknode *ha, struct linknode *hb)
{ struct linknode *pa, *pb, *qa, *ca,*cb,*p,*q;
struct linknode *hl[maxsize];
int i , j, n;
if((ha->i!=hb->i)||(ha->j!=hb->j))
printf("矩阵不匹配,不能相加\n");
else
{ p=ha->k.next; n=ha->j;
for (i=1;i<=n; i++)
{ hl[i]=p;
p=p->k.next;
}
ca=ha->k.next; cb=hb->k.next;
while(ca->i==0)
{pa=ca->rptr; pb=cb->rptr;
qa=ca;
while(pb->j!=0)
{ if((pa->j<pb->j)&&(pa->j!=0))
{ qa=pa; pa=pa->rptr;}
else if ((pa->j>pb->j)||(pa->j==0)) /*插入一个结点*/
{ p=(struct linknode*)malloc(sizeof(struct linknode));
p->i=pb->i; p->j=pb->j;
p->k.v=pb->k.v;
qa->rptr=p; p->rptr=pa;
qa=p; pb=pb->rptr;
j=p->j; q=hl[j]->cptr;
while((q->i<p->i)&&(q->i!=0))
{ hl[j]=q; q=hl[j]->cptr;}
hl[j]->cptr=p; p->cptr=q;
hl[j]=p;
}
else
{pa->k.v=pa->k.v+pb->k.v;
if(pa->k.v==0) /*删除一个结点*/
{ qa->rptr=pa->rptr;
j=pa->j; q=hl[j]->cptr;
while (q->i<pa->i)
{hl[j]=q; q=hl[j]->cptr;}
hl[j]->cptr=q->cptr;
pa=pa->rptr; pb=pb->rptr;
free(q);
}
else
{ qa=pa; pa=pa->rptr;
pb=pb->rptr;
}
}
}
ca=ca->k.next; cb=cb->k.next;
}
}
return ha;
}
void print(struct linknode *ha) /*输出十字链表*/
{ struct linknode *p,*q;
p=ha->k.next;
while(p->k.next!=ha)
{ q=p->rptr;
while(q->rptr!=p)
{ printf("%3d%3d%3d\t",q->i,q->j,q->k.v);
q=q->rptr;
}
if(p!=q)
printf("%3d%3d%3d",q->i,q->j,q->k.v);
printf("\n");
p=p->k.next;
}
q=p->rptr;
while(q->rptr!=p)
{ printf("%3d%3d%3d\t",q->i,q->j,q->k.v);
q=q->rptr;
}
if(p!=q)
printf("%3d%3d%3d",q->i,q->j,q->k.v);
printf("\n");
}

void main()
{
struct linknode *ha=NULL,*hb=NULL,*hc=NULL;
ha=creatlindmat( ); /*生成一个十字链表ha*/
hb=creatlindmat( ); /*生成另一个十字链表hb*/
printf("A:\n"); /*输出十字链表ha*/
print(ha);printf("\n");
printf("B:\n"); /*输出十字链表hb*/
print(hb);printf("\n");
hc=matadd(ha,hb); /*十字链表相加*/
printf("A+B:\n"); /*输出相加后的结果*/
print(hc);printf("\n");
}

P94 数据类型描述如下:
#define elemtype char
struct node1
{ int atom;
struct node1 *link;
union
{
struct node1 *slink;
elemtype data;
} ds;
}

P95 数据类型描述如下:
struct node2
{ elemtype data;
struct node2 *link1,*link2;
}

P96 求广义表的深度depth(LS)
int depth(struct node1 *LS)
{
int max=0,dep;
while(LS!=NULL)
{ if(LS->atom==0) //有子表
{ dep=depth(LS->ds.slink);
if(dep>max) max=dep;
}
LS=LS->link;
}
return max+1;
}

P96 广义表的建立creat(LS)
void creat(struct node1 *LS)
{
char ch;
scanf("%c",&ch);
if(ch=='#')
LS=NULL;
else if(ch=='(')
{LS=(struct node*)malloc(sizeof(struct node));
LS->atom=0;
creat(LS->ds.slink);
}
else
{ LS=(struct node*)malloc(sizeof(struct node));
LS->atom=1;
LS->ds.data=ch;
}
scanf("%c",&ch);
if(LS==NULL);
else if(ch==',')
creat(LS->link);
else if((ch==')')||(ch==';'))
LS->link=NULL;
}

P97 输出广义表print(LS)
void print(struct node1 *LS)
{
if(LS->atom==0)
{
printf("(");
if(LS->ds.slink==NULL)
printf("#");
else
print(LS->ds.slink);
}
else
printf("%c ",LS->ds.data);
if(LS->atom==0)
printf(")");
if(LS->link!=NULL)
{
printf(";");
print(LS->link);
}
}

P98 该算法的时间复杂度为O(n)。整个完整程序如下:
#include<stdio.h>
#define elemtype char
struct node1
{ int atom;
struct node1 *link;
union
{
struct node1 *slink;
elemtype data;
} ds;
};

void creat(struct node1 LS) /*建立广义表的单链表*/
{
char ch;
scanf("%c",&ch);
if(ch=='#')
LS=NULL;
else if(ch=='(')
{LS=(struct node1*)malloc(sizeof(struct node1));
LS->atom=0;
creat(LS->ds.slink);
}
else
{ LS=(struct node1*)malloc(sizeof(struct node1));
LS->atom=1;
LS->ds.data=ch;
}
scanf("%c",&ch);
if(LS==NULL);
else if(ch==',')
creat(LS->link);
else if((ch==')')||(ch==';'))
LS->link=NULL;
}
void print(struct node1 LS) /*输出广义单链表*/
{
if(LS->atom==0)
{
printf("(");
if(LS->ds.slink==NULL)
printf("#");
else
print(LS->ds.slink);
}
else
printf("%c",LS->ds.data);
if(LS->atom==0)
printf(")");
if(LS->link!=NULL)
{
printf(";");
print(LS->link);
}
}
int depth(struct node1 LS) /*求广义表的深度*/
{
int max=0;
while(LS!=NULL)
{ if(LS->atom==0)
{ int dep=depth(LS->ds.slink);
if(dep>max) max=dep;
}
LS=LS->link;
}
return max+1;
}
main()
{ int dep;
struct node1 *p=NULL;
creat(p); /*建立广义表的单链表*/
print(p); /*输出广义单链表*/
dep=depth(p); /*求广义表的深度*/
printf("%d\n",dep);
}

第六章 树
P109 二叉链表的结点类型定义如下:
typedef struct btnode
{ anytype data;
struct btnode *Lch,*Rch;
}tnodetype;

P109 三叉链表的结点类型定义如下:
typedef struct btnode3
{ anytype data;
struct btnode *Lch,*Rch,*Parent ;
}tnodetype3;

P112 C语言的先序遍历算法:
void preorder (tnodetype *t)
/*先序遍历二叉树算法,t为指向根结点的指针*/
{ if (t!=NULL)
{printf("%d ",t->data);
preorder(t->lch);
preorder(t->rch);
}
}

P113 C语言的中序遍历算法:
void inorder(tnodetype *t)
/*中序遍历二叉树算法,t为指向根结点的指针*/
{
if(t!=NULL)
{inorder(t->lch);
printf("%d ",t->data);
inorder(t->rch);
}
}

P113 C语言的后序遍历算法:
void postorder(tnodetype *t)
/*后序遍历二叉树算法,t为指向根结点的指针*/
{
if(t!=NULL)
{ postorder(t->lch);
postorder(t->rch);
printf("%d ",t->data);
}
}

P114 如果引入队列作为辅助存储工具,按层次遍历二叉树的算法可描述如下:
void levelorder(tnodetype *t)
/*按层次遍历二叉树算法,t为指向根结点的指针*/
{tnodetype q[20]; /*辅助队列*/
front=0;
rear=0; /*置空队列*/
if (t!=NULL)
{ rear++;
q[rear]=t; /*根结点入队*/
}
while (front!=rear)
{ front++;
t=q [front];
printf ("%c\n",t->data);
if (t->lch!=NULL) /*t的左孩子不空,则入队*/
{ rear++;
q [rear]=t->lch;
}
if (t->rch!=NULL) /*t的右孩子不空,则入队*/
{ rear++;
q [rear]=t->rch;
}
}
}

P115 以中序遍历的方法统计二叉树中的结点数和叶子结点数,算法描述为:
void inordercount (tnodetype *t)
/*中序遍历二叉树,统计树中的结点数和叶子结点数*/
{ if (t!=NULL)
{ inordercount (t->lch); /*中序遍历左子树*/
printf ("%c\n",t->data); /*访问根结点*/
countnode++; /*结点计数*/
if ((t->lch==NULL)&&(t->rch==NULL))
countleaf++; /*叶子结点计数*/
inordercount (t->rch); /*中序遍历右子树*/
}
}

P115 可按如下方法计算一棵二叉树的深度:
void preorderdeep (tnodetype *t,int j)
/*先序遍历二叉树,并计算二叉树的深度*/
{ if (t!=NULL)
{ printf ("%c\n",t->data); /*访问根结点*/
j++;
if (k<j) k=j;
preorderdeep (t->lch,j); /*先序遍历左子树*/
preorderdeep (t->rch,j); /*先序遍历右子树*/
}
}

P117 线索二叉树的结点类型定义如下:
struct nodexs
{anytype data;
struct nodexs *lch, *rch;
int ltag,rtag; /*左、右标志域*/
}

P117 中序次序线索化算法
void inorderxs (struct nodexs *t)
/*中序遍历t所指向的二叉树,并为结点建立线索*/
{ if (t!=NULL)
{ inorderxs (t->lch);
printf ("%c\n",t->data);
if (t->lch!=NULL)
t->ltag=0;
else { t->ltag=1;
t->lch=pr;
} /*建立t所指向结点的左线索,令其指向前驱结点pr*/
if (pr!=NULL)
{ if (pr->rch!=NULL)
pr->rtag=0;
else { pr->rtag=1;
pr->rch=p;
}
} /*建立pr所指向结点的右线索,令其指向后继结点p*/
pr=p;
inorderxs (t->rch);
}
}

P118 在中根线索树上检索某结点的前驱结点的算法描述如下:
struct nodexs * inpre (struct nodexs *q)
/*在中根线索树上检索q所指向的结点的前驱结点*/
{ if (q->ltag==1)
p=q->lch;
else { r=q->lch;
while (r->rtag!=1)
r=r->rch;
p=r;
}
return (p);
}

P119 在中根线索树上检索某结点的后继结点的算法描述如下:
struct nodexs * insucc (struct nodexs *q)
/*在中根线索树上检索q所指向的结点的后继结点*/
{ if (q->rtag==1)
p=q->rch;
else { r=q->rch;
while (r->ltag!=1)
r=r->lch;
p=r;
}
return (p);
}

P120 算法程序用C语言描述如下:
void sortBT(BT *t,BT *s) /*将指针s所指的结点插入到以t为根指针的二叉树中*/
{ if (t==NULL) t=s; /*若t所指为空树,s所指结点为根*/
else if (s->data < t->data)
sortBT(t->lch,s); /*s结点插入到t的左子树上去*/
else
sortBT(t->rch,s); /*s结点插入到t的右子树上去*/
}

P121 二叉排序树结点删除算法的C语言描述如下:
void delnode(bt,f,p)
/*bt为一棵二叉排序树的根指针,p指向被删除结点,f指向其双亲*/
/*当p=bt时f为NULL*/
{ fag=0; /*fag=0时需修改f指针信息,fag=1时不需修改*/
if (p->lch==NULL)
s=p->rch; /*被删除结点为叶子或其左子树为空*/
else if (p->rch==NULL)
s=p->lch;
else { q=p; /*被删除结点的左、右子树均非空*/
s=p->lch;
while (s->rch!=NULL)
{ q=s;
s=s->rch;
} /*寻找s结点*/
if (q=p)
q->lch=s->lch;
else q->rch=s->lch;
p->data=s->data; /*s所指向的结点代替被删除结点*/
DISPOSE(s);
Fag=1;
}
if (fag=0) /*需要修改双亲指针*/
{ if (f=NULL)
bt=s; /*被删除结点为根结点*/
else if (f->lch=p)
f->lch=s;
else f->rch=s;
DISPOSE(p); /*释放被删除结点*/
}
}

第七章 图
P134 用邻接矩阵表示法表示图,除了存储用于表示顶点间相邻关系的邻接矩阵外,通常还需要用一个顺序表来存储顶点信息。其形式说明如下:
# define n 6 /*图的顶点数*/
# define e 8 /*图的边(弧)数*/
typedef char vextype; /*顶点的数据类型*/
typedef float adjtype; /*权值类型*/
typedef struct
{vextype vexs[n];
adjtype arcs[n][n];
}graph;

P135 建立一个无向网络的算法。
CREATGRAPH(ga) /*建立无向网络*/
Graph * ga;
{
int i,j,k;
float w;
for(i=0;i<n;i++ )
ga ->vexs[i]=getchar(); /*读入顶点信息,建立顶点表*/
for(i=0;i<n;i++ )
for(j=0;j<n;j++)
ga ->arcs[i][j]=0; /*邻接矩阵初始化*/
for(k=0;k<e;k++) /*读入e条边*/
(scanf("%d%d%f",&I,&j,&w); /*读入边(vi,vj)上的权w */
ga ->arcs[i][j]=w;
ga - >arcs[j][i]=w;
}
} /*CREATGRAPH*/

P136 邻接表的形式说明及其建立算法:
typedef struct node
{int adjvex; /*邻接点域*/
struct node * next; /*链域*/
}edgenode; /*边表结点*/
typedef struct
{vextype vertex; /*顶点信息*/
edgenode link; /*边表头指针*/
}vexnode; /*顶点表结点*/
vexnode ga[n];

CREATADJLIST(ga) /*建立无向图的邻接表*/
Vexnode ga[ ];
{int i,j,k;
edgenode * s;
for(i=o;i<n;i++= /*读入顶点信息*/
(ga[i].vertex=getchar();
ga[i].1ink=NULL; /*边表头指针初始化*/
}
for(k=0;k<e;k++= /*建立边表*/
{scanf("%d%d",&i,&j); /*读入边(vi , vj)的顶点对序号*/
s=malloc(sizeof(edgenode)); /*生成邻接点序号为j的表结点*s */
s-> adjvex=j;
s- - >next:=ga[i].Link;
ga[i].1ink=s; /*将*s插入顶点vi的边表头部*/
s=malloc(size0f(edgende)); /*生成邻接点序号为i的边表结点*s */
s ->adjvex=i;
s ->next=ga[j].1ink;
ga[j].1ink=s; /*将*s插入顶点vj的边表头部*/
}
} /* CREATADJLIST */

P139 分别以邻接矩阵和邻接表作为图的存储结构给出具体算法,算法中g、g1和visited为全程量,visited的各分量初始值均为FALSE。
int visited[n] /*定义布尔向量visitd为全程量*/
Graph g; /*图g为全程量*/

DFS(i) /*从Vi+1出发深度优先搜索图g,g用邻接矩阵表示*/
int i;
{ int j;
printf("node:%c\n" , g.vexs[i]); /*访问出发点vi+1 */
Visited[i]=TRUE; /*标记vi+l已访问过*/
for (j=0;j<n;j++) /*依次搜索vi+1的邻接点*/
if((g.arcs[i][j]==1) &&(! visited[j]))
DFS(j); /*若Vi+l的邻接点vj+l未曾访问过,则从vj+l出发进行深度优先搜索*/
} /*DFS*/
vexnode gl[n] /*邻接表全程量*/

DFSL(i) /*从vi+l出发深度优先搜索图g1,g1用邻接表表示*/
int i;
{ int j;
edgenode * p;
printf("node:%C\n" ,g1[i].vertex);
vistited[i]=TRUE;
p=g1[i].1ink; /*取vi+1的边表头指针*/
while(p !=NULL) /*依次搜索vi+l的邻接点*/
{
if(! Vistited[p ->adjvex])
DFSL(p - >adjvex); /*从vi+1的未曾访问过的邻接点出发进行深度优先搜索*/
p=p - >next; /*找vi+l的下一个邻接点*/
}
} /* DFSL */

P142 以邻接矩阵和邻接表作为图的存储结构,分别给出宽度优先搜索算法。
BFS(k) /*从vk+l出发宽度优先搜索图g,g用邻接矩阵表示,visited为访问标志向量*/
int k;
{ int i,j;
SETNULL(Q); /*置空队Q */
printf("%c\n",g.vexs[k]); /*访问出发点vk+l*x/
visited[k]=TRUE; /*标记vk+l已访问过*/
ENQUEUE(Q,K); /*已访问过的顶点(序号)入队列*/
While(!EMPTY(Q)) /*队非空时执行*/
{i=DEQUEUE(Q); /*队头元素序号出队列*/
for(j=0;j<n;j++)
if((g.arcs[i][j]==1)&&(! visited[j]))
{printf("%c\n" , g.vexs[j]); /*访问vi+l的未曾访问的邻接点vj+l */
visited[j]=TRUE;
ENQUEUE(Q,j); /*访问过的顶点入队*/
}
}
} /* BFS */
BFSL(k) /*从vk+l出发宽度优先搜索图g1,g1用邻接表表示*/
int k
{ int i;
edgenode * p;
SETNULL(Q);
printf("%c\n" , g1[k].vertex);
visited[k]=TRUE;
ENQUEUE(Q,k);
while(! EMPTY(Q));
{ i=DEQUEUE(Q);
p=g1[i].1ink /*取vi+l的边表头指针*/
while(p !=NULL) /*依次搜索vi+l的邻接点*/
{ if( ! visited[p - >adjvex]) /*访问vi+l的未访问的邻接点*/
{ printf{"%c\n" , g1[p - >adjvex].vertex};
visited[p - >adjvex]=TRUE;
ENQUEUE(Q,p - >adjvex); /*访问过的顶点入队*/
}
p=p - >next; /*找vi+l的下一个邻接点*/
}
}
} /*BFSL*/

P148 在对算法Prim求精之前,先确定有关的存储结构如下:
typdef struct
{Int fromvex,endvex; /*边的起点和终点*/
float length; /*边的权值*/
} edge;

float dist[n][n]; /*连通网络的带权邻接矩阵*/
edgeT[n-1]; /*生成树*/

P149 抽象语句(1)可求精为:
for(j=1;j<n;j++) /*对n-1个蓝点构造候选紫边集*/
{T[j-1].fromvex=1}; /*紫边的起点为红点*/
T[j-1].endvex=j+1; /*紫边的终点为蓝点*/
T[j-1].1ength=dist[0][j]; /*紫边长度*/
}

P149 抽象语句(3)所求的第k条最短紫边可求精为:
min=max; /*znax大于任何边上的权值*/
for (j=k;j<n-1;j++) /*扫描当前候选紫边集T[k]到T[n-2],找最短紫边*/
if(T[j].1ength<min)
{min=T[j].1ength;m=j; /*记录当前最短紫边的位置*/
}

P149 抽象语句(4)的求精:
e=T[m];T[m]=T[k];T[k]=e, /* T[k]和T[m]交换*/
v=T[kl.Endvex]; /* v是刚被涂红色的顶点*/

P149 抽象语句(5)可求精为:
for(j=k+1;j<n-1;j++) /*调整候选紫边集T[k+1]到T[n-2]*/
{d=dist[v-1][T[j].endvex-1]; /*新紫边的长度*/
if(d<T[j].1ength) /*新紫边的长度小于原最短紫边*/
{T[j].1ength=d;
T[j].fromvex=v; /*新紫边取代原最短紫边*/
}
}

P150 完整的算法:
PRIM() /*从第一个顶点出发构造连通网络dist的最小生成树,结果放在T中*/
{int j , k , m , v , min , max=l0000;
float d;
edge e;
for(j=1;j<n;j++) /*构造初始候选紫边集*/
{T[j-1].formvex=1; /*顶点1是第一个加入树中的红点*/
T[j-1].endvex=j+1;
T[j-1].length=dist[o][j];
}
for(k=0;k<n-1;k++) /*求第k条边*/
{min=max;
for(j=k;j<n-1;j++) /*在候选紫边集中找最短紫边*/
if(T[j].1ength<min)
{min=T[j].1ength;
m=j;
} /*T[m]是当前最短紫边*/
}
e=T[m];T[m]=T[k];T[k]=e; /*T[k]和T[m]交换后,T[k]是第k条红色树边*/
v=T[k].endvex ; /* v是新红点*/
for(j=k+1;j<n-1;j++) /*调整候选紫边集*/
{d=dist[v-1][T[j].endvex-1];
if(d<T[j].1ength);
{T[j].1ength=d;
T[j].fromvex=v;
}
}
} /* PRIM */

P151 Kruskl算法的粗略描述:
T=(V,φ);
While(T中所含边数<n-1)
{从E中选取当前最短边(u,v);
从E中删去边(u,v);
if((u,v)并入T之后不产生回路,将边(u,v)并入T中;
}

P153 迪杰斯特拉算法实现。算法描述如下:
#define max 32767 /*max代表一个很大的数*/
void dijkstra (float cost[][n],int v)
/*求源点v到其余顶点的最短路径及其长度*/
{ v1=v-1;
for (i=0;i<n;i++)
{ dist[i]=cost[v1][i]; /*初始化dist*/
if (dist[i]<max)
pre[i]=v;
else pre[i]=0;
}
pre[v1]=0;
for (i=0;i<n;i++)
s[i]=0; /*s数组初始化为空*/
s[v1]=1; /*将源点v归入s集合*/
for (i=0;i<n;i++)
{ min=max;
for (j=0;j<n;j++)
if (!s[j] && (dist[j]<min))
{ min=dist[j];
k=j;
} /*选择dist值最小的顶点k+1*/
s[k]=1; /*将顶点k+1归入s集合中*/
for (j=0;j<n;j++)
if (!s[j]&&(dist[j]>dist[k]+cost[k][j]))
{ dist[j]=dist[k]+cost[k][j]; /*修改 V-S集合中各顶点的dist值*/
pre[j]=k+1; /*k+1顶点是j+1顶点的前驱*/
}
} /*所有顶点均已加入到S集合中*/
for (j=0;j<n;j++) /*打印结果*/
{ printf("%f\n%d",dist[j],j+1;);
p=pre[j];
while (p!=0)
{ printf("%d",p);
p=pre[p-1];
}
}
}

P155 弗洛伊德算法可以描述为:
A(0)[i][j]=cost[i][j]; //cost为图的邻接矩阵
A(k)[i][j]=min{A(k-1) [i][j],A(k-1) [i][k]+A(k-1) [k][j]}
其中 k=1,2,…,n

P155 弗洛伊德算法实现。算法描述如下:
int path[n][n]; /*路径矩阵*/
void floyd (float A[][n],cost[][n])
{ for (i=0;i<n;i++) /*设置A和path的初值*/
for (j=0;j<n;j++)
{ if (cost[i][j]<max)
path[i][j]=j;
else { path[i][j]=0;
A[i][j]=cost[i][j];
}
}
for (k=0;k<n;k++)
/*做n次迭代,每次均试图将顶点k扩充到当前求得的从i到j的最短路径上*/
for (i=0;i<n;i++)
for (j=0;j<n;j++)
if (A[i][j]>(A[i][k]+A[k]

⑩ 用哈希表实现C语言关键字的算法

#include <iostream.h>
#include<fstream.h>
#include <string>
#include <iomanip.h>
using namespace std;

const int TOTAL=32;
const int MAXLEN=10;
const int HASHLEN=41;
int cont=0;
class HashTable
{
public:
char keyword[MAXLEN];
int count;
int num;
};
HashTable HS[HASHLEN];

char KeyWords[TOTAL][MAXLEN]= {
"char", "double", "enum", "float", "int", "long", "short", "signed",
"struct", "union", "unsigned", "void", "break", "case", "continue",
"default", "do", "else", "for", "goto", "if", "return", "switch", "while",
"auto", "extern", "register", "static", "const", "sizeof", "typedef", "volatile"
};

template<class T>
class HASH
{
public:
void Show(int key);
int CreatHX(char *keyword);
int GetFreePos(int key);
void ResetHX();
int GetKey(char *keyword);
int isKeyWords(char *word);
int Read(char *filename);
int isLetter(char ch);
int FindHX(char *keyword);
private:
int key;
char *keyword;
char *word;
char ch;

};
template<class T>
void HASH<T>::Show(int key)
{
if(key<0||key>=HASHLEN)
{
cout<<"关键字不存在!"<<endl;
return;
}
if(strlen(HS[key].keyword)==0)
{
cout<<"哈希表位置:"<<key<<" 记录是空的"<<endl;
return ;
}
cout<<"哈希表位置: "<<key<<" 关键字: "
<<HS[key].keyword<<" 出现次数 "<<HS[key].count<<endl;
cont++;
}

template<class T>
int HASH<T>::CreatHX(char *keyword)
{
int key;
if(!isKeyWords(keyword)) return -1;
key=GetKey(keyword);

if(strlen(HS[key].keyword)>0)
{
if(strcmp(HS[key].keyword,keyword)==0)
{
HS[key].count++;
return 1;
}
key=FindHX(keyword);
if(key<0)
{
key=GetFreePos(GetKey(keyword));
if(key<0) return -1;
strcpy(HS[key].keyword,keyword);
}

if(key<0) return -1;
HS[key].count++;
}
else
{
strcpy(HS[key].keyword,keyword);
HS[key].count++;
}
return 1;
}
template<class T>
int HASH<T>::GetFreePos(int key)
{
int find,tem=0;
if(key<0||key>=HASHLEN) return -1;
for(find=key+1;find<HASHLEN;find++)
{
tem++;
if(strlen(HS[find].keyword)==0){
HS[find].num=tem;
return find; }
}

for(find=0;find<key;find++)
{
tem++;
if(strlen(HS[find].keyword)==0){
HS[find].num=tem;
return find; }
}
return -1;
}

template<class T>
void HASH<T>::ResetHX()
{
int i;
for(i=0;i<HASHLEN;i++)
{
strcpy(HS[i].keyword,"");
HS[i].count=0;
HS[i].num=0;
}
}

template<class T>
int HASH<T>::GetKey(char *keyword)
{
return ( keyword[0]*100+keyword[strlen(keyword)-1] ) % 41;
}

template<class T>
int HASH<T>::isKeyWords(char *word)
{
int i;
for(i=0;i<TOTAL;i++)
if(strcmp(word,KeyWords[i])==0) return 1;
return 0;
}

template<class T>
int HASH<T>::isLetter(char ch)
{
if( (ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z') ) return 1;
else return 0;

}

template<class T>
int HASH<T>::FindHX(char *keyword)
{
int key,find,tem=0;
if(!isKeyWords(keyword)) return -1;
key=GetKey(keyword);
if(strcmp(HS[key].keyword,keyword)==0) return key;
for(find=key+1;find<HASHLEN;find++)
{
tem++;
if(strcmp(HS[find].keyword,keyword)==0){
HS[find].num=tem;
return find; }
}

for(find=0;find<key;find++)
{
tem++;
if(strcmp(HS[find].keyword,keyword)==0){
HS[find].num=tem;
return find; }
}
return -1;
}

template<class T>
int HASH<T>::Read(char *filename)
{
char word[MAXLEN],ch;
int i;
FILE *read;
fstream myfile;
myfile.open("filename");
if(!myfile)
{
cout<<"文件不存在,请确认好再输入!"<<endl;
return -1;
}
ResetHX();
while(!feof(read))
{
i=0;
ch=fgetc(read);
while(isLetter(ch)==0 && feof(read)==0 )
ch=fgetc(read);
while(isLetter(ch)==1 && feof(read)==0 )
{
if(i==MAXLEN)
{
while(isLetter(ch)==1&& feof(read)==0)
{
ch=fgetc(read);
}
i=0;
break;
}

else
{
word[i++]=ch;
ch=fgetc(read);
}
}
word[i]='\0';
if(isKeyWords(word))
{
CreatHX(word);
}
}
fclose(read);
cout<<"读取成功,文件中关键字已经存入hash表,请继续操作"<<endl;
return 1;
}

void main()
{
HASH<char> hash;
char filename[128],word[MAXLEN];
int i=0,count=0;
int key;
char j,y;
cout<<"请输入要读取的文件名:";
cin>>filename;
cout<<endl;
hash.Read(filename);
for(i=0;i<HASHLEN;i++)
{
hash.Show(i);
getchar();
}
cout<<"关键字总数为:"<<cont<<endl;
cout<<"请输入你想要查找的关键字: ";
cin>>word;
cout<<endl;
hash.Show(hash.FindHX(word));
cout<<" C语言中的关键字和其在哈希表的位置:"<<endl;
for(i=0;i<TOTAL;i++)
{
cout<<setiosflags(ios::left)<<"["<<setw(2)<<hash.GetKey(KeyWords[i])<<"]"
<<setiosflags(ios::left)<<setw(11)<<KeyWords[i];
if((i+1)%4==0) cout<<endl;
}
cout<<"是否显示冲突关键字列表?y(是) 其它(否):";
cin>>j;
if(j=='y')
{
cout<<"冲突关键字列表"<<endl;
for(i=0;i<HASHLEN;i++)
{
if(strlen(HS[i].keyword)>0)
{
key=hash.GetKey(HS[i].keyword);
if(key!=i)
{
count++;
cout<<setiosflags(ios::left)<<setw(14)<<
"\t[关键 字]:"<<HS[i].keyword<<setiosflags(ios::left)<<
setw(20)<<"\t[哈希表当前位置]: "<<i<<setiosflags(ios::left)<<
setw(20)<<"\t[冲突次数]: "<<HS[i].num<<endl;
}
}
}
if(count==0)
cout<<"没有冲突"<<endl;
else
cout<<"\t冲突关键字共:"<<count<<"个"<<endl;
}
else
cout<<"不显示冲突关键字列表,但已存在!"<<endl;
return;
}