(一)BST树(二叉搜索树)

1.BST二叉搜索树

1.1BST树的定义

1 BST树也是一个二叉树,故满足递归定义;

2 其次每个节点只存在一个值;

3 需满足左子树值<=根值<=右子树,故按照中序遍历会得到一个非递减序列。

BST涉及操作主要增,删,查,除了删麻烦一点,其他操作均可递归实现,二叉查找树的一般性质:

1.在一棵二叉查找树上,执行查找、插入、删除等操作,的时间复杂度为O(log2n)。因为,一棵由n个结点,随机构造的二叉查找树的高度为log2n,所以顺理成章,一般操作的执行时间为O(log2n)。

2.但若是一棵具有n个结点的线性链,则此些操作最坏情况运行时间为O(n)。

1.2基本操作:

①插入,删除,元素个数,前/中/后/层序遍历,

②二叉树镜像,判断当前二叉树是否是一颗BST树,

③找出指定区间的元素,

④判断一颗BST树是不是平衡树,

==》每一个左右子树的高度差不超过1,

⑤找出两个元素的LCA公共祖先节点并返回,

⑥判断参数传入的二叉树是否是当前二叉树的一颗子树

⑦找第K小/大的元素值 ===》 中序遍历

1.3BST树结构定义

// BST树类型定义
template<typename T>
class BSTree
{
public:BSTree() :root(nullptr) {}
private:// 定义节点类型struct BSTNode{// T() 零构造BSTNode(T val = T()):data(val), left(nullptr), right(nullptr){}T data; // 数据域BSTNode *left; // 指向左孩子BSTNode *right; // 指向右孩子};BSTNode *root = nullptr;
};

2.代码实现

2.1插入,删除,遍历,查询

2.1.1插入

非递归:

如果root为空,直接将root指向新申请节点并返回,否则寻找合适的位置,用parent指针保存当前节点的父节点,当当前节点为空则找到合适位置,用父节点链起新节点。

// BST树的插入操作
void insert(const T &val)
{BSTNode *n = new BSTNode(val);if (root == nullptr){root = n;return;}BSTNode *parent = nullptr;BSTNode *cur = root;while (cur != nullptr){if (val > cur->data){parent = cur;cur = cur->right;}else if (val < cur->data){parent = cur;cur = cur->left;}else{return;}}if (val < parent->data){parent->left = n;}else{parent->right = n;}
}

递归:

root为空,申请节点返回,

否则,>val 新节点在当前节点左边,<val 在右边

在寻找时最后要将当前层都返回给上一层,保持链表连接

//递归插入
void insert02(const T &val)
{root = insert02(root, val);
}
BSTNode* insert02(BSTNode* root, const T& val)
{if (root == nullptr){return new BSTNode(val);}if (root->data < val){root->right = insert02(root->right, val);}else if (root->data > val){root->left = insert02(root->left, val);}return root;//①不管是那种情况,最后都要把链表链回去,其他节点的指针域不能变  ==>包含重复的情况,返回就ok了/*②错误:==>并不是每次都将节点链回去==》本来想把重复情况去掉但没想到把链表打散了if(root->data==val)//如果没有重复节点插入那么这样只有最后一次插入节点会返回给根节点,相当于只插入了最后一次的节点{return root;}    */
}
2.1.2删除

删除有三种情况:①该节点无子节点,②有一个,③有两个

当有两个子节点时,删除该节点则需:

用前驱节点或者后继节点的值覆盖该节点,接下来就需要处理前驱节点或者后继节点:

对于前驱节点或者后继节点:同样有①②两种情况,所以先处理情况③,最后统一处理情况①②

非递归:

