数据结构之树之不同种类篇
二叉搜索树(BST)
基础知识
- 树通常用来存储已排序或已有序的数据。在树中存储数据,常见的就是二叉搜索树(Binary Search Tree,BST)
- BST中的数据是按值排序的:一个节点所有的左侧子孙节点都小于或等于该节点,所有的右侧子孙节点都大于或等于该节点。
- 当面试官说树的时候,通常二叉树,再确认是否为二叉搜索树
- 如果使用中序遍历二叉搜索树,遍历的顺序会按照节点关键字的大小关系从小到大依次进行。
定义
typedef struct TreeNode
{int val;TreeNode* left;TreeNode* right;TreeNode* parent;
};
插入
将关键字k插入到二叉搜索树
若是已经存在,就不再插入
所以插入都是插入在叶子节点
比如已经有了5->6->9,
当插入7的时候,7并不会插入在6和9之间,而是插入在9的左边
- 找到要插入的位置节点,当前位置为空时
- 新建一个节点,给其赋当前值,再使其指向当前的父节点
int BST_Insert(TreeNode *root, int k, TreeNode* parent=NULL)
{if(root==NULL){TreeNode* tempNode=(TreeNode*)malloc(sizeof(TreeNode));tempNode->val=k;tempNode->left=NULL;tempNode->right=NULL;tempNode->parent=parent;return 1;}else if(k==root->val)return 0;//树中存在相同的关键字else if(k<root->val)return BST_insert(root->left,k,root);elsereturn BST_insert(root->right,k,root);
}
构造
用数组arry[]创建二叉查找树
void create_BST(TreeNode* root,int arr[],int n){root=NULL;//初始为空树for(int i=0;i<n;i++)BST_insert(root,arry[i]);
}
查找
递归:
TreeNode* BST_Search(TreeNode* root,int key){if (root==NULL||key==root->val)return root;if (key<root->val) return BST_Search(root->left,key);elsereturn BST_Search(root->right,key);
}
非递归:
TreeNode* BST_Search_NonRecur(TreeNode* root, int key)
{while(root != NULL && key != root->val){if(key < root->val)root = root->val;elseroot = root->val;}return T;
}
最大值与最小值
TreeNode* BST_Minnum(TreeNode* root)
{while(root->left != NULL)root = root->left;return root;
}
TreeNode* BST_Maxnum(TreeNode* root)
{while(root->right != NULL)root = root->right;return root;
}
最近共同祖先(LCA)
二叉搜索树的最近共同祖先(LowestCommonAncestor)
假定树中存在这两个点的值
检查当前节点
If value1和value2都小于当前节点的值检查左子节点
If value1 和value2 都大于当前节点的值检查右子节点
否则
当前节点就是最近共同祖先
非递归:
TreeNode* find_LCA(TreeNode* root,int value1,int value2){while(root!=NULL){int value=root->val;if(val>value1&&val>value2)root=root->left;else if(val<value1&&val<value2)root=root->right;elseereturn root;}return NULL;//only if empty tree;
}
前驱和后继
问题:给定一个二叉查找树的节点,求出它在中序遍历中的前驱和后继。
后继:
两种情况
- 若结点 x 的右子树不为空,则 x 的后继就是它的右子树中关键字值最小的结点;
- 若结点 x 的右子树为空,为了找到其后继,从结点 x 开始向上查找,直到遇到一个祖先结点 y,它的左儿子也是结点 x 的祖先,则结点 y 就是结点 x 的后继。如下图
TreeNode* BST_successor(TreeNode* node){if(node->right!=NULL)return BST_Minnum(node->right);TreeNode* p =node->parent;while(p!=NULL&&p->right==node){node=p;p=p->parent;}
}
前驱
两种情况
- 若结点 x 的左子树不为空,则 x 的前驱是它的左子树中关键字值最大的结点;
- 若结点 x 的左子树为空,为了找到其前驱,从结点 x 开始向上查找,直到遇到一个祖先结点 y,它的右儿子也是结点 x 的祖先,则结点 y 就是结点 x 的前驱。
TreeNode* BST_predecessor(TreeNode* node){if(node->left!=NULL)return BST_Maxnum(node->left);TreeNode* p=node->parent;while(p!=NULL&&p->left==node){node=p;p=p->parent;}return p;
}
BST的删除
二叉查找树的删除操作相对复杂一点,它要按3种情况来处理:
1.若被删除结点z是叶子结点,则直接删除,不会破坏二叉排序树的性质;
2. 若结点z只有左子树或只有右子树,则让z的子树成为z父结点的子树,代替z的位置;
3. 若结点z既有左子树,又有右子树,则用z的后继(没有左孩子)代替z(先删除后继结点,再将后继的内容去替换z),而删除后继结点的过程就是第一种或第二种情况。
说明:第三种情况,因为z有两个子女,所以它的后继肯定是它右子树中的最左边(最小值),也就是说它的后继要么是叶子,要么只有一个右孩子
void BST_delete(TreeNode* root,TreeNode* z){if(z->left==NULL&&z->right=NULL){if(z->parent!=NULL){if(z->parent->left==z)z->parent->left=NULL;elsez->parent->right=NULL;}elseroot->NULL;free(z);}else if(z->left!=NULL&&z->right==NULL){z->left->parent=z->parent;//将z的左子树的父亲设为z的父亲if(z->parent!=NULL){if(z->parent->left==z)z->parent->left=z->left;//将z父亲的左孩子指向z的左子树elsez->parent->right=z->left;}elseroot=z->left; //删除左斜单支树的根结点free(z);}else if(z->right!=NULL&&z->left==NULL){z->right->parent=z->parent;if(z->parent!=NULL){if(z->parent->left==z){z->parent->left=z->right;}elsez->parent->right=z->right;}elseroot=z->right; //删除右斜单支树的根结点free(z);}else{TreeNode* s=BST_Successor(z);z->val=s->val; //后继s的关键字替换为z的关键字BST_Delete(T,s); //转为第一或第二种情况}}
对于一个高度为h的二叉查找树来说,删除操作和插入操作一样,都是O(h)
参考1
参考2
红黑树
http://taop.marchtea.com/03.01.html
二叉查找树回顾
由于红黑树本质上就是一棵二叉查找树,所以在了解红黑树之前,咱们先来看下二叉查找树。
二叉查找树(Binary Search Tree),也称有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
- 若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意结点的左、右子树也分别为二叉查找树。
- 没有键值相等的结点(no duplicate nodes)。
因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O(lgn).(至于n个结点的二叉树高度为lgn的证明,可参考算法导论 第12章 二叉查找树 第12.4节)。
红黑树概述
前面我们已经说过,红黑树,本质上来说就是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
但它是如何保证一棵n个结点的红黑树的高度始终保持在h = logn的呢?这就引出了红黑树的5条性质:
- 每个结点要么是红的,要么是黑的。
- 根结点是黑的。
- 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
- 如果一个结点是红的,那么它的俩个儿子都是黑的。
- 对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。
正是红黑树的这5条性质,使得一棵n个结点是红黑树始终保持了logn的高度,从而也就解释了上面我们所说的“红黑树的查找、插入、删除的时间复杂度最坏为O(log n)”这一结论的原因。
如下图所示,即是一颗红黑树(下图引自wikipedia: http://t.cn/hgvH1l ):
上文中我们所说的 “叶结点” 或”NULL结点”,它不包含数据而只充当树在此结束的指示,这些结点以及它们的父结点,在绘图中都会经常被省略。
树的旋转知识
当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质。
为了继续保持红黑树的性质,我们可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后,继续保持它的性质或平衡。
树的旋转,分为左旋和右旋,以下借助图来做形象的解释和介绍:
1.左旋
如上图所示:
当在某个结点pivot上,做左旋操作时,我们假设它的右孩子y不是NIL[T],pivot可以为任何不是NIL[T]的左孩子结点。
左旋以pivot到y之间的链为“支轴”进行,它使y成为该孩子树新的根,而y的左孩子b则成为pivot的右孩子。
左旋操作的参考代码如下所示(以x代替上述的pivot):
B树
http://taop.marchtea.com/03.02.html
平衡树
数据结构之树之不同种类篇相关推荐
- python tree结构_Python入门篇-数据结构树(tree)篇
Python入门篇-数据结构树(tree)篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.树概述 1>.树的概念 非线性结构,每个元素可以有多个前躯和后继 树是n(n& ...
- 数据结构之树:树的介绍——9
数据结构之树,介绍篇 树的基本定义 介绍:树(tree)是计算机中非常重要的数据结构,它的外形看起来像一颗倒挂着的的树,使用树这种结构可以描述生活中很多的事物,如族谱,单位的组织架构,xml,html ...
- 数据结构之树的详解汇总
"If I have seen further it is by standing on the shoulders of giants." --Issac Newton 树的种类 ...
- 数据结构显示树的所有结点_您需要了解的有关树数据结构的所有信息
数据结构显示树的所有结点 When you first learn to code, it's common to learn arrays as the "main data struct ...
- 【技术点】数据结构--B树系列之B+树(五)
文章目录 前言 B+树的结构 Key & Data 叶子节点保存数据:减少I/O的设计 中间节点的索引作用 链表的作用:范围查询 B+树的操作 插入 删除 B+树总结 B*树 总结 前言 前面 ...
- Python 数据结构 tree 树
[Python] 数据结构 tree 树 树节点类 TreeNode 作为最简单的树节点,我们只需要3个基本属性 name: 当前节点的名字(使用str来保存) parent: 父节点对象(对根节点来 ...
- 【数据结构】初入数据结构的树(Tree)以及Java代码实现(一)
初入数据结构的树(Tree)以及Java代码实现(一) 树的定义 为什么叫树? 树型结构的元素具有一对多关系 树的定义 树的一些基本概念 树的结点 后代,祖先 子树.空树 树的度与高(深度),结点的度 ...
- 【数据结构和算法设计】算法篇(7) 贪心法
文章目录 7.1 贪心法概述 7.1.1 什么是贪心法 7.1.2 用贪心法求解的问题应具有的性质 1. 贪心选择性质 2. 最优子结构性质 7.1.3 贪心法的一般求解过程 7.2 求解活动安排问题 ...
- 数据结构树的基本操作_《数据结构》树的基本操作.doc
<数据结构>树的基本操作 实验四 课程名称:完成日期:姓名:学号:指导教师:实验名称:实验序号:实验成绩:一.实验目的及要求 二.实验环境.实验内容 求出它的深度. .调试过程及实验结果 ...
最新文章
- ubuntu dig timeout解决方法,dnscat执行失败也是这个原因
- 这才是实现分布式锁的正确姿势!
- DCMTK:部分元素访问API的应用程序
- 《此生未完成》痛句摘录(一)
- php 正则提取日期,PHP正则匹配日期和时间(时间戳转换)的实例代码
- [uva816]AbbottsRevenge Abbott的复仇(经典迷宫BFS)
- 【20090702-03】ArcEngine的类库介绍(转)
- python中sorted()函数的用法_Python中的Sorted()函数
- Retrofit的讲解和使用
- nvidia tesla k40
- [软考]项目管理常用案例总结
- 正宇丨人活得虚伪容易,活得真实很难
- hdu 1207 汉诺塔II (四柱汉诺塔)
- 2012年中国各省市区GDP排行榜 附各主要城市GDP排行榜
- 初学,用python获取B站视频
- 【历史上的今天】8 月 2 日:字节跳动收购 Musical.ly;PlayStation 之父诞生;早期的女性计算机先驱
- 瑞萨RL78簇bootloader深入探讨(一):User工程建立
- Python实现100以内的加减法口算练习题
- 仓库物品领用吉度PDA出入库盘点扫码方案
- 今日新闻早报 精选简报12条 每天一分钟 知晓天下事 2月2日