经典算法系(21)-二分查找树性能分析(Binary Search Tree Performance Analysis)https://www.douban.com/note/221942390/

 预言家 2012-06-25 21:43:34
二叉搜索树(Binary Search Tree,BST)是一颗典型的二叉树,同时任何节点的键值大于等于该节点左子树中的所有键值,小于等于该节点右子树中的所有键值,并且每个节点域中保存一个记录以其为根节点的子树中所有节点个数的属性,这个属性可用于支持贪婪算法的实现。

 二叉搜索树的建立是在树的底部添加新的元素,搜索即从根元素开始到达树底部的一条路径,插入和搜索相似(注意对重复键的处理),排序按照节点访问方式不同有前序、中序、后序三种
 二叉搜索树算法的运行时间取决于树的形状,最好情况下根节点与每个外部节点间有㏒N个节点,此时树完全平衡,最坏情况下搜索路径上有N个节点。由于创建二叉搜索树的时候,第一个插入的元素总是作为根元素,所以元素插入的顺序决定树的形状。在随机情况下,极度平衡和极度不平衡的树都很少出现,所以这种情况下二叉搜索树算法有着良好的运行情况。
 所以平均情况下,N个随机生成的BST树种,一次搜索,插入大约需要1.39㏒N此比较。如果键值不是随机出现,则二叉搜索树退化为N个节点的链表,一次操作为线性O(N)运行时间
 使用BST树存储文件中每一个文本串,基于字符串的排序使得搜索变得容易

基于BST的构造、搜索、插入,选择,删除(Binary Tree Search)

 算法原理:

 BottomUp插入策略:按照前序策略遍历整个树结构,首先查看当前节点是否为NULL,然后与关键值比较查看是否为目标值,不是的话就分别针对左右子树递归调用搜索算法,然后进入下一个结构,注意在递归调用之间的衔接是由返回一个节点来实现的,所以如果已经到达树底部,则返回一个新节点,这个节点正好位于上一级的子树连接上,这样正好形成整个树结构;

 TopDown插入策略:在BottomUp插入策略的基础上,将新插入的节点在递归回溯的时候逐层旋转,知道根节点的位置;使用基于递归插入操作和旋转操作的策略可以使得最近插入的元素接近BST树的顶部,同时保持树的平衡性。这种插入方式称为从根部插入,实现策略:首先使用普通递归插入将在树底部找到一个合适的位置插入新的节点,然后使用旋转操作将这个新加入的节点旋转到根节点处,不仅可以保持树的平衡,而且由于最近插入的项被使用的概率大,靠近根节点则加速搜索效率;

 旋转操作:BST树中从根部插入新节点:首要考虑的就是是否能够保持BST树的性质。现在使用基于旋转(Rotation)的转换策略,使得BST树保持原有性质。旋转实质上是交换根节点和一个孩子的角色,同时保持各节点的顺序

 

 选择第Kth个值(最小或者最大):利用Node节点中的count标记(此标记说明以当前节点为根节点的子树的所有节点数),可以快速查找给定的序列中第Kth个最小或者最大值;当然前提是将给定的序列扩建成BST;从根节点开始,首先检查其左子树中节点个数,如果正好为K个则返回根节点本身,如果大于K个节点,则对左子树递归调用算法,如果小于K个节点,则说明第K个最小键在根节点的右子树中,变成查找右子树中第K-t-1个最小键的项(t为左子树所有节点,1为根节点自身)

 BST树的节点删除操作:被删除的节点可以有三种情况,没有子节点,有一个子节点,有两个子节点。第一种情况可直接删除;第二种情况需要临时存储子节点的索引,并让被删除节点的父节点指向这个这个索引;第三种情况需要维护BST树的性质,所以一般性策略是选择右子树中最小的元素作为新的根节点(右子树中最小的元素出现在最左边,所以它至多只有一个子节点,可容易删除),然而有时候也会选择左子树中的最大元素作为新的根节点(由于在左右子树中任意选择新的节点作为新的根节点,所以可能造成BST树的不平衡)

 算法代码:

struct Node {
        int value;
        int count;
        Node *left;
        Node *right;
        Node(int v, int c=1, Node* l=NULL, Node* r=NULL):
                                count(c), value(v), left(l), right(r) {

}
};
/**
 * 对root节点进行右旋转操作,也就是:
 * 1. 让root原来的左孩子变成newRoot;
 * 2. 让root变成newRoot的右子节点;
 * 3. 让root原来的左孩子的的右子节点变成root的左子节点
 * */
