我们在数据结构初期认识到了基础的二叉树,大概对二叉树有了初步的了解。但是二叉树对于我来说却是一个不晓得难点;下面分享一下在学习二叉树的变形二插搜索树的一些经验;

二叉搜索树

二叉搜索树又叫二叉排序树,它或者是一颗空树,或者是一颗具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有的节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有的节点的值都大于根节点的值
  • 它的左右子树也分别是二叉搜索树

相关的操作

(1)二插搜索树的查找
它既然能够起名叫做二插搜索树那么必然在搜索这方面具有优势,下图给出一个二叉搜索树尝试查找一下进行分析一下;

从图片的分析中我们就可以很明了的看出来,这种用递归和非递归可以完成相对来说我觉得在二叉树的操作中递归反而更好写的而非递归想多有挑战性。

//声明一下,这里只是将最后封装的类中的函数拿出来体现一下思想,完整代码在结尾处展示
//1.非递归版本
Node* Find(const K& key)//搜索二叉树的查找
{Node* cur = _root//根节点while(cur){if(cur->_key > key)//进入左子树cur = cur->_left;else if(cur->_key < key)//进入右子树cur = cur->_right;elsereturn cur;}return NULL;
}//2.递归版本
Node* _FindR(Node* root,const K& key)
{if(root == NULL)return NULL;if(root->_key = key)return root; else if(root->_key < key)return _FindR(root->_right,key);else if(root->_key > key)return _FindR(root->_left,key)
}

(2)二插搜索树的插入

  • 如果本来就是空树那就直接插入
  • 如果不是空树按照其规则进行插入

    相比较于空树的直接插入,而向非空的二叉树中插入式就要进行比较,这里我们可以考虑有两个指针一个指针Cur指向当前查找节点,另外一个指针Parent指向当前节点的前一个(为了插入新节点时能够同树连接起来)。
//声明一下,这里只是将最后封装的类中的函数拿出来体现一下思想,完整代码在结尾处展示
//1.非递归版本
bool Insert(const K& key)
{if(_root == nullptr)//空树的情况{_root = new Node(key);}Node* perent = nullptr;Node* cur = _root;while(cur){if(cur->_key > key){parent = cur;cur = cur->left;}else if(cur->_key < key){parent = cur;cur = cur->right;}else//注意有一个注意那就是在搜索二叉树中每一个节点数都是“惟一的”{return false;}}if(parent->_key > key){parent->_left = new Node(key);}else//这里已经排除等于的情况不大于那就小于{parent->_right = new Node(key);}return true;
}
//2.递归版本
bool _Insert(Node* root,const K& key)
{if(root->_key == nullptr)//空树root = new Node(key);if(root->_key > key)return _Insert(root->_left,key);else if(root->_key < key)return _Insert(root->_right,key);elsereturn false;
}

(3)二插搜索树的删除
二叉树的删除可以分为四个情况:

  • 要删除的结点没左右孩子
  • 要删除的节点只有左孩子
  • 要删除的节点只有右孩子
  • 要删除的节点有左右孩子

    从上面的图中我们呢可以看出来在搜索二叉树中,要删除一个节点总共就是有四种情况;但是在用代码实现时,我们还可以将上面的情况进行综合简化,总的来说就可以规划为下面的三种情况:
  • 情况一:删除该节点并且使该节点的双亲指向被删除节点的左孩子
  • 情况二: 删除该节点并且使该节点的双亲指向被删除节点的右孩子
  • 情况三:要删除的节点具有双亲,在它的左子树找到左子树最大节点其余交换,然后是删除交换后的节点(这样做是为了保证删除后不会破坏二叉树的结构)或者是找到该节点右子树中最小节点与其交换然后然后删除就OK了。
