背景
在有序表的查找中,折半查找假设每个元素被查找的概率是相同的。现实情况下,文本中的字符被查找的概率是不相同的,为了使算法总体查找的平均长度最优化。一般会考虑构建最优查找树来处理。
最优静态查找树
P H = ∑ i = 1 n c p i PH = \sum_{i=1}^{n}cp_iPH=i=1∑ncpi
注
1 c cc是常量
2 p i p_ipi是关键字的被查找概率
所得PH值最小的二叉树就是最优查找树
注
缺点:时间复杂度O ( n 3 ) O(n^3)O(n3)
次优查找树
为了规避最优查找树构建的高时间复杂度,一般使用次优查找树来替代,性能不低于最优查找树的3%。
核心:选出一个结点,使得它左右两侧的子数组的权值累加和之差的绝对值最小。
有序序列
r l . k e y < r l + 1 . k e y < . . . < r h . k e y r_l.key<r_{l+1}.key<...<r_h.keyrl.key<rl+1.key<...<rh.key
△ P i = ∣ ∑ j = i + 1 h w j − ∑ j = l i − 1 w j ∣ \triangle{P_i} = |\sum_{j=i+1}^{h}w_j-\sum_{j=l}^{i-1}w_j|△Pi=∣j=i+1∑hwj−j=l∑i−1wj∣
为便于计算引入累计权值和
s w i = ∑ j = l i w j sw_i = \sum_{j=l}^{i}w_jswi=j=l∑iwj
并设w l − 1 = 0 w_{l-1}=0wl−1=0,s w l − 1 = 0 sw_{l-1}=0swl−1=0
{ s w i − 1 − s w l − 1 = ∑ j = l i − 1 w j s w h − s w i + 1 = ∑ j = i + 1 h w j \begin{cases} sw_{i-1}-sw_{l-1} = \sum_{j=l}^{i-1}w_j \\ sw_{h}-sw_{i+1} = \sum_{j=i+1}^{h}w_j \end{cases}{swi−1−swl−1=∑j=li−1wjswh−swi+1=∑j=i+1hwj
右子树-左子树的累计权值差
△ P i = ∣ ( s w h − s w i ) − ( s w i − 1 − s w l − 1 ) ∣ = ∣ s w h + s w l − 1 − s w i − s w i − 1 ∣ \triangle{P_i} = |(sw_h-sw_{i})-(sw_{i-1}-sw_{l-1})| = |sw_h+sw_{l-1}-sw_{i}-sw_{i-1}|△Pi=∣(swh−swi)−(swi−1−swl−1)∣=∣swh+swl−1−swi−swi−1∣
具体实现如下
package main
import (
"fmt"
"math"
)
//次优查找树
//O(nlog(n))
//BTreeNode is node
type BTreeNode struct {
data string
lchild, rchild *BTreeNode
}
//Element is list data type
type Element struct {
data string
weight float64
}
func main() {
bt := BTreeNode{}
list := []Element{
{data: "A", weight: 1},
{data: "B", weight: 1},
{data: "C", weight: 2},
{data: "D", weight: 5},
{data: "E", weight: 3},
{data: "F", weight: 4},
{data: "G", weight: 4},
{data: "H", weight: 3},
{data: "I", weight: 5},
}
sw := []float64{0}
for i := 0; i < len(list); i++ {
sw = append(sw, list[i].weight+sw[i])
}
low := 1
high := len(list)
SecondOptimal(&bt, list, sw, low, high)
fmt.Println(bt)
}
//SecondOptimal 次优查找树生成 low下标从1开始
//sw[0]=0
func SecondOptimal(BT *BTreeNode, list []Element, sw []float64, low int, high int) {
i := low
//初始化右子树和左子树的权值差的最小值
min := sw[high] - sw[low-1]
//计算P的增量 dw-sw[i]-sw[i-1]
dw := sw[high] + sw[low-1]
for j := low + 1; j <= high; j++ {
if math.Abs(dw-sw[j]-sw[j-1]) < min {
i = j
min = math.Abs(dw - sw[j] - sw[j-1])
}
}
BT.data = list[i-1].data
if i == low {
BT.lchild = nil
} else {
//构造左子树
if BT.lchild == nil {
BT.lchild = &BTreeNode{}
}
SecondOptimal(BT.lchild, list, sw, low, i-1)
}
if i == high {
BT.rchild = nil
} else {
if BT.rchild == nil {
BT.rchild = &BTreeNode{}
}
SecondOptimal(BT.rchild, list, sw, i+1, high)
}
}