Node* rightRotate(Node *root) {
        Node *newRoot=root->left;
        root->left=root->left->right;
        newRoot->right=root;
        return newRoot;
}
/**
 * 对root节点进行左旋转操作,也就是:
 * 1. 让root原来的右孩子变成newRoot;
 * 2. 让root变成newRoot的左子节点;
 * 3. 让root原来的右孩子的左子节点变成root的右子节点
 * */
Node* leftRotate(Node *root) {
        Node *newRoot=root->right;
        root->right=root->right->left;
        newRoot->left=root;
        return newRoot;
}

Node* binaryTreeSearch(Node *root, int target) {

if(root==NULL)
                return NULL;

if(target>root->value)
                return binaryTreeSearch(root->right, target);
        else if(target<root->value)
                return binaryTreeSearch(root->left, target);
        else
                return root;
}

Node* binaryTreeInsert(Node *root, int target) {

if(root==NULL) {
                return new Node(target);
        }

if(target>root->value)
                root->right=binaryTreeInsert(root->right, target);
        else if(target<root->value)
                root->left=binaryTreeInsert(root->left, target);

return root;
}
/**
 * 这样可以将新插入的元素旋转到为root;
 * 不仅可以保持BST的平衡性,而且可以保证
 * 新插入的元素的最大访问延迟;
 * */
Node* binaryTreeInsertTopDown(Node *root, int target) {

if(root==NULL) {
                return new Node(target);
        }

if(target>root->value) {
                root->right=binaryTreeInsert(root->right, target);
                root=leftRotate(root);
        }
        else if(target<root->value) {
                root->left=binaryTreeInsert(root->left, target);
                root=rightRotate(root);
        }

return root;
}

Node* binaryTreeInsertWithCount(Node *root, int target) {

if(root==NULL) {
                return new Node(target);
        }

if(target>root->value)
                root->right=binaryTreeInsert(root->right, target);
        else if(target<root->value)
                root->left=binaryTreeInsert(root->left, target);
        root->count++;
        return root;
}
/**
 * 从一个序列中选定第K大的数字,
 * */
int binaryTreeSelect(Node *root, int k) {
        /**
         * 如果当前root为NULL,则选择失败
         * */
        if(root==NULL) {
                printf("\nfind nothing-_-\n");
                return -1;
        }
        /**
         * 如果root的左子节点为NULL
         * */
        if(root->left==NULL) {
                if(k==1)
                        return root->value;
                return binaryTreeSelect(root->right, k-1);
        }
        /**
         * 如果root的左子节点不为NULL;
         * 1. 如果K<=leftCount,则Kth个节点在左子树中
         * 2. 如果K==leftCount+1,则kth个节点就是root自身
         * 3. 如果k>leftCount+1,则Kth个节点就是右子树中的k-1-leftCount个节点
         * */
        int leftCount=root->left->count;
        if(leftCount>=k)
                return binaryTreeSelect(root->left, k);
        else if(leftCount+1==k)
                return root->value;
        else
                return binaryTreeSelect(root->right, k-1-leftCount);
}

/**
 * 将指定的元素target旋转到根节点
 * */
Node* binaryTreeRotate(Node *root, int target) {

if(root==NULL)
                return NULL;

if(target>root->value) {
                root->right=binaryTreeRotate(root->right,target);
                leftRotate(root);
        } else if(target<root->value) {
                root->left=binaryTreeRotate(root->left,target);
                rightRotate(root);
        }

return root;
}
/**
 * 此方法寻找root的左子树中具有最大value的子节点,也就是最‘左边’的子节点;
 * */
Node* subtreeRightMaximum(Node *root) {
        Node *cur=root;
        Node *pre;
        while(cur!=NULL) {
                pre=cur;
                cur=cur->left;
        }
        return pre;
}
/**
 * 此方法寻找root的右子树中具有最大value的子节点,也就是最‘左边’的子节点;
 * */
Node* subTreeLeftMaximum(Node* root) {
        Node *cur=root;
        Node *pre;
        while(cur!=NULL) {
                pre=cur;
                cur=cur->right;
        }
        return pre;
}