bool Earse(const K& key)
{//1.要删除该节点就先要找到该节点Node* parent = nullptr;Node* cur = _root;while(cur){if(cur->_key < key){parent = cur;cur = cur->_right;}else if(cur->_key > key){parent = cur;cur = cur->_left;}else{if(cur->_left == nullptr)//情况二:要删除的节点只有右孩子{if(parent->_left == cur)//如果是父亲节点的左孩子{parent->_left = cur->_right;}else if(parent->_right == cur)//如果是父亲节点的右孩子{parent->_right = cur->_right;}}else if(cur->_right == nullptr)//情况一:要删除的节点只有孩子{if(parent->_left == cur)//如果是父亲节点的左孩子{parent->_left = cur->_left;}else if(parent->_right == cur)//如果是父亲节点的右孩子{parent->_right = cur->_left;}}else//情况三:要删除的节点有左右孩子{Node* del = cur;//要删除的节点//方式一:找到该节点的左子树中最大,然后和该节点进行交换然后删除节点//方式二:找到该节点的右子树中最小,然后和该节点进项交换然后删除节点//这里只是实现:其中一种情况同理,请自行实现;Node* lessParent = cur;Node* lessRight = cutr->_right;//去该节点的右子树要到最大节点,其实很简单就是先进行入右子树一直往左边找直到nullptr//为止,那么此时lessparent节点就是右子树的最大节点.while(lessRight->_left){lessParent = lessRight;lessRight = lessRight->_left;}           //进行值得交换或者是覆盖cur->_key = lessRight->_key;del = lessRight;if (lessParent->_left == lessRight){lessParent->_left = lessRight->_right;}else{lessParent->_right = lessRight->_right;}    }delete del;return true;}}return false;
}//2.递归版本
bool _EarseR(Node** root, const k& key){Node* cur = *root;Node* del = cur;Node* replace = nullptr;if (*root == nullptr)//是一个空树return false;if (cur->_key > key)return _EarseR(&(cur->_left), key);else if (cur->_key < key)return _EarseR(&(cur->_right), key);else{del = cur;if (cur->_left == nullptr)//要删除的节点只有右子树{*root = cur->_right;}else if (cur->_right == nullptr)//要删除的节点只有左子树{*root = cur->_left;}else//要删除的节点具有左右子树{replace = cur->_right;while (replace->_left){replace = replace->_left;}del = replace;cur->_key = replace->_key;return _EarseR(&(cur->_right), replace->_key);}delete del;del = NULL;return true;}}

注意: 相对于二插搜索树的其他操作,二叉搜索树的删除就会具有一定的难度。一点就是要删除要考虑的情况比较多,还有就是在进行递归的转换时应当考虑同非递归的处理方法也有不同;在非递归中我们在删除的时候,我们都会有一个就是前面的有一个指针保存要删除节点的父节点,用来保证删除后二插搜索树的正确性,但是在递归的啥时候相当于把删除操作进行了细化,当递归到是要删除的节点时,此时在递归内面在一个全新的栈帧内面,此时要删除的节点就是这个树的根节点,而他的父亲节点却不知道。这里该怎么办?我想到了一个处理的方法,就是使用二级指针,此时就是根节点的地址了,比如说上图要删除节点8,那次是函数传入的就是节点7的右孩子的二级指针,直接就可以*root = cur->_right;了,
这样就比较方便了,剩下的删除逻辑就是同非递归一样的;

完整代码

//搜索二叉树的实现//K模型template<class k>
struct BSTreeNode//实现一个数节点的结构体
{BSTreeNode(const k& data = K()) :_left(nullptr), _right(nullptr), _key(data){}BSTreeNode<k>* _left;BSTreeNode<k>* _right;k _key;
};template<class k>
class BSTree
{typedef BSTreeNode<k> Node;
public:BSTree() :_root(nullptr){}~BSTree(){}//搜索二叉树插入bool Insert(const k& key){if (_root == nullptr)//如果是空树{_root = new Node(key);return true;}Node* parent = _root;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}if (parent->_key > key){parent->_left = new Node(key);}else if (parent->_key < key){parent->_right = new Node(key);}return true;}//搜索二叉树中序遍历void Inorder(){_Inorder(_root);cout << endl;}//搜索二叉树的查找Node* Find(const k& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return cur;}}return NULL;}//二插搜索树删除---要删除的节点要么是只有一个孩子要么就是没有孩子bool Erase(const k& key){Node* parent = NULL;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_right;}else{Node* del = cur;//1.是叶子;或者只有一个叶子if (cur->_left == NULL){if (parent->_left == cur){parent->_left = cur->_right;}else if (parent->_right == cur){parent->_right = cur->_right;}}else if (cur->_right == NULL){if (parent->_left == cur){parent->_left = cur->_left;}else if (parent->_right == cur){parent->_right = cur->_left;}}else//2.左右都不为空{Node* lessParent = cur;Node* lessRight = cur->_right;while (lessRight->_left){lessParent = lessRight;lessRight = lessRight->_left;}cur->_key = lessRight->_key;del = lessRight;if (lessParent->_left == lessRight){lessParent->_left = lessRight->_right;}else{lessParent->_right = lessRight->_right;}}delete del;return true;}   }return false;}//递归版本bool _InsertR(Node*& root, const k& key)//这里传入的是根节点的引用,因为进入递归后,再去传入的节点地址和上一次没有任何关系;相当于什么都没有插入进去{if (root == nullptr)root = new Node(key);  if (root->_key > key)return _InsertR(root->_left, key);else if (root->_key < key)return _InsertR(root->_right, key);elsereturn false;     }bool InsertR(const k& key){return _InsertR(_root, key);}Node* _FindR(Node* root, const k& key){if (root == NULL)return NULL;if (root->_key == key)//底肥终止条件return root;else if (root->_key > key)return _FindR(root->_left, key);else if(root->_key < key)return _FindR(root->_right,key);}Node* FindR(const k& key){return _FindR(_root, key);}bool _EarseR(Node** root, const k& key){Node* cur = *root;Node* del = cur;Node* replace = nullptr;if (*root == nullptr)//是一个空树return false;if (cur->_key > key)return _EarseR(&(cur->_left), key);else if (cur->_key < key)return _EarseR(&(cur->_right), key);else{del = cur;if (cur->_left == nullptr)//要删除的节点只有右子树{*root = cur->_right;}else if (cur->_right == nullptr)//要删除的节点只有左子树{*root = cur->_left;}else//要删除的节点具有左右子树{replace = cur->_right;while (replace->_left){replace = replace->_left;}del = replace;cur->_key = replace->_key;return _EarseR(&(cur->_right), replace->_key);}delete del;del = NULL;return true;}}bool EarseR(const k& key){return _EarseR(&_root, key);}
private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}
private:Node* _root;
};void Test()
{int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };BSTree<int> t;for (auto e : a){t.Insert(e);}t.InsertR(11);t.InsertR(12);t.InsertR(13);//std::cout << t.FindR(6)->_key << std::endl;//t.Inorder();//t.Erase(8);t.EarseR(8);t.Inorder();
}//代码正确,测试请自行测试,

