datastructre_查找_顺序查找/折半查找(二分查找)/分块查找性能_二叉查找树_查找前驱节点
文章目录
顺序查找
一般线(无序)性表的顺序查找
-
对于含有n个元素的线性表,给定值key和表中的第i个元素相等(查找成功)的定位需要进行: n − i + 1 n-i+1 n−i+1次关键字比较
- C i = n − i + 1 C_i=n-i+1 Ci=n−i+1次
-
设每个元素的查找概率相等,
- P i = 1 n P_i=\frac{1}{n} Pi=n1
-
则 A S L s u c c e s s f u l = ∑ i = 1 n P i C i = 1 n ∑ i = 1 n ( n − i + 1 ) = 1 n ( ∑ i = 1 n ( n + 1 ) − ∑ i = 1 n i ) = 1 n ( n ( n + 1 ) − n ( n + 1 ) 2 ) = n + 1 2 则ASL_{successful}=\sum\limits_{i=1}^{n}P_iC_i \\=\frac{1}{n}\sum\limits_{i=1}^{n}(n-i+1) =\frac{1}{n}(\sum\limits_{i=1}^{n}(n+1)-\sum\limits_{i=1}^{n}i) \\=\frac{1}{n}(n(n+1)-\frac{n(n+1)}{2})=\frac{n+1}{2} 则ASLsuccessful=i=1∑nPiCi=n1i=1∑n(n−i+1)=n1(i=1∑n(n+1)−i=1∑ni)=n1(n(n+1)−2n(n+1))=2n+1
有序线性表的顺序查找
- 有序线性表和无序线性表的顺序查找的差别体现在查找失败是需要比较关键字的次数
- 查找成功的平均查找长度和无序顺序表是一致的( n + 1 2 \frac{n+1}{2} 2n+1)
- 假设有序查找表序列为 a i , i = 1 , 2 , ⋯ , n a_i,i=1,2,\cdots,n ai,i=1,2,⋯,n
- 那么查找失败的元素可能来自于(对应于)
区间
(
a
j
,
a
j
+
1
,
j
=
0
,
1
,
2
,
⋯
,
n
)
区间(a_{j},a_{j+1},j=0,1,2,\cdots,n)
区间(aj,aj+1,j=0,1,2,⋯,n)
- 其中 , 定义 a 0 = − ∞ ; a n + 1 = + ∞ 其中,定义a_0=-\infin;a_{n+1}=+\infin 其中,定义a0=−∞;an+1=+∞
- 那么共有n+1个区间
- 假设来自这n+1个区间的元素的概率一致, p i = 1 n + 1 p_i=\frac{1}{n+1} pi=n+11
- 第一个区间中的元素(记为
b
1
b_1
b1判断出查找失败需要比较的次数为1次
- 类似的定义第类个元素 b 2 b_2 b2判断出查找失败需要比较2次
- b n b_{n} bn需要比较n次
-
b
n
+
1
b_{n+1}
bn+1比较特殊,因为判断和最后一个元素比较完后,只要不等于
a
n
a_n
an那么就可以判断查找失败
- 没有 a n + 1 元素的存在 , 也就不用在比较第 n + 1 次了 没有a_{n+1}元素的存在,也就不用在比较第n+1次了 没有an+1元素的存在,也就不用在比较第n+1次了
- 所以有序线性表的顺序查找的失败凭据查找长度
- A S L f a i l e d = ∑ i = 1 n + 1 P i C i = 1 n + 1 ( ( ∑ i = 1 n i ) + n ) = n 2 + n n + 1 ASL_{failed}=\sum\limits_{i=1}^{n+1}P_iC_i=\frac{1}{n+1}((\sum\limits_{i=1}^{n}i)+n)=\frac{n}{2}+\frac{n}{n+1} ASLfailed=i=1∑n+1PiCi=n+11((i=1∑ni)+n)=2n+n+1n
平均查找长度
-
在实际应用的大多数情况下,查找成功的可能性比不成功的可能性大得多,
-
特别是在表中记录数n很大时,查找不成功的概率可以忽略不计。
-
当查找不成功的情形不能忽视时,查找算法的平均查找长度应是
- 查找成功时的平均查找长度与查找不成功时的平均查找长度之和。
-
对于顺序查找,不论给定值key为何值,查找不成功时和给定值进行比较的关键字个数均为1+n
-
假设查找成功与不成功的可能性相同,对每个记录的查找概率也相等,则
-
P = 1 2 n P=\frac{1}{2n} P=2n1,此时顺序查找的平均查找长度为
折半查找(二分查找)内含&性能分析
二分查找算法
## ol=list(range(10))#order list # import numpy as np # 你需要的不是ndarray,而是array # ol=2*np.array(ol)+3 ## print(ol) ## def binarySearch(ol,k): """ 二分查找(参数有序顺序表) """ l=0 r=len(ol)-1 while(l<=r): mid=(l+r)//2 print('indexes:[%d,%d],mid:%d' %(l,r, mid))#log if(ol[mid]==ol[k]): return mid if(k<mid): r=mid-1 if(mid<k): l=mid+1 return -1 print(binarySearch(ol, 7))
二叉排序树
-
左子树上的所有的结点的关键字均小于根结点的关键字
-
右子树上的所有结点的关键字均大于根结点的关键字
-
左子树和右子树根子又是一棵二叉排序树
-
如果给二叉排序树中的顶点按照从小到大编号,那么编号顺序和二叉树的中序遍历下的访问顺序是一样的
-
二叉排序树未必是一棵平衡树,反之才成立
-
在最坏的(最不平衡)的情况下,二叉排序树是一棵单枝树,性能和链表一样都是O(n)
-
最好情况下复杂度为 O ( log 2 n ) 最好情况下复杂度为O(\log_2n) 最好情况下复杂度为O(log2n)
判定树
- 有序表的顺序查找中,折半查找的过程可以用二叉判定树来描述
-
判定树是一棵平衡树
- 但不是所有的平衡树都可能称为判定树
-
也是一棵二叉排序序树
-
构建一个关键字比较序列有两种偏好:每个点都应该坚持同一种方式计算,否则不合法
- 折半中点
m
i
d
c
=
⌈
l
+
r
2
⌉
(
采用向下取整的偏好来取中点
)
mid_c=\lceil\frac{l+r}{2}\rceil(采用向下取整的偏好来取中点)
midc=⌈2l+r⌉(采用向下取整的偏好来取中点)
- 但是 m i d f = ⌊ l + r 2 ⌋ 也是允许的 但是mid_f=\lfloor\frac{l+r}{2}\rfloor也是允许的 但是midf=⌊2l+r⌋也是允许的
- 折半中点
m
i
d
c
=
⌈
l
+
r
2
⌉
(
采用向下取整的偏好来取中点
)
mid_c=\lceil\frac{l+r}{2}\rceil(采用向下取整的偏好来取中点)
midc=⌈2l+r⌉(采用向下取整的偏好来取中点)
-
判定树相关问题
-
对于给定一棵平衡树,如果我按照中序遍历的顺序编号遍历的的点
-
然后基于这些遍历的点推断各个节点的序号是采用 m i d c 方式还是 m i d f 方式 mid_c方式还是mid_f方式 midc方式还是midf方式
- 如果该平衡树的中序遍历序号(标记在节点旁)全部都是通过 m i d c 或者 m i d f 中的一种 , 那么它是判定树 mid_c或者mid_f中的一种,那么它是判定树 midc或者midf中的一种,那么它是判定树
- 否则如果混合出现两种偏好,那么就不是一棵判定树
-
判定树经常被用来结局给定序列(长度),求解关键字比较次数的问题/平均查找长度
判断给定的序列能否构成折半查找关键字比较序列
- 可以用给定的序列(元素值)尝试构建二叉排序树,如果无法构建,那么它就不是折半查找关键字比较序列
通用的计算折半查找的定义法
-
为了简单起见,先仅讨论比较整齐的满二叉树的情况
-
h 层的满二叉树有 2 h − 1 个结点 h层的满二叉树有2^h-1个结点 h层的满二叉树有2h−1个结点
-
因此我们假设有序表的长度为 n = 2 h − 1 ( 或者说 h = log 2 ( n + 1 ) ) 因此我们假设有序表的长度为n=2^h-1(或者说h=\log_2(n+1)) 因此我们假设有序表的长度为n=2h−1(或者说h=log2(n+1))
-
根结点是有序顺序表的中间元素
-
左子树(的根结点)<根结点<右子树(的根结点)
-
根据要查找的元素的不同,在所有查找成功的情况中
- 在第一层查找成功的元素只需要比较1次
- 在第二层查找成功则共需要比较2次(每层一次,共计2次)
- …
- 在第h层查找成功,则需要比较h次(每层1次,共计h次)
-
如果所有元素查找概率相等,那么第一层有一个元素,共有1*1次
-
第二层有 2 1 个元素 , 查找这类元素共需要比较 2 ∗ 2 次 第二层有2^1个元素,查找这类元素共需要比较2*2次 第二层有21个元素,查找这类元素共需要比较2∗2次
-
第三层有 2 2 个元素 , 这类元素需要比较 3 ∗ 2 2 次 第三层有2^2个元素,这类元素需要比较3*2^2次 第三层有22个元素,这类元素需要比较3∗22次
-
…
-
第 h 层有不超过 2 h − 1 个元素 ( 最深层未必是符合满二叉树的 ) , 这类元素需要比较 h × 2 h − 1 次 第h层有不超过2^{h-1}个元素(最深层未必是符合满二叉树的),这类元素需要比较h\times2^{h-1}次 第h层有不超过2h−1个元素(最深层未必是符合满二叉树的),这类元素需要比较h×2h−1次
-
又假设所有元的查找概率相同,(即,所有层的所有元素之间的被查找概率一致)
-
可以假设要对判定树每个元素构成的待查找序列进行分别查找
-
总的查找次数为: t = ∑ j = 1 h j × 2 j − 1 t=\sum\limits_{j=1}^{h}j\times 2^{j-1} t=j=1∑hj×2j−1
-
利用错位相减法,计算上面的求和式:
-
2 t = 2 ∑ j = 1 h j × 2 j − 1 = ∑ j = 1 h j × 2 j 2 t − t = ∑ j = 1 h j × 2 j − ∑ j = 2 h j × 2 j = ∑ j = 1 h j × ( 2 j − 2 j − 1 ) = ∑ j = 1 h j × ( 2 j − 1 ) 2t=2\sum\limits_{j=1}^{h}j\times 2^{j-1} =\sum\limits_{j=1}^{h}j\times 2^{j} \\2t-t=\sum\limits_{j=1}^{h}j\times 2^{j}-\sum\limits_{j=2}^{h}j\times 2^{j} \\=\sum\limits_{j=1}^{h}j\times (2^j-2^{j-1}) =\sum\limits_{j=1}^{h}j\times (2^{j-1}) 2t=2j=1∑hj×2j−1=j=1∑hj×2j2t−t=j=1∑hj×2j−j=2∑hj×2j=j=1∑hj×(2j−2j−1)=j=1∑hj×(2j−1)
-
-
-
满二叉判定树的查找成功的平均查找长度
-
A S L = ∑ i = 1 n P i C i = 1 n ∑ j = 1 h j × 2 j − 1 ( 其中 h = log 2 ( n + 1 ) ) = n + 1 n log 2 ( n + 1 ) − 1 ASL=\sum\limits_{i=1}^{n}P_iC_i =\frac{1}{n}\sum\limits_{j=1}^{h}j\times 2^{j-1}(其中h=\log_2(n+1)) \\=\frac{n+1}{n}\log_2(n+1)-1 ASL=i=1∑nPiCi=n1j=1∑hj×2j−1(其中h=log2(n+1))=nn+1log2(n+1)−1
求和式的一些性质
-
联系求和式 ∑ i = p n \sum\limits_{i=p}^{n} i=p∑n以及它的展开 a p + a p + 1 + ⋯ + a n a_p+a_{p+1}+\cdots+a_n ap+ap+1+⋯+an是求和号的一些性质的源泉
-
∑ i = p n f ( i ) = ∑ i = n + q n + q f ( i − q ) = f ( p ) + f ( p + 1 ) + ⋯ + f ( n ) ( 或者 : ∑ i = p n f ( i ) = ∑ i = p − q n − q f ( i + q ) ) \sum\limits_{i=p}^{n}f(i)=\sum\limits_{i=n+q}^{n+q}f(i-q)=f(p)+f(p+1)+\cdots+f(n) \\(或者:\sum\limits_{i=p}^{n}f(i)=\sum\limits_{i=p-q}^{n-q}f(i+q)) i=p∑nf(i)=i=n+q∑n+qf(i−q)=f(p)+f(p+1)+⋯+f(n)(或者:i=p∑nf(i)=i=p−q∑n−qf(i+q))
求和号错位相减中的应用
特别的 , t = ∑ j = 1 h j ⋅ 2 j − 1 = ∑ j = 1 − 1 = 0 h − 1 ( j + 1 ) ⋅ 2 j − 1 + 1 = ∑ j = 0 h − 1 ( j + 1 ) ⋅ 2 j = ∑ j = 0 h − 1 j ⋅ 2 j + ∑ j = 0 h − 1 1 ⋅ 2 j = 0 + ∑ j = 1 h − 1 j ⋅ 2 j + 1 ( 1 − 2 h ) 1 − 2 / / a 1 = 0 , 前 n 项的 q = 2 的等比数列求和 = ( ∑ j = 1 h − 1 j ⋅ 2 j ) + ( 2 h − 1 ) 2 t = ∑ j = 1 h j × 2 j = ( ∑ j = 1 h − 1 j ⋅ 2 j ) + h ⋅ 2 h 观察到 t 被分为两部分 , 2 t − t = h ⋅ 2 h − 2 h + 1 = ( h − 1 ) 2 h + 1 特别的,t=\sum\limits_{j=1}^{h}j\cdot 2^{j-1} =\sum\limits_{j=1-1=0}^{h-1}(j+1)\cdot 2^{j-1+1} =\sum\limits_{j=0}^{h-1}(j+1)\cdot 2^{j} \\=\sum\limits_{j=0}^{h-1}j\cdot 2^{j}+\sum\limits_{j=0}^{h-1}1\cdot 2^{j} \\=0+\sum\limits_{j=1}^{h-1}j\cdot2^j+\frac{1(1-2^h)}{1-2}//a1=0,前n项的q=2的等比数列求和 \\=(\sum\limits_{j=1}^{h-1}j\cdot2^j)+(2^h-1) \\2t=\sum\limits_{j=1}^{h}j\times 2^{j}=(\sum\limits_{j=1}^{h-1}j\cdot 2^j)+h\cdot 2^h \\ 观察到t被分为两部分,2t-t=h\cdot2^h-2^h+1=(h-1)2^h+1 特别的,t=j=1∑hj⋅2j−1=j=1−1=0∑h−1(j+1)⋅2j−1+1=j=0∑h−1(j+1)⋅2j=j=0∑h−1j⋅2j+j=0∑h−11⋅2j=0+j=1∑h−1j⋅2j+1−21(1−2h)//a1=0,前n项的q=2的等比数列求和=(j=1∑h−1j⋅2j)+(2h−1)2t=j=1∑hj×2j=(j=1∑h−1j⋅2j)+h⋅2h观察到t被分为两部分,2t−t=h⋅2h−2h+1=(h−1)2h+1
A S L f u l l ASL_{full} ASLfull折半查找的成功查找的平均长度
本例中 h = log 2 ( n + 1 ) t = ( log 2 ( n + 1 ) − 1 ) ( n + 1 ) + 1 = ( n + 1 ) log 2 ( n + 1 ) − n − 1 + 1 = ( n + 1 ) log 2 ( n + 1 ) − n 本例中h=\log_2(n+1) \\t=(\log_2(n+1)-1)(n+1)+1=(n+1)\log_2{(n+1)}-n-1+1 \\=(n+1)\log_2{(n+1)}-n 本例中h=log2(n+1)t=(log2(n+1)−1)(n+1)+1=(n+1)log2(n+1)−n−1+1=(n+1)log2(n+1)−n
∴ A S L f u l l = 1 n ( ( n + 1 ) log 2 ( n + 1 ) − n ) = 1 n ( n + 1 ) log 2 ( n + 1 ) − 1 \therefore ASL_{full}=\frac{1}{n}((n+1)\log_2{(n+1)}-n)=\frac{1}{n}(n+1)\log_2{(n+1)}-1 ∴ASLfull=n1((n+1)log2(n+1)−n)=n1(n+1)log2(n+1)−1
-
当n比较大的时候, n + 1 n ≈ 1 \frac{n+1}{n}\approx1 nn+1≈1
-
则 A S L ≈ log 2 ( n + 1 ) − 1 ASL\approx\log_2(n+1)-1 ASL≈log2(n+1)−1
-
对于有n个结点判定树(高度可以类似完全二叉树的方法计算,都是 ⌈ log 2 ( n + 1 ) ⌉ \lceil\log_2(n+1)\rceil ⌈log2(n+1)⌉)
-
折半查找的时间复杂度为 O ( log 2 n ) 折半查找的时间复杂度为O(\log_2n) 折半查找的时间复杂度为O(log2n)
-
上面的 A S L s s ASL_{ss} ASLss并不是通用的(只有当判定树为满二叉树才可以用)
- 一般还是按照定义,来求解ASL
- 一般的判定树除了最后一层不一定满,其余层都是满的
- 而且是平均情况
极端情况
-
如果是规定了查找表长度,往往要计算最坏情况
-
最多比较 m a x = ⌈ log 2 ( n + 1 ) ⌉ 最多比较max=\lceil\log_2(n+1)\rceil 最多比较max=⌈log2(n+1)⌉就能够判定查找失败或者成功(结合一般的判定树高度)
-
最好情况则是 m a x − 1 max-1 max−1次比较
- 例如n=16时,$判断出一个元素查找失败至少要37/12;最多要:49/13
分块查找
- 结合了顺序查找和折半查找的优点
- 分块查找,块内无序
- 块之间有序
- 而且第一个块的所有元素小于第二个块的所有元素
-
更一般的
m
a
x
(
B
l
o
c
k
i
)
⩽
m
i
n
(
B
l
o
c
k
i
+
1
)
更一般的max(Block_i)\leqslant min (Block_{i+1})
更一般的max(Blocki)⩽min(Blocki+1)
- i , i + 1 表示前后相邻的两序列块的下标 i,i+1表示前后相邻的两序列块的下标 i,i+1表示前后相邻的两序列块的下标
- 每块内最大元素或者最小元素的数据组成了索引块
索引表
- 索引表中的每个元素各含有各块的最大(或最小)关键字和各块中的第一个元素地址
- 索引表按照关键字有序排列
查找过程
- 确定索引表中待查找的记录所在的索引块
- 这一部分可采用折半查找或者顺序查找
- 在块内进一步查找
- 一般块内是无序的,则采用顺序查找
- 但如果是有序表的强化分块,那么还可以选用折半查找
性能分析
- 分块查找的平均查找长度为:
- 索引查找和块内查找的平均查找长度之和
- 设索引查找和块内查找的平均查找长度分别为 L I , L B L_I,L_B LI,LB
- 其他极端策略结合折中案例
- 在计算机组成原理中,cache和内存的映射策略中,直接映射和全相联映射折中出了组相联映射
索引表采用顺序查找的性能
-
查找长度均匀的分为
b
=
n
s
块
(
每块长度为
s
)
查找长度均匀的分为b=\frac{n}{s}块(每块长度为s)
查找长度均匀的分为b=sn块(每块长度为s)
- A S L = L I + L B = b + 1 2 + s + 1 2 = 1 2 ( n s + s ) + 1 ASL=L_I+L_B=\frac{b+1}{2}+\frac{s+1}{2}=\frac{1}{2}(\frac{n}{s}+s)+1 ASL=LI+LB=2b+1+2s+1=21(sn+s)+1
- 有基本不等式可得,
n
s
+
s
在
n
s
=
s
时取得最小值
2
n
\frac{n}{s}+s在\frac{n}{s}=s时取得最小值2\sqrt{n}
sn+s在sn=s时取得最小值2n
- 即 s = n 时 , 性能最好 即s=\sqrt{n}时,性能最好 即s=n时,性能最好
- 所以 A S L m i n = n + 1 所以ASL_{min}=\sqrt{n}+1 所以ASLmin=n+1
索引表采用折半查找的性能
- A S L = L I + L S = ⌈ log 2 ( b + 1 ) ⌉ + s + 1 2 ASL=L_I+L_S=\lceil\log_2(b+1)\rceil+\frac{s+1}{2} ASL=LI+LS=⌈log2(b+1)⌉+2s+1
再提高有序顺序表的查找速度
- 如果能够结合索引和折半查找,有序顺序表的查找速度可以比单纯的折半查找更加快
- 假设有序顺序查找表元素有
n
=
65025
=
25
5
2
=
(
2
8
−
1
)
2
n=65025=255^2=(2^8-1)^2
n=65025=2552=(28−1)2
- 根据前面的分析我们为每个索引块取大小 s = 65025 = 255 s=\sqrt{65025}=255 s=65025=255
- 此时共有恰好255项索引项在索引表中
二叉查找树
brief description:
- if the node x to be find successor has its right subtree,the find the right subtree’s min
- else(the node x do not has it’s right subtree(empty),then we simply go up the tree from x unitl we encounter a node that is the left child of its parent
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」