Node* binaryTreeDelete(Node *root, int target) {

if(root==NULL)
                return NULL;
        Node *temp;
        Node *newRoot;
        /**
         * 如果target比root->value大,则说明其位于root的
         * 右子树,则继续递归
         * 如果target比root->value小,则说明其位于root的
         * 左子树,则继续递归
         * 如果target等于root->value,则说明当前节点root
         * 就是需要删除的节点,然后分三种情况讨论:
         * 1. 如果root没有左右子节点
         * 2. 如果root只有左节点或者只有右节点
         * 3. 如果root德尔左右子节点都存在;
         * */
        if(target>root->value)
                root->right=binaryTreeDelete(root->right, target);
        else if(target<root->value)
                root->left=binaryTreeDelete(root->left, target);
        else {
                if(root->left==NULL && root->right) {
                        delete root;
                        return NULL;
                } else if(root->left==NULL) {
                        temp=root->right;
                        delete root;
                        return temp;
                } else if(root->right==NULL) {
                        temp=root->left;
                        delete root;
                        return temp;
                }
                /**
                 * 左右子节点都存在的情况,需要从左右子树中寻找下一个根节点;
                 * 这里是从右子树中选取最小的一个节点作为新的根节点;
                 * */
                newRoot=subtreeRightMaximum(root->right);
                /**
                 * 由于右子树中最小的节点必然至多只有一个右节点,所以其删除操作
                 * 较为简单;然后将其的左右子树替换成当前的左右子树;
                 * */
                newRoot=binaryTreeDelete(root->right, newRoot->value);
                newRoot->right=root->right;
                newRoot->left=root->left;
                delete root;
        }

}

 算法说明:

 BST中搜索和插入的策略都是一样的,从传入的树节点开始,首先判断其是否为NULL,如果是的话对于搜索来讲表示失败,对于插入来讲表示需要插入新的节点;如果不是NULL的话,对于搜索来讲比对是否为目标值,然后针对左右子树递归调用,对于插入来讲比对是否相同,表示树中已经有同样的节点算法说明。

 BST树的构建和搜索也使用同样的遍历策略,所以插入与搜索一样容易实现;旋转可用于防止树变得不平衡,实现删除,合并和其他操作的辅助操作,BST树的插入操作可以通过在树的底部插入新元素,然后使用左旋和右旋将新元素带到根节点处,防止树的不平衡状态。每次BST搜索命中的项也可以通过旋转带到根节点处

 使用BST树进行选择算法最大的缺点就是计数域的出现导致额外的内存占用,树结构改变时需要额外的维护操作,同时我们可以对查找到的节点元素使用旋转操作,将其放到根节点的位置,下次使用的时候就能很快定位。
http://www.cnblogs.com/zhangchaoyang/articles/1808928.html
BST树的性能特征:

 二叉搜索树算法的运行时间取决于树的形状,最好情况下树可能完全平衡,这样一次搜索过程就是一条路径的长度㏒N,最差情况下树退化为链表,这样一次搜索过程路径长度可能为N

 使用插入操作构建BST树的过程中,越是前面的节点对树最终形状的影响越是大,第一个元素就是树根,对于随机序列来讲,最坏情况出现的概率很小,所以平均情况能保持较好的运行时间,㏒N

 使用索引项来表示搜索节点,避免动态分配内存。当序列以随机序列插入时,生成完全平衡树的概率很小,但二叉树路径的长度和树的高度与BST的搜索开销联系紧密。平均情况下一棵根据N个随机键生成的BST树中,搜索命中(插入和搜索失败)大约需要1.39㏒N次比较。最坏情况下,可能需要N此比较(也就是顺序搜索)http://www.cnblogs.com/zhangchaoyang/articles/1808928.html

https://www.douban.com/note/221942390/