//非递归目的就是从上往下找到要删除的节点,只需要找到节点对其进行处理即可,不需要考虑递归时每一层递归的不同返回情况
//删除
void remove(const T& val)
{if (root == nullptr)return;BSTNode* cur = root;BSTNode* parent = nullptr;while (cur != nullptr)//寻找目标节点{if (cur->data > val){parent = cur;cur = cur->left;}else if (cur->data < val){parent = cur;cur = cur->right;}elsebreak;}if (cur == nullptr)//没有要删除的节点return;if (parent == nullptr)//!!!当只有一个根节点时,对其删除则将root=nullptr{delete cur;root = nullptr;return;}if(cur->left != nullptr && cur->right != nullptr)  //情况③!!!前驱节点没有右子树,故必定到达情况②或① 所以不用while用if就够了{BSTNode* pioneer = cur->left;//前驱节点parent = cur;//!!! parent = cur not  pioneerwhile (pioneer->right != nullptr){parent = pioneer;pioneer = pioneer->right;}cur->data = pioneer->data;cur = pioneer;}if (cur->left != nullptr&&cur->right==nullptr)//情况②  只有左孩子{if (parent->left==cur)//是父节点的左孩子{parent->left = cur->left;}else if (parent->right ==cur)//是父节点的右孩子{parent->right = cur->left;}}else if (cur->right != nullptr&&cur->left==nullptr)//只有右孩子{if (parent->left == cur){parent->left = cur->right;}else if (parent->right == cur){parent->right = cur->right;}}else//没有孩子{if (parent->left == cur){parent->left = nullptr;}else if (parent->right == cur){parent->right = nullptr;}}delete cur;/*优化://处理#1和#2两种情况BSTNode *child = cur->left;if (child == nullptr)child = cur->right;//把child写入cur的地址域中if (parent == nullptr)//如果要删除的是一个只有一个孩子节点的根节点  root==cur{root = child;}else if (parent->left == cur){parent->left = child;}else{parent->right = child;}delete cur;*/
}

递归:

//递归就是要在多次探索的时候,找到合适的情况处理,但注意要想让递归顺利进行就不能改变原数据结构,就必须在每一层将原始状态返回回去
//这也是处理合适情况时(找到合适位置时)需要特别注意的,在处理合适情况每一种细分的情况时,返回的节点就可能不一样,要区别处理
BSTNode* remove02(BSTNode* cur, const T& val)
{if (cur == nullptr)return nullptr;if (cur->data > val){cur->left = remove02(cur->left, val);}else if(cur->data<val){cur->right = remove02(cur->right, val);}else//找到了要删除的节点{BSTNode* parent = nullptr;if (cur->left != nullptr && cur->right != nullptr)//有两个孩子{parent = cur;BSTNode* pre = cur->left;while (pre->right != nullptr){parent = pre;pre = pre->right;}cur->data = pre->data;//pre前驱节点即是要删除的节点//法一:手动做BSTNode* child = pre->left;if (child == nullptr)child = pre->right;if (parent->left == pre){parent->left = child;}else if (parent->right == pre){parent->right = child;}delete pre;//法二(优化):利用递归去我的左面删除我的前驱节点//cur->left = remove02(cur->left, pre->data);}else//有一个或没有{if (cur->left != nullptr)//有左孩子把左孩子返回给上一层{BSTNode* left = cur->left;delete cur;return left;}else if (cur->right!= nullptr)//有右孩子把右孩子返回给上一层{BSTNode* right = cur->right;delete cur;return right;}else//没有孩子{delete cur;return nullptr;}}}return cur;
}
2.1.3遍历

1.前序遍历

递归:

//前序遍历
void PreOrder()
{cout << "前序遍历:";preOrder(root);cout << endl;
}
void preOrder(BSTNode* root)
{if (root == nullptr)return;cout << root->data << " ";preOrder(root->left);preOrder(root->right);
}

非递归:

法一:

