经过n久的沉寂,偶又回来了!
第七日:基本数据结构(二)
1、哈希表
也叫散列表
很多时候,应用程序需要这样一种结构
仅需要三个基本操作:
插入、查找、删除
哈希表就可以提供这样的功能组合
哈希表是一种在线性表基础上实现的数据结构
一般而言,可以把数组理解为一个不带操作线性表
为了便于理解,以数组的概念代替线性表
假定每个元素(数据项)都有一个域称为关键域
通过某种方法,如果可以在数组的下标和关键域值(简称关键值)之间建立某种映射关系(如果高中数学没忘光的话,应该记得这个概念)
那么这个数组就可以叫做一个哈希表
建立下标和关键值之间映射的方法,称为哈希函数,或者散列函数
在这里,散列的意思可以理解为,将集合中的元素,分散到了数组之中
哈希表是实现字典的一个很有效的手段
想到了点儿什么没有?bingle!拼音检字法就是一种有效的哈希方案
声母和韵母作为关键值,页码是数组下标,拼音检字表就是一个哈希表
不过想要检索到一个字,要计算两次哈希函数
第一次检索到声母,第二次检索到拼音对应的页码
偏旁部首检字也是类似的
最简单的哈希表是直接寻址表,下标等于关键值
哈希函数是T[k]=k,也就是y=x
插入、删除、查找操作都很好实现,就不一一列出了
而且这些操作都很快,o(1)时间就可以实现
但是这种表的缺陷也很明显,如果集合内关键值的取值范围很大,就要有一个相应大小的数组与之对应才行
这个问题也很好解决,想想字典是怎么做的
建立哈希函数的时候,使定义域大于值域,也就是缩小结果的取值范围
简单的讲就是从关键值中提取共性,比如拼音中的声母,比如部首检字法中的偏旁
作用都是一样的
这样就可以使不同的关键值被映射到哈希表中的同一个位置
这样又引出了两个问题:
1、数组的一个下标的位置只能存一个值,如何处理映射过来的多个值?
2、如何使对应到每个下标的元素个数不要相差太大
第一个问题很好解决,可以在数组中存放一个指向链表的指针,具体元素存放在链表中
第二个问题就很不好回答了,如果做不到这一点,一个哈希函数就必定不是一个好的哈希函数
2、二叉查找树(二叉搜索树)
二叉查找树首先是一颗二叉树,它仅比二叉树多了以下特性
假设x是二叉查找树上的一个节点
如果y是x的左子树上的一个节点,那么key[y]<=key[x]
如果y是x的右子树上的一个节点,那么key[y]>=key[x]
从这种意义上讲,一个升序排列的链表可以看作是一个所有左子为nil的二叉排序树
定义在二叉搜索树上的基本操作:
查找:
TREE-SEARCH(x,k)
1 if x = NIL or k= key[x]
2 then return x
3 if k<key[x]
4 then return TREE-SEARCH(left[x],k)
5 else return TREE-SEARCH(right[x],k)
非递归的查找算法:
ITERATIVE-TREE-SEARCH(x,k)
1 while x <> NIL and k <> key[x]
2 do if k < key[x]
3 then x <- left[x]
4 else x <- right[x]
5 return x
最小和最大
TREE-MINIMUM(x)
1 while left[x] <> nil
2 do x <- left[x]
3 return x
TREE-MAXIMUM(x)
1 while right[x] <> nil
2 do x <- right[x]
3 return x
后继与前驱
TREE-SUCCESSOR(x)
1 if right[x] <> NIL
2 then return TREE-MINIMUM(right[x])
3 y <- p[x]
4 while y <> NIL and x = right[y] //父亲节点是左孩子的直接后继
5 do x <- y
6 y <- p[y]
7 return y
求前驱把以上过程中左右反过来就可以了
插入与删除
插入比较简单,找到地方,插进去,嗯
TREE-INSERT(T,z)
1 y <- NIL
2 x <- root[T]
3 while x <> NIL
4 do y <- x
5 if key[z] < key [x]
6 then x <- left[x]
7 else x <- right[x]
8 p[z] <- y
9 if y = NIL
10 then root[T] <- z //树T空的时候
11 else if key[z] < key[y]
12 then left[y] <- z
13 else right[y] <- z
删除就比较麻烦了,自己画个图可能会比较好理解
分以下几种情况:
如果z的左子树或者右子树为空,直接以左子树或右子树替换z
都不空的情况下
如果z的直接后继没有右子树(肯定没有左子树),用后继替换z
如果后继有右子树,先用右子树替换后继,在用后继替换z
不知道有没有人能看懂我说了些什么
TREE-DELETE(T,z)
1 if left[z] = NIL or right[z] = NIL
2 then y <- z
3 else y <- TREE-SUCCESSOR(z)
4 if left[y] <> NIL
5 then x <- left[y]
6 else x <- right[y]
7 if x <> NIL
8 then p[x] <- p[y]
9 if p[y] = NIL
10 then root[T] <- x
11 else if y = left[p[y]]
12 then left[p[y]] <- x
13 else right[p[y]] <- x
14 if y <> z
15 then key[z] <- key[y]
16 把y的数据copy到z
17 return y
就到这里,休息!
|