二分查找树性能分析(Binary Search Tree Performance Analysis)相关推荐

  1. 笔试算法题(58):二分查找树性能分析(Binary Search Tree Performance Analysis)

    议题:二分查找树性能分析(Binary Search Tree Performance Analysis) 分析: 二叉搜索树(Binary Search Tree,BST)是一颗典型的二叉树,同时任 ...

  2. 数据结构与算法(八)二分搜索树(Binary Search Tree)

    本文主要包括以下内容: 二分搜索树的基本概念 二分搜索树的基本操作 1. 插入 2. 删除 3. 查询 实现二分搜索树 二分搜索树的不足 二分搜索树的基本概念 二分搜索树(英语:Binary Sear ...

  3. 二叉搜索树(binary search tree)的建立、删除、查找

    由于输入的数据顺序不同,建立的bst会不一样.最坏的情况就是一个链,所以我们引入了平衡二叉树的概念.这里我们先来看binary search tree.(我随笔里面有一些相关知识) 建立(也就是插入) ...

  4. 给定key值,在Binary Search Tree中查找最接近该键值的结点集合

    http://blog.csdn.net/zhouhao011280s/article/details/8044056 给定key值,在Binary Search Tree中查找最接近该键值的结点集合 ...

  5. PTA甲级 1043 Is It a Binary Search Tree (25分) 树的遍历

    强烈推荐,刷PTA的朋友都认识一下柳神–PTA解法大佬 本文由参考于柳神博客写成 柳神的CSDN博客,这个可以搜索文章 柳神的个人博客,这个没有广告,但是不能搜索 还有就是非常非常有用的 算法笔记 全 ...

  6. 查找二叉树中出现次数最多的数 Find Mode in Binary Search Tree

    为什么80%的码农都做不了架构师?>>>    问题: Given a binary search tree (BST) with duplicates, find all the  ...

  7. 【题意+分析】1043 Is It a Binary Search Tree (25 分)

    立志用最少的代码做最高效的表达 PAT甲级最优题解-->传送门 A Binary Search Tree (BST) is recursively defined as a binary tre ...

  8. 数据结构树之二分查找树

    二叉查找树(Binary Search Tree),也称有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树: ...

  9. 问题六十四:怎么用C++实现二叉查找树(binary search tree)及其相关操作

    64.0 概述 什么是二叉查找树(binary search tree)? 二叉查找树(binary search tree)又叫二叉排序树(binary ordered tree). 对于任意二叉查 ...

最新文章

  1. Python中的urllib,urllib三种不同的请求方式
  2. js进阶ajax函数封装(匿名函数作为参数传递)(封装函数引入文件的方式非常好用)...
  3. 计算机视觉结课论文,计算机视觉与图像识别结课论文
  4. c语言笔记:转义字符
  5. Leetcode每日一题:142.linked-list-cycle-ii(环形链表Ⅱ)
  6. 批量ping IP并检测IP延迟率和丢包率脚本
  7. 中南大学 09 MATLAB 矩阵的处理
  8. 安卓(Android)+苹果(Ios)仿微信、陌陌 移动社交APP系统源码,手机IM聊天软件源码,企业即时通讯APP程序源码...
  9. WinDbg、Symbol Packages、SRVINSTW、DebugView下载地址
  10. CTF:菜狗截获了一张菜鸡发给菜猫的动态图,却发现另有玄机
  11. xxl job踩坑记
  12. RocketMQ初识
  13. 侯立安院士在第五届跨界大会上分享健康建筑病原微生物风险控制
  14. vue 网页滚动到指定位置显示动画效果
  15. 关于电脑连接好WiFi却无法使用浏览器上网的一种解决方法
  16. 无线网dhcp服务器租期,缺省情况下,DHCP服务器分配IP地址的租期为()。
  17. 迅游服务器延时不稳定,用迅游玩《征途》 网络延迟天堑变通途
  18. 十月的尾巴,梦起航的时刻
  19. element-ui el-input-number 输入框不显示数字,默认清空
  20. 盘点 | 2020年网络新技术及人工智能在网络安全领域的特点

热门文章

  1. websvn mysql_Centos 5.3 Nginx+php+mysql配置 独立的 Subversion (SVN)服务器
  2. 安卓给string对象赋值_String 面试题!看完让你恍然大悟!
  3. linux ora01075,操作系统时间被修改导致ORA-01075和ORA-00600[2252]
  4. 服务器空岛怎么修改地形,迷你世界空岛地形码是什么 空岛地形码怎么输入[多图]...
  5. python print换行_聊聊 print 的前世今生
  6. NTP时间服务相关基础及配置文件详解
  7. Linux中介绍who命令实例
  8. 转 使用 HttpClient 4 进行文件上传
  9. TKStudio-LPC1220_GPIO_LED工程后记
  10. A840S黑砖修复过程(2013-05-22修改)