次优查找树的建立

  查找效率最高即平均查找长度最小,根据前面所学知识,我们可以给出有序表在非等概率情况下应遵循的两个原则:   

  1、最先访问的结点应是访问概率最大的结点; 

  2、每次访问应使结点两边尚未访问的结点的被访概率之和尽可能相等。

  这两个原则可用一句话来表示,即判定树为带权内路径长度之和最小的二叉树,亦即:PH = ∑wihi  最小,其中 n 为有序表长度,hi 为第 i 个结点在判定树上的层次数,wi = cpi,c 为某个常数,pi 为第 i 个结点的查找概率。

        这样的树称为静态最优查找树(static optimal search tree),构造这样一棵树的时间代价太大,亦即时间复杂度很大,因此我们通常是构造次优查找树(nearly optimal search tree),构造它的时间代价远远低于构造最优查找树,但查找性能只比最优查找树差1%~2%,很少差3%以上。

次优查找树的构造: 
  
        设有序表每个记录的权值为 wl,wl+1,…,wh,第一个应访问的结点号为 i ,则有: 
Δpi =   ∑wj - ∑wj   最小,即 Δpi = Min {Δpj } 
再分别对 {rl,rl+1,…,ri-1} 和 {ri+1,ri+2,…,rh} 分别构造次优查找树。 
  
为便于计算,引入累计权值swi=∑wj,并设wl-1=swl-1=0,则:

查找构造静态次优查找树(SOST) - 蔷薇阁 - 落落工作室

    
        由于在构造次优查找树时没有考虑前面说的原则一,因此被选为根的结点的权值可能比其邻近结点的权值小,此时应对查找树作适当的调整,将相邻权值大的结点作为根结点。

          次优查找树的查找方法与折半查找类似,其平均查找长度与 log n 成正比。

