1 / 97

# 搜索初探 - PowerPoint PPT Presentation

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

## PowerPoint Slideshow about '搜索初探' - gayora

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

### 搜索初探

Cs 2.6

1040310615, simple, Swai

• 百鸡问题

a+b+c=n

5a+3b+c/3=n

• 输出：满足问题的解的数目k,公鸡,母鸡,小鸡的只数g[], m[], s[]

1.void chicken_question(int n, int &k, int g[], int m[], int s[])

2.{

• int a, b, c;
• k=0;
• for(a=0;a<=n;a++){
• for(b=0;b<=n;b++){
• for(c=0;c<=n;c++){

8. if((a+b+c==n)&&(5*a+3*b+c/3==n)&&(c%3==0)){

• g[k]=a;
• m[k]=b;
• s[k]=c;
• k++;
• }
• }
• }
• }

17. }

• 输入：所购买三种鸡的总数目n
• 输出：满足问题的解的数目k,公鸡,母鸡,小鸡的只数g[], m[], s[]

1.void chicken_question(int n, int &k, int g[], int m[], int s[])

2.{

• int i, j, a, b, c;
• k=0;
• i=n/5;
• j=n/3;
• for(a=0;a<=n;a++){
• for(b=0;b<=n;b++){
• c=n-a-b;
• if((a+b+c==n)&&(5*a+3*b+c/3==n)&&(c%3==0)){
• g[k]=a;
• m[k]=b;
• s[k]=c;
• k++;
• }
• }
• }

16.}

T2(n)<=1+2+2+1+2(n/5+1)+n/5+1+2(n/5+1)(n/3+1)+(3+10+4)(n/5+1)(n/3+1)=19n2/15+161n/15+28≈c2n2 , c2 >0， （n很大时）

n→∞

lim f(n)/g(n) ≠ ∞，即:f(n)= Ο(g(n))

n→∞这个定义表明：f(n)的增长至多像g(n)的增长那么快。此时称Ο(g(n))是f(n)的上界

n→∞

lim f(n)/g(n) ≠ 0，即:f(n)= s(g(n))

n→∞这个定义表明：f(n)的增长至少像g(n)的增长那么快。此时称s(g(n))是f(n)的下界

n→∞

lim f(n)/g(n) = c，即:f(n)= ⊙(g(n))

n→∞

e.g.指数函数f(n)=5*2n+n2

f(n)>=5*2n=c1g(n)

f(n)<=5*2n+2n<=6*2n=c2g(n)

1﹤ ㏒ ㏒ n ﹤ ㏒ n ﹤(n)1/2 ﹤n3/4 ﹤n ﹤n㏒n ﹤n2 ﹤2n ﹤n! ﹤2n2

• 循环次数的统计
• 基本操作频率的统计
• 计算步的统计
• 最坏情况分析
• 平均情况分析

1. int linear_search(int A[], int n, int x)