void preOrder01()
{cout << "前序遍历:";stack<BSTNode*> s;BSTNode* cur = root;while (!s.empty() || cur != nullptr){while (cur != nullptr)边遍历边打印,并存入栈中,以后需要借助这些节点进入右子树{cout << cur->data << " ";s.push(cur);cur = cur->left;}//当p为空时,说明根和左子树都遍历完了,该进入右子树了,//那么原来入栈的左子树出栈,直到有右子树,进入右子树接着进行先根遍历if (!s.empty()){cur = s.top();s.pop();cur = cur->right;}}cout << endl;
}

法二:

void preOrder02()
{cout << "前序遍历:";stack<BSTNode*> s;s.push(root);while (!s.empty()){BSTNode* cur = s.top();cout << cur->data << " ";s.pop();if (cur->right != nullptr)//栈,先进后出,所以先进右边{s.push(cur->right);}if (cur->left != nullptr){s.push(cur->left);}}cout << endl;
}

2.中序遍历:

递归:

//中序遍历
void InOrder()
{cout << "中序遍历:";inOrder(root);cout << endl;
}
void inOrder(BSTNode* root)
{if (root == nullptr)return;inOrder(root->left);cout << root->data << " ";inOrder(root->right);
}

递归:

//中序遍历非递归
void InOrder02()
{cout << "中序遍历";stack<BSTNode*> s;BSTNode* cur = root;while (!s.empty()||cur!=nullptr){while (cur != nullptr)//先走左子树,将左孩子全入栈  ===>前{s.push(cur);cur = cur->left;}//当cur==nullptr说明已经到达当前分支的最左子节点,紧接着就该打印栈顶节点了if (!s.empty()){cur = s.top();cout << cur->data << " ";                      //====>中s.pop();cur = cur->right;                              //====>后//如果cur->right==nullptr,说明该打印当前节点的父节点(当前树的根节点)了,继续进入这个判断,出栈,打印//如果!=nullptr则进入右子树,开始新一轮的左子树遍历(递归的自我实现)}}
}

3.后序遍历:

递归:

//后序遍历
void PostOrder()
{cout << "后序遍历:";postOrder(root);cout << endl;
}
void postOrder(BSTNode* root)
{if (root == nullptr)return;postOrder(root->left);postOrder(root->right);cout << root->data << " ";
}

非递归:

void PostOrder02()
{cout << "后序遍历:";stack<BSTNode*> s;BSTNode* cur = root;BSTNode* tag = nullptr;while(!s.empty() || cur != nullptr){while (cur != nullptr){s.push(cur);cur = cur->left;}cur = s.top(); s.pop();//到最左节点就拿出来,进行分情况讨论if (cur->right == nullptr || cur->right == tag)//没右孩子或者右面已经访问过了{cout << cur->data << " ";tag = cur;//记录上一次访问的元素,为访问其根节点时顺利打印做准备,表示已经访问过了,可以后根打印根节点了cur = nullptr;//要置空,打印过就不再引用,否则卡在这里一直打印最左节点}else//有右子树就往右子树走,进入右子树重新开始对其的后根遍历  (有右子树并且没有访问过){s.push(cur);cur = cur->right;}}cout << endl;
}

4.层序遍历

递归:

int high(BSTNode* root)
{if (root == nullptr)return 0;int left = high(root->left);int right = high(root->right);return left > right ? left + 1 : right + 1;
}
void levelOrder(BSTNode* root)
{if (root == nullptr)return;cout << "层序遍历:";int h = high(root);for (int i = 0; i < h; ++i){levelOrder(root, i);}cout << endl;
}
void levelOrder(BSTNode* root, int level)
{if (root == nullptr)return;if (level == 0){cout << root->data << " ";return;}levelOrder(root->left, level - 1);levelOrder(root->right, level - 1);
}

非递归:

void levelOrder02(BSTNode* root)
{queue<BSTNode*> que;que.push(root);while (!que.empty()){BSTNode* front = que.front();if (front->left != nullptr){que.push(front->left);}if (front->right != nullptr){que.push(front->right);}cout << front->data << " ";que.pop();}cout << endl;
}

2.1.4常见操作

// 查询操作

bool query(const T &val)
{BSTNode *p = root;bool flag = false;while (p != nullptr){if (val > p->data){p = p->right;}else if (val < p->data){p = p->left;}else{flag = true;break;}}return flag;
}
//递归查询
bool query02(BSTNode *node, const T &val)
{if (node == nullptr)return false;if (node->data > val)return query02(node->left);else if (node->data < val)return query02(node->right);elsereturn true;
}

//二叉树的镜像

void mirror()
{mirror(root);
}
void mirror(BSTNode* root)
{if (root == nullptr)return;BSTNode* p = root->left;root->left = root->right;root->right = p;mirror(root->left);mirror(root->right);
}

//判断当前二叉树是否是一颗二叉搜索树

void IsBSTree()
{cout << isBSTree(root) << endl;
}
//判断是否是二叉搜索树
bool isBSTree(BSTNode* root)
{if (root == nullptr)return true;if (!isBSTree(root->left))    //L{return false;}if (tmp != nullptr)           //V{if (tmp->data > root->data){return false;}}tmp = root;return isBSTree(root->right);  //R//错误的代码:只满足做小于中,中小于右并不是一个BST树,有可能node的后继节点比node的父节点(根节点)还大//因此需要时刻保持当前树的所有左节点或者根节点小于根节点或右节点//left->data<root->data   &&  root->data<right->data//可以利用中序遍历的特点,遍历过的节点肯定小于 没遍历的节点,如果违反就不是BST树//即拿一个tmp记录刚刚遍历的节点,在进行下一次判断时,避免根节点的前驱比根还大//if (root == nullptr)//    return;//if (root->left != nullptr&&root->data > root->right->data)//{//    return false;//}//else if(root->right != nullptr&&root->left->data > root->data)//{//   return false;//}//return isBSTree(root->left) && isBSTree(root->right);
}

//找出指定区间的元素

void findValue(vector<T> &vec,int i, int j)
{findValue(root, vec, i, j);
}
//寻找i与j之间元素放在vec中
void findValue(BSTNode* root, vector<T>& vec, int i, int j)
{if (root == nullptr)return;if (root->data > i)  //>i才往下走   //L{findValue(root->left, vec, i, j);}if (root->data >= i && root->data <= j)      //V{vec.push_back(root->data);}if (root->data < j)  //<j才往下走   //R{findValue(root->right, vec, i, j);}
}

//判断一颗BST树是不是平衡树,每一个左右子树的高度差不超过1

bool isBalancedTree()
{int level = 0;bool result = true;isBalancedTree(root, level,result);return result;
}
//判断是否是平衡二叉搜索树
int isBalancedTree(BSTNode* root, int level, bool& result)
{//*把节点的层数返回   level  同时计算层数,不用使用high()多次递归调用*if (root == nullptr){return level;}int left = isBalancedTree(root->left, level + 1, result);if (!result)return left;int right = isBalancedTree(root->right, level + 1, result);if (!result)return right;if (abs(left - right) > 1){result = false;}return max(left, right);
}

//找出两个元素的LCA公共祖先节点并返回

//===>不需要遍历整棵树,
//而是从根节点沿一条路从上往下走
int getLCA(int a, int b)
{if (!query(a) || !(query(b))){throw "";}int min = a;int max = b;if (a > b){min = b;max = a;}return getLCA(root, min, max);
}
//寻找a与b之间的数
int getLCA(BSTNode* root, int a, int b)
{if (root == nullptr){throw "no LCA";}//root大于a并且大于b   往左走   if (root->data > a && root->data > b){return getLCA(root->left, a, b);}//root小于a并且小于b   往右走else if (root->data < a && root->data < b){return getLCA(root->right, a, b);}//root>a并且root<b 找到了!!else{return root->data;}
}

//判断参数传入的二叉树是否是当前二叉树的一颗子树

bool isChildTree(BSTree<T> tree)
{if (!query(tree.root->data)){return false;}BSTNode *p = root;while (p != nullptr){if (p->data < tree.root->data){p = p->right;}else if (p->data > tree.root->data){p = p->left;}else{break;}}if (p == nullptr)return false;return isChildTree(p, tree.root);
}
//判断是否是子树
bool isChildTree(BSTNode* node1, BSTNode* node2)
{if (node1 == nullptr && node2 == nullptr)return true;if (node1 == nullptr)return false;if (node2 == nullptr)return true;if (node1->data != node1->data)return false;return isChildTree(node1->left, node2->left)&& isChildTree(node1->right, node2->right);
}

//找第K小的元素值

//中序遍历 《=====》
//找第K大的元素值, 只需要在中序遍历时从右节点往左节点走
//  (先走右root->right)  不用计算Number()-k+1  免去Number()的递归
int getTopKVlaue(int k)
{int i = 0;BSTNode *node = getTopKVlaue(root, k,i);if (node == nullptr){throw"no node!";}else{return node->data;}
}
//寻找第K小节点
BSTNode* getTopKVlaue(BSTNode* root, int k, int& t)
{if (root == nullptr)return nullptr;BSTNode* node1 = getTopKVlaue(root->left, k, t);if (node1 != nullptr)return node1;if (++t == k){return root;}return getTopKVlaue(root->right, k, t);
}

(一)BST树(二叉搜索树)相关推荐

  1. 【Java数据结构】BST树(二叉搜索树)总结03(求BST树高度,求BST树节点个数)

    二叉树总结:入口 二叉树的基本操作: 1.插入,删除 操作 2.前.中.后序遍历,层序遍历 3.求BST树高度,求BST树节点个数 4.返回中序遍历第k个节点的值 5.判断一个二叉树是否是BST树,判 ...

  2. C++数据结构与算法 竞赛树, 二叉搜索树

    竞赛树: tournament tree, 也是以可完全二叉树,所以使用数组描述效率最好,竞赛树的基本操作是替换最大(小)元素 赢者树和输者树: 为了便于实现,需要将赢者树限制为完全二叉树.n个参与者 ...

  3. BST(二叉搜索树)

    BST(二叉搜索树) 所谓二叉搜索树(Binary Search Tree,简称 BST)大家应该都不陌生,它是一种特殊的二叉树. 特殊在哪里呢? 简单来说就是:左小右大 BST的完整定义如下: BS ...

  4. 树、二叉树、二叉搜索树_检查二叉树是否为BST(二叉搜索树)

    树.二叉树.二叉搜索树 Description: 描述: This article describes how to check whether a given tree is BST or not? ...

  5. ( “树” 之 BST) 235. 二叉搜索树的最近公共祖先 ——【Leetcode每日一题】

    二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点. 二叉查找树中序遍历有序. 235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. ...

  6. 2.13_binary_search_tree(BST)_二叉搜索树

    二叉树节点类 class BiTreeNode(object):"""二叉树节点类"""def __init__(self, data):s ...

  7. LeetCode 653. 两数之和 IV - 输入 BST(二叉搜索树迭代器双指针)

    文章目录 1. 题目 2. 解题 1. 题目 给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true. 案例 1: 输入: 5/ \3 6/ \ ...

  8. 树——二叉搜索树的实现

    1.定义 它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值 均大于它的根结点的值: 它的左.右子 ...

  9. 《恋上数据结构第1季》平衡二叉搜索树、AVL树

    AVL树 二叉搜索树缺点分析 改进二叉搜索树 平衡(Balance) 理想平衡 如何改进二叉搜索树? 平衡二叉搜索树(Balanced Binary Search Tree) AVL树 BST 对比 ...

  10. 深入理解二叉搜索树(BST)

    一棵二叉搜索树(BST)是以一棵二叉树来组织的,可以用链表数据结构来表示,其中,每一个结点就是一个对象,一般地,包含数据内容key和指向孩子(也可能是父母)的指针属性.如果某个孩子结点不存在,其指针属 ...

最新文章

  1. android studio 图片的命名规则
  2. 下一站,向冠军冲击!
  3. Jenkins 持续集成 概念(学习笔记二十六)
  4. 几种网站后门排查 不全面
  5. oracle通过sid远程登录,oracle怎么捕获用户登录信息,如SID,IP地址等
  6. oracle 字段以逗号结尾的更新 数据库_Oracle数据库某个字段的值为逗号分隔的多个值组成的字符串,以一个多选的下拉框进行查询...
  7. 《Reids 设计与实现》第十八章 事务
  8. 例14:计算某日是该年的第几天
  9. 美男子的JavaScript笔记,望能助君留住秀发
  10. 作词家下岗系列:教你用 AI 做一个写歌词的软件!
  11. Detached InstanceError:Instance is not bound to a Session 关闭session后使用SQLAlchemy对象
  12. 博士申请 | 南方科技大学NCC Lab刘泉影教授招收深度学习方向全奖博士生
  13. Nordic 52832工程报错undefined reference to `__start_pwr_mgmt_data'解决办法
  14. 【待更新】【Rockchip】瑞芯微/rockchip 开发环境搭建|编译|烧录 开发实例
  15. 怎样用计算机弹出声音,电脑没声音怎么调出来
  16. 图片如何转文字?三款良心软件,太好用了!
  17. 【Python爬虫】| XPANX获取抖音APP小视屏
  18. 大数据时代:我们的邮件被谁偷看了?
  19. 煤炭价格预测:基于matlab的时间序列分析(主要流程+完整代码)
  20. [PB] PB中读写文件通用的两个函数

热门文章

  1. linux下制作windows启动盘,行家里手:Linux使用WoeUSB制作Windows启动盘
  2. Fiddler 微信小程序抓包
  3. 模糊查询的hql语句_hql语句拼接模糊查询
  4. swoft2 小白教程系列-进程篇
  5. 马尔可夫决策过程(一)
  6. 快速分析德邦快递走件信息,并筛选代收的单号
  7. 基于强化学习的自我完善聊天机器人
  8. C语言用链表实现管理系统
  9. 【微信小程序】一文带你吃透小程序开发框架——视图层中的事件系统
  10. Python实现文字转语音