以上就是二插搜索树的K模型的递归和非递归实现,,当然还有一个K-V模型我们在后面实现。虽然我们实现理解不是最优,你是希望能给大家一点帮助,那样也就有价值了!!!

【数据结构:树】——搜索二叉树-K模型(非递归和递归)相关推荐

  1. 数据结构-树与二叉树-思维导图+小结

    数据结构-树与二叉树-思维导图 1 数据结构-第五章-树与二叉树-思维导图 2 思维导图-补充 3 小结 3.1 知识点小结 3.2 习题小结 1 数据结构-第五章-树与二叉树-思维导图   数据结构 ...

  2. 王道——数据结构——树与二叉树(3)

    系列文章目录 其他章节相关文章 王道--数据结构--栈和队列(1) 本章节其他相关文章 王道--数据结构--树与二叉树(1) 王道--数据结构--树与二叉树(2) 王道--数据结构--树与二叉树(4) ...

  3. 数据结构——树和二叉树章节思维导图

    数据结构--树和二叉树章节思维导图

  4. 数据结构—树与二叉树

    总第119篇 前言 之前谈到的线性表.栈和队列都是一对一的数据结构,但是现实中也存在很多一对多的数据结构,这篇要写的就是一种一对多的数据结构---树.全文分为如下几部分: 树的一些基本概念 树的存储结 ...

  5. 数据结构-树与二叉树

    文章目录 一:树 (1)树的概念 (2)树的一些基本术语 (3)树的表示 A:孩子兄弟表示法 B:双亲表示法 C:孩子表示法 二:二叉树 (1)二叉树的概念 (2)特殊的二叉树 (3)二叉树的性质 ( ...

  6. 数据结构——树和二叉树

    目录 1.树.森林 1.1定义和基本术语 1.1.1结点.树的关系和属性 1.1.2基本概念 1.2树的性质 1.3树的存储结构 1.3.1双亲表示法(顺序存储) 1.3.2孩子表示法(顺序+链式存储 ...

  7. mysql 遍历二叉树_数据结构——树与二叉树的遍历

    目录 树 二叉树 二叉树的遍历 总结 参考资料 序 树是学习数据结构的时候非常重要的一个数据结构,尤其是二叉树更为重要.像Java的HashMap 就使用了红黑树,而Mysql的索引就使用到了B+树. ...

  8. 数据结构-树,二叉树,森林

    树,二叉树,森林 王卓老师的数据结构课程笔记 树和二叉树 定义 结点之间有分支,具有层次关系 是n个结点的有限集. 若n = 0,称为空树: 若n > 0,则它满足如下两个条件: 有且仅有一个特 ...

  9. 数据结构树、二叉树、完全二叉树、二叉查找树、平衡二叉树、红黑树、B+树

    树.二叉树.平衡二叉树.二叉搜索树 树的前序遍历.中序遍历和后序遍历 树的前序遍历.中序遍历和后续遍历是以遍历时根所在的位置顺序命名的.层次遍历即按层从上至下,从左至右遍历即可. 前序遍历:根-> ...

  10. 数据结构——树与二叉树

    树与二叉树 一.树的定义: 1.定义:树(Tree)是n(n>=0)个节点的有限集,n=0时称为"空树".在任意一棵非空树中: ⒈有且仅有一个特定的称为根(root)的节点. ...

最新文章

  1. Linux shell脚本基础学习详细介绍(完整版)一
  2. python3编程题_Python3简单面试编程题
  3. Ubuntu16.04编译RK3399:make kernel.img error
  4. MeasureSpec学习 - 转
  5. flutter 自定义键盘_掘金 AMA:听闲鱼客户端架构师邬吉风聊 Flutter 和移动端开发那些事...
  6. socket编程之TCP/UDP
  7. 各版本JQuery文件下载
  8. eclipse安装中文补丁包
  9. 图形界面上的任意形状图形按钮
  10. dr.oracle黑钻面膜,dr.diamond是什么牌子?dr.diamond钻石面膜怎么样?
  11. android 分享到YouTube失败 403
  12. 商标设计需要注意的要素
  13. Altium Designer(七)已有原理图生成原理图库
  14. 电商网站之更新订单状态
  15. Designing Websites for Performance 如何设计高性能网站 Lynda课程中文字幕
  16. JSP-----JSP简介
  17. Windows11 安装教程(ultraiso制作启动盘)
  18. 3DMax_界面介绍及基本工具使用
  19. Skewed Join Optimization
  20. JS两个自然数相除 , 商 3 余 10, 被除数 , 除数 , 商 , 余数的和是 163, 求被除数 , 除数

热门文章

  1. 神经网络和深度学习基本原理
  2. [Mysql] STR_TO_DATE函数
  3. python爬取网易云飙升榜数据
  4. java程序cpu飙升问题排查过程
  5. 新浪十年路 新浪的触角 新浪成年
  6. 深度定制游戏引擎实现云服务器的非线性增长
  7. 学习嵌入式怎么入门和提高?嵌入式开发教程pdf
  8. 在我差点崩溃了的时候,还好有主从复制
  9. Nginx-代理服务器
  10. python基础第二章:流程控制