二叉排序树基本操作(链表实现)(有错误)
目录
一、二叉排序树的定义
二、基本操作:
1、插入
2、创建
3、删除
4、获得某一个节点所在的层次
总结
代码
一、二叉排序树的定义
对于一棵空的二叉树或者具有如下性质的二叉树:
1.若其左子树不为空,则左子树所有结点的值均小于根结点的值。
2.若其右子树不为空,则右子树所有结点的值均大于根结点的值。
3.其左右子树也是二叉排序树。
例如:
二、基本操作:
1、插入
当root为空时直接插入。
若root不为空,模拟查找的过程找到其该插入的位置,然后插入。
BiNode* BiSortTree::InsertBST(BiNode* bt, datatype x)
{if (bt == NULL){BiNode* s = new BiNode;s->data = x;s->lchild = s->rchild = NULL;bt = s;return bt;}else if (bt->data > x){if (bt->lchild == NULL)bt->lchild = InsertBST(bt->lchild, x);else InsertBST(bt->lchild, x);}else{if (bt->rchild == NULL)bt->rchild = InsertBST(bt->rchild, x);else InsertBST(bt->rchild, x);}
}
2、创建
其实就是一个不断插入的过程。
BiSortTree::BiSortTree(int a[], int n)
{root = NULL;for (int i = 0;i < n;i++){if (i == 0) root = InsertBST(root, a[i]);//在插入操作时,当root节点为空时分配根节点空间,并且root的地址不会改变。else InsertBST(root, a[i]);}
}
3、删除
1.当删除的结点是叶子结点,直接删除即可。
下图中需要删除的结点为p所指,f为其双亲结点。
2.被删除的结点只有左子树或右子树,让当前结点的左孩子或右孩子(不为空的那个)指向其左孩子或右孩子的儿子。
3.被删除的结点既有左子树又有右子树,找到左子树的最大值,与当前要删除的结点替换值,并删除之前找到的左子树的最大值。
有一个特殊情况,当左子树中的最大值结点是被删除结点的孩子。
步骤:找到要删除节点 -> 根据该节点的左右子树情况判断如何删除
bool Delete(Tree &p) //删除该节点
{/* 从二叉排序树中删除节点p, 并重接它的左或右子树 */Tree q, s; /* q是辅助指针,s指向直接前驱 */if( !p->left && !p->right ) /* p为叶子节点 */p = NULL;else if( !p->left ) /* 左子树为空,重接右子树 */{q = p; p = p->right;free(q);}else if( !p->right ) /* 右子树为空,重接左子树 */{q = p;p = p->left; free(q);}else /* 左右子树均不为空 */{q = p;s = p->left;while(s->right) /* 转左,然后向右走到尽头 */{q = s;s = s->right;}p->val = s->val;if( q != p ) /* 判断是否执行上述while循环 */q->right = s->left; /* 执行上述while循环,重接右子树 */ elseq->left = s->left; /* 未执行上述while循环,重接左子树 */free(s);}return true;
}bool DeleteBST(Tree &T, int key) //找到要删除的节点
{/* 若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素节点 *//* 并返回TRUE;否则返回FALSE */if( T == NULL){cout << "要删除节点不存在" << endl;return false; /* 不存在关键字等于key的数据元素 */ }else{if( key == T->val )Delete(T);else if( key < T->val)return DeleteBST(T->left, key);elsereturn DeleteBST(T->right, key);}return true;
}
4、获得某一个节点所在的层次
根节点的层次为1
Tree getLevel(Tree &t, int key, int &level) //查找某个节点所在的层次
{if(t == NULL){cout << "没找到该节点!" << endl;return NULL; }if(t->val == key) return t;else{level ++;if(key < t->val) return getLevel(t->left, key, level);else return getLevel(t->right, key, level); }
}
总结
二叉排序树
一、创建
不断插入的过程,但要注意在创建开始把树置为NULL
因为插入一个新节点就是找到一个NULL节点插入二、插入 -- 递归
1.如果树为空,直接创建新节点插入
2.树不为空
(1).如果要插入节点值小于根节点,递归插入左子树
(2).如果要插入节点值大于根节点,递归插入右子树三、查找 -- 递归
1.如果树为空,返回NULL
2.树不为空
(1)如果查找节点值等于根节点,返回
(2)如果查找节点值小于根节点,递归查找左子树
(3)如果查找节点值大于根节点,递归查找右子树四、删除 -- 递归
1.查找要删除节点
(1).如果节点不存在,返回false
(2).如果节点值等于根节点值,删除根节点
(3).如果节点值大于根节点值,递归右子树
(4).如果节点值小于根节点值,递归左子树
2.删除节点 -- 三指针p,q,s
p指向删除节点,s指向删除节点的前驱节点,q是辅助指针
(1)如果该节点没有左右孩子,即是叶子节点,直接置为NULL
(2)如果该节点没有左孩子或右孩子,上移子树即可
①如果没有左孩子,让该节点的右孩子上移代替该节点
②如果没有右孩子,让该节点的左孩子上移代替该节点
(3)如果该节点有左右孩子,用该节点的直接前驱或者直接后继替换该节点
我们这里使用直接前驱,左子树的递归右子树
①如果该该节点的左子树有右子树,用右子树的值替换该节点
显而易见,该直接前驱肯定没有右子树,但是可能有左子树
所以我们需要把s的左子树给q的右子树,因为s的左子树的值肯定比q的值要大
②如果该节点的左子树没有右子树,直接让该节点的左子树替换该节点
代码
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;typedef struct tree
{int val;struct tree *left, *right;
}*Tree, TreeNode; //声明
void Create(Tree &t);
void InOderTraverse(Tree &t);
Tree Search(Tree t, int key);
void Insert(Tree &t, int key);
bool Delete(Tree &p);
bool DeleteBST(Tree &T, int key);//实现
void InOderTraverse(Tree &t) //中序递归遍历二叉树
{if(t != NULL){InOderTraverse(t->left);printf("%d ", t->val);InOderTraverse(t->right);}
}Tree Search(Tree t, int key) //递归查找
{if(t == NULL) return NULL;if(key == t->val) return t;else if(key < t->val) return Search(t->left, key);else Search(t->right, key);
}void Insert(Tree &t, int key)
{//如果树为空直接插入 if(t == NULL){Tree newNode = new TreeNode;newNode->val = key;newNode->left = newNode->right = NULL;t = newNode;}//查找插入位置 else if(key < t->val) Insert(t->left, key);else Insert(t->right, key);}void Create(Tree &t) //创建 -- 不断插入的过程
{t = NULL; //初始化树为空, 必须要有, 对应插入操作的直接插入这一种情况 int cnt;cout << "请输入节点个数: ";cin >> cnt;for(int i = 0; i < cnt; i ++ ){int val;cin >> val;Insert(t, val);}
}Tree find_Father(Tree t, int key) //查找父节点
{Tree cur = t;while(cur != NULL && cur->val != key){if(key > cur->val) cur = cur->right;else cur = cur->left;}if(cur == NULL) return NULL;return cur;
}bool Delete(Tree &p) //删除该节点
{/* 从二叉排序树中删除节点p, 并重接它的左或右子树 */Tree q, s; /* q是辅助指针,s指向直接前驱 */if( !p->left && !p->right ) /* p为叶子节点 */p = NULL;else if( !p->left ) /* 左子树为空,重接右子树 */{q = p; p = p->right;free(q);}else if( !p->right ) /* 右子树为空,重接左子树 */{q = p;p = p->left; free(q);}else /* 左右子树均不为空 */{q = p;s = p->left;while(s->right) /* 转左,然后向右走到尽头 */{q = s;s = s->right;}p->val = s->val;if( q != p ) /* 判断是否执行上述while循环 */q->right = s->left; /* 执行上述while循环,重接右子树 */ elseq->left = s->left; /* 未执行上述while循环,重接左子树 */free(s);}return true;
}bool DeleteBST(Tree &T, int key) //找到要删除的节点
{/* 若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素节点 *//* 并返回TRUE;否则返回FALSE */if( T == NULL){cout << "要删除节点不存在" << endl;return false; /* 不存在关键字等于key的数据元素 */ }else{if( key == T->val )Delete(T);else if( key < T->val)return DeleteBST(T->left, key);elsereturn DeleteBST(T->right, key);}return true;
}Tree getLevel(Tree &t, int key, int &level) //查找某个节点所在的层次
{if(t == NULL){cout << "没找到该节点!" << endl;return NULL; }if(t->val == key) return t;else{level ++;if(key < t->val) return getLevel(t->left, key, level);else return getLevel(t->right, key, level); }
}void getHeight(Tree t)
{if(t == NULL) return ;if(t->left == NULL && t->right == NULL){t = max() }
} int main()
{Tree t;Create(t);InOderTraverse(t);cout << endl;return 0;
}
说明
对二叉树的删除操作:左右孩子都有的情况
我们通过让该节点的直接前驱替换该节点然后删除该直接前驱节点实现删除该节点的目的
已知一个节点的直接前驱是它的左子树的递归右子树
证明:一个节点的直接前驱是它左子树中最大的那个值
左子树最大的那个值肯定在该子树的右子树
现在我们用s标记的该节点直接前驱节点,q节点是s节点的父亲节点
当递归结束时,s节点肯定没有右子树了,但是可能有左子树
并且s节点的左子树的值一定大于q节点的值
证明:因为s节点是q节点的右子树,所以s节点以及s节点的子树的值都是大于等于根节点的值
所以,删除直接前驱节点s只需要让 q->right = s->left;
如图:
但是,如果要删除节点的左子树没有右子树
此时q节点直接要删除节点,s节点指向要删除节点的左子树也就是q节点的左子树
那么s节点包括他的左子树的值就小于q节点
如图:
此时 q->left = s->left;
二叉排序树基本操作(链表实现)(有错误)相关推荐
- 修正《啊哈算法》上模拟链表算法的错误
算法描述: 在<啊哈算法>上有介绍模拟链表的实现.链表这种数据结构可以通过数组来实现,我们需要两个数组,其中一个数组data存放数据,即数据域.另外一个数组right用来存放每一个数的 ...
- 单向链表的C语言实现与基本操作
本文的主要内容目录: 一.单向链表的C语言实现 二.单向链表的基本操作 一.单向链表的C语言实现 链表作为一种基本的数据结构在程序开发过程当中经常会使用到.对C语言来说链表的实现主要依靠结构体和指针, ...
- c语言链表错误,C语言创建链表错误之通过指针参数申请动态内存实例分析
本文实例讲述了C语言创建链表中经典错误的通过指针参数申请动态内存,分享给大家供大家参考之用.具体实例如下: #include #include // 用malloc要包含这个头文件 typedef s ...
- 二叉排序树的定义及基本操作(构造、查找、插入、删除)递归及非递归算法
文章目录 二叉排序树的定义 二叉排序树的查找 二叉排序树的插入 二叉排序树的构造 二叉排序树的删除 完整代码及实例 二叉排序树的查找效率 二叉排序树的定义 二叉排序树(Binary Sort Tree ...
- 【超详细】一文学会链表解题
本文是最近写的两篇链表的整合版,为方便大家查阅,所以整合了一下,也对原有文章中逻辑上的一些错误作了修正,虽说只是整合,也做了不少排版上的工作,如有帮助,欢迎转发+在看^_^. 前言 如果说数据结构是算 ...
- python删除链表满足pred的元素_python 数据结构一 之 线性表
python数据结构教程第一课 从这里将会正式开始讲解python的一些实用的数据结构,原理加上实例源码. 一.简介 二.线性表的抽象数据类型 三.顺序表的实现 四.链接表的实现 1.单链表 2.带尾 ...
- 编程题总结 链表问题常用解决方法
链表问题 前言 链表基本操作 链表创建 头插法 尾插法 节点遍历 节点删除 双指针法 链表中的倒数k个结点 解题思路 解题代码 分割法 链表分割 解题思路 解题代码 遍历与创建链表结合 链式A+B 解 ...
- 链表插入操作的时间复杂度真的是O(1)吗?
提起链表,很多人可能都会知道它的优势就是能够快速插入.删除数据.但是往链表中插入数据的时间复杂度真的是O(1)吗?相信看完这篇文章,读者会有自己的答案了. 为什么用一节来讲解链表代码实现 ? 1. 链 ...
- 都是套路:盘点 Java 面试中链表的几大解题模板方法!
来自:码海 前言 如果说数据结构是算法的基础,那么数组和链表就是数据结构的基础.因为像堆,栈,对,图等比较复杂的数组结基本上都可以由数组和链表来表示,所以掌握数组和链表的基本操作十分重要. 今天就来看 ...
最新文章
- 设置WebStorm像VSCode一样每行代码结尾自动格式化加入“;”分号(JavaScript、TypeScript格式化)
- linux每日命令(31):tar命令
- 迪米特法则 java_java设计模式--迪米特法则
- c++求平均值_云顶之弈S4:六射手娱乐吃鸡!人均刮痧王,每个人都是主C
- Tomcat常用面试题
- TypeScript BigInt
- webpack打包后引用cdn的js_手摸手 Webpack 多入口配置实践
- 服务器文件地址怎么写,服务器里的文件链接地址怎么写
- OpenMP对于嵌套循环应该添加多少个parallel for
- asp.net项目发布打包研究
- 怒爬某 Hub 资源就为撸了一个鉴黄平台
- openssl 升级
- 树中两个节点的最低公共祖先
- CMS(内容管理系统)
- idea退出debug模式_一文搞懂如何在Intellij IDEA中使用Debug,超级详细
- cd linux自带系统安装,大神示范win7系统将CDLinux装入硬盘的法子
- flink流处理示例开发
- 谷哥学术2022年2月资源分享下载列表 15/20
- 假设检验3- 卡方分布
- 比ping更强大的fping