2. {

3. int j=0;

• while(j
• j++;
• if(x==A[j])
• return j;
• else
• return -1;

10. }

1.int binary_search(int A[], int n, int x)

2.{

• int mid, low=0, high=n-1, j=-1;
• while(low<=high && j<0){
• mid=(low+high)/2;
• if(x==A[mid]) j=mid;
• else if(x
• else low=mid+1;
• }
• return j;

11.}

• 在二叉检索算法中，当数组中不存在元素x，或者x是数组的第一个元素与最后一个元素，这是二叉检索算法的最坏情况。假定x是数组的最后一个元素，第一次比较后，数组后半部分的元素个数是[n/2],这是第二次要继续进行检索的元素个数。类似的，第三次进行检索的元素个数是[n/4],…第i次检索时元素的个数是[n/2j-1].这种情况一直延续到被检索的元素的个数是1。假设检索x次所需要检索的最大比较次数是j次，则j满足：

[n/2j-1]=1，即j-1≤㏒n≤j,得到j= [㏒n]+1,这表明在最坏情况下，二叉检索算法元素比较次数最多为[㏒n]+1次，因此它的时间复杂性是Ο(㏒n),它至少执行[㏒n]+1次，因此，它的时间复杂性是⊙(㏒n)。

e.g.用插入法按递增顺序排序数组A

1. void insert_sort(int A[], int n)

2. {

• int a, i, j;
• for(i=1; i
• a=A[i];
• j=i-1;
• while(j>=0 && A[j] > a){
• A[j+1] = A[j];
• j--;
• }
• A[j+1] = a;
• }

13. }

ii-1

Ti=(i-1)/i+∑(i-j+1)/i=(i-1)/i +∑j/i = ½+i/2-1/i

j=2j=1

nnn n

T= ∑Ti=∑(1/2+i/2-1/i)=(n-1)/2+1/2 ∑i-∑1/i +1

i=2 i=2 n i=2 i=1

=(n2+3n)/4 - ∑1/i

i=1 n

i=1

### 查找（Search）

1、静态查找表2、动态查找表3、哈希查找表

• 查找是确定在数据元素集合中是否存在一个数据元素关键字等于给定关键字的过程
• 操作过程：比较

• 动态查找：不但检查是否存在，而且当不存在时要将记录插

• 衡量查找算法效率的标准：

ASL = ∑Pi * Ci

Pi 是查找第i个记录的概率

Ci是查找第i个记录所需要比较的次数

• 顺序查找

int SequelSeach(elemtype s[], keytype Key, int n)

/*在s[0]~s[n-1]中顺序查找关键字为Key的记录*/

/*查找成功返回该记录的下标序号；失败时返回-1*/

{

int i=0;

while(i

if(s[i].Key == Key) return i;

else return -1;

}

e.g. 定义如下主函数:

typedef int keytype;

typedef struct{

keytype Key;

}elemtype;

int SequelSearch(elemtype s[], keytype Key, int n);

int main(void)

{

elemtype Test[10]={710,342,45,686,6,

841,429,134,68,264};

int n=10, Key=686, i;

i = SequelSeach(Test, Key, n);

if(i!=-1)

printf(“\n查找成功！该数据元素为第%d个记录”，i );

else printf(“\n查找失败！该数据元素在记录集合中不存在”)；

}

int SequelSeach(elemtype s[], keytype Key, int n)

/*在s[0]~s[n-1]中顺序查找关键字为Key的记录*/

/*查找成功返回该记录的下标序号；失败时返回-1*/

{

int i=0;

s[n].Key = Key; /*s[n]为监视哨*/

while( s[i].Key!= Key) i++;

if(i >= n) return -1;

else return i;

}

Pi=1/n,(i = 1,2,3,…,n), Ci = i, 则查找成功时

ASLsq = (n+1)/2；

• 使用条件：顺序表，表内元素之间有序

s[0].Key <= s[1].Key <= …<= s[n-1].Key

(1)令mid = [(low+high)/2]

(2)比较Key与s[mid].Key

1. s[mid].Key=Key, 查找成功

2. s[mid].Key

3. s[mid].Key>Key,查找s[mid]前半部， high=mid-1

(3)比较low和high,若low<=high,重复(1)(2);否则，表中不存在关键字为Key的记录，查找失败

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[i+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 7 （初始时为最大下标 n ）;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 =4

mid=4 但 key=9 < 10, 向左

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

high=7

low=1

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;

mid=4

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=3（mid-1）

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 =2

mid=2

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=3（mid-1）

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 =2

mid=2; 但 key=9 > 8, 向右

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=3（mid-1）

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 3; high（高下标）= 3 ;

mid=2

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

high=3

low=3（mid+1）

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 3; high（高下标）= 3 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 = 3

mid=3;

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

high=3

low=3

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 9 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 = 3

mid=3; 但 key=9 中点值也为 9 ，找到

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

high=3

low=3

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找不成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 7 （初始时为最大下标 n ）;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2

mid=4 但 key=5 < 10, 向左

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

high=7

low=1

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找不成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;

mid=4

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=3（mid-1）

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找不成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 =2

mid=2

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=3

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 3 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 =2

mid=2; 但 key=5 < 8, 向左

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=3

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 1 ;

mid=2

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=1（mid-1）

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 1 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 = 1

mid=1

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=1

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、应用范围：顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 1 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 = 1

mid=1; 但 key=5 > 4, 向右

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=1

high=1

1、静态查找表
• 2、有序表的查找
• 折半查找（或二分查找法）
• 1、顺序表，表内元素之间有序。不可直接用于线性链表。

e.g: 查找 key = 5 的结点所在的数组元素的下标地址。

• 查找成功的情况：数组 ST.elem 如下图所示有序
• 数组 ST.elem ：递增序 ST.elem[i]. Key <= ST.elem[I+1]. Key; i= 1,2,……n-1
• 查找范围 ：low（低下标）= 1; high（高下标）= 1 ;
• 比较对象：中点元素，其下标地址为 mid = （low+high）/ 2 = 1

mid=1; 但 key=5 > 4, 向右

key

4

8

9

10

11

13

19

0

1

2

3

4

5

6

7

low=2 （mid+1）

high=1

int BinarySeach(elemtype s[], keytype key,int n)

/*在有序表s[0]~s[n-1]中二分查找关键字为Key的记录*/

/*查找成功时返回记录的下标序号，失败时返回-1*/

{

int low=0, high=n-1, mid;

while(low<=high){

mid=(low+high)/2;

if(s[mid].key == key) return mid;

else if(s[mid].key

else high = mid-1;

}

return -1;

}

3、性能分析：

2、平均情况分析（在成功查找的情况下）：

∴ASL = ( 20×1+ 21×2+ 22×3+ … + 2t-1 ×t) / n

t

= ∑ (i × 2i-1 ) / n

i=1

= [(n + 1) ×( log2(n + 1) - 1 ) + 1 ] / n

= (n + 1) ×log2(n + 1) / n - 1

= (n + 1) ×log2(n + 1) / n - 1

ASL = log2(n + 1) - 1 或者简单地记为：ASL = log2n - 1

### 图的搜索算法

——simple

2 5 10

3 6 8

4 7 9

1.确定邻接于1的顶点集合.

2.确定邻接于{ 2，3，4 }的新的顶点集合.

3.邻接{ 5，6，7 }的顶点集合是{ 8，9 }，不存在邻接于

{ 8.9 }的顶点.

2 5 10

• 3 6 8

4 7 9

//从顶点v开始的宽度优先搜索

While(Q不为空)

{

while(u)

{

if(u尚未被标记)

{

}

u = 邻接于w的下一个顶点;

}

}

2 5

1 3 6 8

4 7 9

2.类Network

u = 邻接于w的下一个顶点;

Class Network

{

public:

virtual int Begin(int i) = 0 ;

virtual int NextVertex(int i) = 0 ;

virtual void InitializePos() = 0 ;

virtual void DeactivatePos() = 0 ;

}

3.BFS的实现:

void Network::BFS(int v , int reach[] , int label)

{

InitializePos();//初始化图遍历器数组

reach[v] = label;

while(!Q.IsEmpty())

{

int w;

Q.Delete(w);//获取一个已标记的顶点

int u = Begin(w);

while(u) //访问w的邻接顶点

{

if(!reach[u])//一个未曾到达的顶点

{

reach[u] = label;//标记已到达该顶点

}

u = NextVertex;//下一个与w邻接的顶点

}

}

DeactivatePos();//释放遍历器数组

}

4.BSF的复杂度分析

Template

Void AdjacencyWDigraph::BFS(int v , int reach[] , int label)

{ //宽度优先搜索

reach[v] = label;

while(!Q.IsEmpty())

{

int w;

Q.Delete(w); //获取一个已标记的顶点

for(int u = 1 ; u <= n ; u ++) //对尚未标记的,邻接自w的顶点进行标记

{

if(a[w][u] != NoEdge && !reach[u])

{

reach[u] = label;

}

}

}

}

void LinkedDigraph::BFS(int v , int reach[] , int label)

{

reach[v] = label;

While(!Q.IsEmpty())

{

int w ;

Q.Delete(w);

ChainNode*p;

for(p = h[w],First() ; p ; p = p ->link)

{

int u = p -> data ;

if(!reach[u])

{

reach[u] = label;

}

}

}

}

5.深度优先搜索

void Network::DFS(int v , int reach[] , int label)

{

initializePos();

dfs(v , reach , label);

DeactivatePos();

}

Void Network::dfs(int v , int reach[] , int label)

{

reach[v] = label;

int u = Begin(v);

while(u) {

if(!reach[u])

dfs(u , reach , label);

u = NetVertex(v); }

}

1 2 3 … n-1 n

DFS的最坏情况;BFS的最好情况

1

2 3 4 … n-1 n

DFS的最好情况;BFS的最坏情况

1 2 3 Marlin In

4 5 6

7 8 9

Nemo

• 广度优先构造搜索树，时间复杂度为指数级
• 用启发式搜索

1.必须有一个对工作量的合理估计

2.启发应容易计算

• 稍好一点的启发:

While(目标节点未到达)

{选择所有节点中启发值最小的最左叶节点。

}

### Hash Table

Swai

• 哈希表的定义
• 构造哈希函数
• 冲突解决方法
• 哈希表的优劣
Hash Table
• 一般的线性表，树中，记录在结构中的相对位置是随机的，即和记录的关键字之间不存在确定的关系，因此，在结构中查找记录时需进行一系列和关键字的比较。这一类查找方法建立在“比较“的基础上，查找的效率依赖于查找过程中所进行的比较次数。
• 哈希表使用关键字的值为基础，计算关键字的位置，来确定目标在表中的位置。同样使用线形表，能直接访问表中的元素
• 但是现实是，理想哈希函数几乎不存在。例如：
template

class Hash Table{

public:

Hast Table(int divisor = 11);

~Hash Table(){delete[]ht;delete[]empty};

bool Search(const K&k,E&e)const;

Hash Table%Insert(const E&e);

private:

int hSearch()const K%k;

int D;//定义桶数

E*ht;//

bool *empty;//

};

• 构造哈希函数
• 解决冲突

• 除余法

• 设 key值都为奇数，选 p 为偶数；则 H(key) = key MOD p，结果为奇数，一半单元被浪费掉。
• 设 key 值都为 5 的倍数，选 p 为 95；则 H(key) = key MOD p，结果为：0、 5、10、15、…… 90奇数。4/5 的单元被浪费掉。

• 移位折叠(Shift Folding)

• 边界折叠(Boundary Folding)

123

654

789

1566

• 提取法

• 基数转换法

• 开放定址法 (Vector)
• 拉链法 (List)

norm(h(K)+p(1)),h(K)+p(2),…… h(K)+p(i ),……)

• 令p(i)=i。故第次探查位置为 (h(K)+i) mod Tsize。

• 冲突：
• 初级冲突：不同关键字值的结点得到同一个散列地址。
• 二次聚集：同不同散列地址的结点争夺同一个单元。
• 结果：冲突加剧，最坏时可能达到 O（n）级代价。

• 改变步长：选和 m 互质的数作为步长，如 3、5、7……
• 如选步长为 5，用 H(key) = ( key+5) MOD 11
• H(key) = ( key+ 5×2) MOD 11
• H(key) = ( key+ 5×3) MOD 11 等进行下一个空的单元，直到找到为止。
• 随机地改变步长，如取步长序列：2，7，4，3，6，1，5
• 如用 H(key) = ( key+2) MOD 11
• H(key) = ( key+7) MOD 11
• H(key) = ( key+ 4) MOD 11 等进行探测下一个空的单元，直到找到为止。

• 选择为一个二次函数。经验公式是

p(i) = h(K)+(- 1) i-1 ( ( i + 1) /2)2

• 二次探查依然会有聚集块的形成，危害比一般线性探查小多了。

• 时间复杂度：设b为哈希表中桶的个数。哈希函数中D为除数且b = D。初始化表的时间为⊙(b)。当表中有n个元素时，最坏的插入和搜索时间均为⊙(b)。即所有n关键字值都在同一个桶里。此时和线性表在最坏的时候，复杂度一样。

Sn ~ ½[1+1/(1 - a) ]

a = n/b。

• 可以通过上述公式确定最大的a值。根据估计的n值。就可以得到的b最小值，然后选择一个比大的整数。(素数或没有小于20的素数因子)
• e.g 有近1000个元素的哈希表。要求成功搜索的次数小于4，不成功次数小于50.5。可以计算得a≦0.9且a ≦ 6/7，故a ≦ 6/7，所以b最小为7n/6 = 1167，所以

• e.g 当Tsize最多为530时，D和d最佳选择为23*23 = 529 。
20 ˆ

55

14

27

79 ˆ

68 ˆ

01

19

10

23 ˆ

11 ˆ

68 ˆ

0

1

2

3

4

5

6

7

8

9

10

11

12

ˆ

ˆ

ˆ

ˆ

ˆ

ˆ

ˆ

• 表中的每一个地址都只是这一个链接或列表，这种表永远不会出现溢出现象。新加入的关键字，仅仅是把它连接到表中的相应位置。
• 对于短链表，这是一个很快的方法。但是随着链接长度的增加，会明显的降低检索时的性能。需要对列表中的内容进行排序能够提高执行性能。
• 对于大多数情况下，不需要彻底查找就能得到结果
template
• class ChainHash Table{
• public:
• ChainHast Table(int divisor = 11 )
• {D = divisor;
• ht = new SortedChain[D];}
• ~ChainHast Table(){delete[]ht;}
• bool Search(const K&k,E&e ) const
• {return ht[k%D].Search(k,e);}
• ChainHash Table&Insert(const E&e)
• {ht[e%D].Distinclnsert(e);
• return *this;}
• ChainHash Table&Delete(const K&k , E&e)
• {ht[k%D].Deltet(k,e);
• return *this;}
• void Output()const; //输出哈希表
• private:
• int D; //位置数
• SortChain<>*ht; //链表数组

• 在拉链中使用线性探查的方法。未发生冲突的一个关键字找到第一个可用的地址，并且把这个地址的所以跟表中的一个关键字保存在一起。这种方式可以通过列表的链接直接访问到下一个元素，而避免了顺序查找。表中每个位置pos保存两个成员：关键字Info和指向下一个元素的指针Index。
• 这种方法减少了空间的使用，但是标的长度限制了表中储存关键字的个数。
Vector和List
• Sn = 1+ (n-1)/2b ~ 1+ a/2
• Un ~ a+1/2 ，a>>1
• 使用链表式的平均性能优于线性开址寻址。例如当a=0.9时，在链表中一次不成功搜索，平均要检查0.9个元素。一次成功搜索需要检查1.45个元素。对与线性开址寻址来说，不成功需要检查50.5个元素，成功时需要5.5个元素

• 时间
• 空间

• Cichelli算法。 用来散列数量相对较少的保留字。

h(word) = (length(word))+g(firstletter(word))+g(lastword(word)))modTsize

g就是要构造的函数。

• 算法分为三部分：首先计算字母的出现次数，然后对单词进行排序，最后搜索可能的最小理想哈希函数。最后一步是算法的核心。

“When all else fails,

try brute force.”

FHCD算法

Thomas Sager提出对Cichelli算法的改进。

h(word) = h0(word)+g(h1(word))+g(h2(word))

g就是要构造的函数

h0 = (T0(c1)+……+T0(cm))mod n

h1 = (T1(c1)+……+T1(cm))mod r

h2 = ((T2(c1)+……+T2(cm))mod r ) + r

• 计算单词频率
• 数据压缩

• 哈希表的定义
• 构造哈希函数
• 冲突解决方法
• 哈希表的优劣

http://acm.hit.edu.cn/forum

Thank You！

2005.11.20