注意:利用上述算法构造好次优二叉树之后,可能并不是最优的,因为在构造过程中,没有考虑单个关键字的相应权值,则有可能出现被选为根的关键字的权值比与

    它相邻的关键字的权值小。此时应做适当的调整:选取邻近的权值较大的关键字作为次优查找树的根节点(也就是左旋和右旋子树

#include<iostream>
#include
<cstring> #include<cstdio> #include<algorithm> #include<string> #include<cmath> #define N 100 #define MAXN 0x3f3f3f3f using namespace std;template<typename T> class TreeNode{public:TreeNode* child[2];T val;int w; TreeNode(){child[0] = child[1] = NULL;} };template<typename T> class NearlyOptimalSearchTree{//次优查找树 public:int n;T val[N];int w[N];int sw[N];TreeNode<T> *t;void input();void init();void outT(TreeNode<T>* t);private:void buildT(int ld, int rd, TreeNode<T>* &t);//建立次优查找树 void adjustment(TreeNode<T>* &t);//调整次优查找树 void rotateT(TreeNode<T>* &t, int x); };template<typename T> void NearlyOptimalSearchTree<T>::input(){cin>>n;for(int i=1; i<=n; ++i)cin>>val[i]>>w[i]; }template<typename T> void NearlyOptimalSearchTree<T>::init(){sw[0] = 0;for(int i=1; i<=n; ++i) sw[i] = sw[i-1]+w[i];buildT(1, n, t);cout<<"没有调整前的先序遍历:"<<endl;outT(t);adjustment(t);cout<<endl<<"调整后的先序遍历:"<<endl;outT(t);cout<<endl; }template<typename T> void NearlyOptimalSearchTree<T>::buildT(int ld, int rd, TreeNode<T>* &t){if(ld > rd) return;int minN = MAXN;int i;for(int j=ld; j<=rd; ++j){int ans = sw[rd] - sw[j-1] - sw[j]; ans = abs(ans);if(minN > ans){minN = ans;i = j;}}t = new TreeNode<T>;t->val = val[i];t->w = w[i];if(ld==rd) return;buildT(ld, i-1, t->child[0]);buildT(i+1, rd, t->child[1]); }template<typename T> void NearlyOptimalSearchTree<T>::adjustment(TreeNode<T>* &t){if(!t) return;int lmax = 0, rmax = 0;if(t->child[0]) lmax = t->child[0]->w;if(t->child[1]) rmax = t->child[1]->w;int maxVal = max(lmax, rmax);if(t->w < maxVal){if(maxVal == lmax){rotateT(t, 1);//右旋子树 } else {rotateT(t, 0);//左旋子树 } }adjustment(t->child[0]);adjustment(t->child[1]); }template<typename T> void NearlyOptimalSearchTree<T>::rotateT(TreeNode<T>* &o, int x){TreeNode<T>* k = o->child[x^1];o->child[x^1] = k->child[x];k->child[x] = o;o = k; }template<typename T> void NearlyOptimalSearchTree<T>::outT(TreeNode<T>* t){if(!t) return;cout<<t->val<<" ";outT(t->child[0]);outT(t->child[1]); }int main(){NearlyOptimalSearchTree<string> nost;nost.input();nost.init();return 0; }

/*
  演示结果如下:

9
A 1
B 1
C 2
D 5
E 3
F 4
G 4
H 3
I 5
没有调整前的先序遍历:
F D B A C E G H I
调整后的先序遍历:
D C B A F E G I H

 

5
A 1
B 30
C 2
D 29
E 2
没有调整前的先序遍历:
C B A D E
调整后的先序遍历:
B A D C E


*/

 

#include<iostream> 
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string> 
#include<iomanip>
#include<cmath>
#include<queue>
#define N 100
#define MAXN 0x3f3f3f3f
using namespace std;template<typename T>
class TreeNode{public:TreeNode* child[2];T val;int w; int d;//距离屏幕左端的宽度 
        TreeNode(){child[0] = child[1] = NULL;}
};template<typename T>
class NearlyOptimalSearchTree{//次优查找树 public:int n;T val[N];int w[N];int sw[N];TreeNode<T> *t;void input();void init();void outT(TreeNode<T>* t);private:int width;int step;void buildT(int ld, int rd, TreeNode<T>* &t);//建立次优查找树 void adjustment(TreeNode<T>* &t);//调整次优查找树 void rotateT(TreeNode<T>* &t, int x);void widthT(TreeNode<T>* t);//计算每个节点到屏幕左端的距离 
};template<typename T>
void NearlyOptimalSearchTree<T>::input(){cin>>n;for(int i=1; i<=n; ++i)cin>>val[i]>>w[i];
}template<typename T>
void NearlyOptimalSearchTree<T>::init(){sw[0] = 0;width = 0;step = 2;for(int i=1; i<=n; ++i)    sw[i] = sw[i-1]+w[i];buildT(1, n, t);cout<<"没有调整前的先序遍历:"<<endl;outT(t);adjustment(t);cout<<endl<<"调整后的先序遍历:"<<endl;outT(t);cout<<endl;
}template<typename T>
void NearlyOptimalSearchTree<T>::buildT(int ld, int rd, TreeNode<T>* &t){if(ld > rd) return;int minN = MAXN;int i;for(int j=ld; j<=rd; ++j){int ans = sw[rd] - sw[j-1] - sw[j];    ans = abs(ans);if(minN > ans){minN = ans;i = j;}}t = new TreeNode<T>;t->val = val[i];t->w = w[i];if(ld==rd) return;buildT(ld, i-1, t->child[0]);buildT(i+1, rd, t->child[1]);
}template<typename T>
void NearlyOptimalSearchTree<T>::adjustment(TreeNode<T>* &t){if(!t) return;int lmax = 0, rmax = 0;if(t->child[0]) lmax = t->child[0]->w;if(t->child[1]) rmax = t->child[1]->w;int maxVal = max(lmax, rmax);if(t->w < maxVal){if(maxVal == lmax){rotateT(t, 1);//右旋子树 } else {rotateT(t, 0);//左旋子树 
        } }adjustment(t->child[0]);adjustment(t->child[1]);
}template<typename T>
void NearlyOptimalSearchTree<T>::rotateT(TreeNode<T>* &o, int x){TreeNode<T>* k = o->child[x^1];o->child[x^1] = k->child[x];k->child[x] = o;o = k; 
}template<typename T>
void NearlyOptimalSearchTree<T>::widthT(TreeNode<T>* t){if(!t) return;widthT(t->child[0]);t->d = width;width+=step; widthT(t->child[1]);
}template<typename T>
void NearlyOptimalSearchTree<T>::outT(TreeNode<T>* t){width=0;widthT(t);queue<TreeNode<T>*> q, qq;q.push(t);int n=1;//当前层的节点个数 int i=1;//当前层第几个节点 int nn=0;//统计下一次的节点的个数int pred;//前一个节点距离左屏幕的距离 while(!q.empty()){TreeNode<T>* tt = q.front();q.pop();qq.push(tt);if(tt != t){//不是根节点, 打印分枝竖线 if(i==1){printf("%*s", tt->d, "|");pred = tt->d;} else {printf("%*s", tt->d-pred, "|");pred = tt->d;}}//放入孩子节点 if(tt->child[0]) q.push(tt->child[0]), ++nn;if(tt->child[1]) q.push(tt->child[1]), ++nn;++i; if(i>n){//上一层访问完毕 i=1;n = nn;nn = 0;printf("\n");bool first = true;//是否是这一行的第一个节点 int ld, rd; while(!qq.empty()){//打印上层节点字符 TreeNode<T>* tt = qq.front();qq.pop();if(first){cout<<setw(tt->d)<<tt->val;pred = tt->d;ld = tt->d;if(tt->child[0])ld = tt->child[0]->d; } else {cout<<setw(tt->d - pred)<<tt->val;pred = tt->d;}first = false;if(qq.empty()){//这一层的最后一个节点 rd = tt->d+1;if(tt->child[1])rd = tt->child[1]->d;}}printf("\n");if(q.empty()) break;//这是最后一层 cout<<setw(ld-1)<<"";for(int i=ld; i<=rd; ++i)printf("-") ;printf("\n");}}
}int main(){NearlyOptimalSearchTree<string> nost;nost.input();nost.init();return 0;
}

/*
  //演示结果

9
A 1
B 1
C 2
D 5
E 3
F 4
G 4
H 3
I 5
没有调整前的先序遍历:

 

F
-------
| |
D G
-------------
| | |
B E H
-----------------
| | |
A C I

 

调整后的先序遍历:

 

D
-------
| |
C F
-----------
| | |
B E G
-----------------
| |
A I
------------------
|
H


*/

 

转载于:https://www.cnblogs.com/hujunzheng/p/4657858.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531458.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

平衡二叉树AVL插入

平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的&#xff0c;所以又称为AVL树。 定义&#xff1a;平衡二叉树或为空树,或为如下性质的二叉排序树: &#xff08;1&#xff09;左右子树深度之差的绝对值不超过1; &…

(五)C语言之二维数组

今天的第二个内容单独拿出来讲一下&#xff0c;对于初接触C语言的人来说&#xff0c;这个知识点比较难懂&#xff0c;后面在讲指针的时候我还会提到这部分的内容&#xff0c;看不懂的同学可以看后面的内容。 指针变量可以指向一维数组中的元素&#xff0c;当然也就可以指向二维…

平衡二叉树AVL删除

平衡二叉树的插入过程: http://www.cnblogs.com/hujunzheng/p/4665451.html 对于二叉平衡树的删除采用的是二叉排序树删除的思路: 假设被删结点是*p&#xff0c;其双亲是*f&#xff0c;不失一般性&#xff0c;设*p是*f的左孩子&#xff0c;下面分三种情况讨论&#xff1a;  ⑴…

(六)C语言之函数

本篇文章分为三个部分讲解&#xff0c;分别为函数、局部变量和全局变量、c语言存储分区 &#xff08;一&#xff09;函数的定义和调用 函数&#xff1a;工程中最小的单位&#xff0c;为了实现某一功能的 函数的定义&#xff1a; 数据类型 函数名(数据类型 形参1&#xff0c;…

堆排序算法---属于选择排序

1.堆 堆实际上是一棵完全二叉树&#xff0c;其任何一非叶节点满足性质&#xff1a; Key[i]<key[2i1]&&Key[i]<key[2i2]或者Key[i]>Key[2i1]&&key>key[2i2] 即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。 堆分为大顶堆和小顶堆…

(七)C语言之指针

c语言相比其他高级语言来说&#xff0c;更接近于对计算机硬件的操作&#xff0c;而指针的应用更是为我们对硬件的操作插上了翅膀&#xff0c;所以指针是嵌入式编程不可少的一部分&#xff0c;在一定意义上说&#xff0c;指针是c语言的精髓。 一、 什么是指针 在计算机中&#…

(八)C语言之结构

今天来说一下C语言里的结构体(struct)、共用体(l联合体)union、枚举。 &#xff08;一&#xff09;结构体&#xff1a;struct 1.1 概念 是一种自定义的数据类型结构体是构造类型的一种不同数据类型的集合地址空间连续&#xff0c;每次分配最大数据类型的宽度占用内存为所有变…

插入排序之表插入排序

1.表插入排序只是求得一个有序的链表&#xff0c;它是修改指针的值来代替移动记录&#xff0c;操作过程如下 2.但是这样只能进行顺序查找&#xff0c;不能进行随机查找&#xff0c;为了能实现有序表的折半查找&#xff0c;需要对记录进行重新排列。操作过程如下&#xff1a; 3.…

电容降压LED驱动电路

电容降压电路具有体积小、成本低、电流相对稳定等优点&#xff0c;可应用于小功率的LED驱动电路中&#xff0c;本文主要介绍了电容降压电路的基本电路 图一&#xff1a; 电容降压式简易电源的基本原理如图一所示&#xff0c;C3为降压电容器&#xff1b;D4为半波整流二极管&…

延时电路分析

延时电路经常会用到&#xff0c;RC电路是比较简单的电路。在电路设计中经常会用到将电阻和电容正极连接&#xff0c;电阻另一端接上电源&#xff0c;电容负极接地。 简单的延时电路 上面就是延时的电路图了&#xff0c;延时的时间为T-ln((VCC-Vout)/VCC)RC&#xff0c;公式中的…

恒流电路的分析(一)

在这里分析一个简单的LED恒流电路&#xff0c;软件采用Multisim进行波形采集 一、元器件 R1为80KΩ左右的金属膜电阻&#xff1b;Q选取耐压值超过350V的VPN三极管&#xff1b;D选取2V左右的稳压二极管(如1N4680)&#xff1b;C2选取10V、100UF以上的电解电容&#xff1b;R2选择…

ST-LINK USB communication error解决方法

今天在用stlink-v2下载程序时出现ST-LINK USB communication error&#xff0c;突然就出现了这个问题&#xff0c;在网上找了好多解决办法都不可以用&#xff0c;下面给出我的解决方案&#xff0c;文章末尾给出了网上的几种解决办法&#xff0c;仅供参考。 第一步&#xff1a;找…

利用STM32制作红外测温仪之硬件设计

最近受疫情的影响详细大家都在家里没事干&#xff0c;这里利用stm32最小系统做一个红外测温仪 这篇教程里我们来制作红外测温仪需要用到的硬件&#xff0c;关于PCB的工程文件&#xff0c;后文会给出。 &#xff08;一&#xff09;系统分析 由于我们的功能比较单一&#xff0c;…

如何在博客中插入背景音乐

1.首先进入网音乐官方网站&#xff1b; 2.查找自己喜欢的歌&#xff0c;看到如下界面 3.点击"生成外链播放器" 4.看到下面的html代码了吗&#xff1f;将代码进行复制。 5.进入博客园&#xff0c;点击 "管理" ->"设置"&#xff0c; 将代码复制…

常用存储器介绍

注意&#xff1a;"易失/非易失"是指存储器断电后&#xff0c;它存储的数据内容是否会丢失的特性。 &#xff08;一&#xff09;RAM和ROM 1.1 RAM RAM即随机存储器&#xff0c;它是指存储器中的数据被读入或者写入与信息所在位置无关&#xff0c;时间都是相同的 1…

TortoiseGit与github实现项目的上传

1. 下载并安装相关软件 这里主要涉及的软件包括msysgit和TortoiseGit。 msysgit的下载地址&#xff1a;http://msysgit.googlecode.com/files/Git-1.7.4-preview20110204.exe TortoiseGit的下载地址&#xff1a;http://code.google.com/p/tortoisegit/downloads/list&#xff0…

uboot启动流程分析

Uboot的启动流程分为两个阶段&#xff0c;第一阶段主要是汇编语言编写&#xff0c;第二阶段是C语言编写&#xff0c;每个阶段所做的工作不同&#xff0c;这篇文章分析的是uboot 2010版&#xff0c;以tiny4412的uboot为例。 启动过程涉及的主要文件&#xff1a; arch/arm/cpu/a…

(二)linux内核镜像制作

&#xff08;一&#xff09;目的 在进行嵌入式开发的时候&#xff0c;我们往往会先在电脑上安装交叉编译器&#xff0c;然后编译目标板上的代码&#xff0c;最后把代码下载到电路板中&#xff0c;嵌入式系统组成包括&#xff1a;BootLoaderkernelfilesystemapplication&#x…

js+css实现骰子的随机转动

网上找的例子&#xff0c;然后增添了新的东西&#xff0c;在这里展示一下...... 效果图预览&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html x…

linux的mount和umount指令使用

mount即挂在操作&#xff0c;磁盘或分区创建好文件系统后需要挂载到一个目录&#xff08;一般mount在/mnt下&#xff09;才能使 用&#xff0c;和winsdows不同的是在linux下需要手动挂载。 用法&#xff1a;mount [-t文件系统] [选项] 设备目录注意&#xff1a;[ ]为可选项…