C++初阶学习————二叉树进阶(二叉搜索树)-创新互联
- 二叉搜索树的概念
- 二叉搜索树的操作
- 基本框架
- 二叉搜索树的插入
- 二叉搜索树的查找
- 二叉搜索树的删除
- 整体代码
- 循环写法
- 递归写法
- 二叉搜索树的应用
- 二叉搜索树的性能分析
前面的文章介绍过二叉树的基础概念以及完全二叉树的应用等等,本篇将介绍二叉搜索树创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站建设、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的城中网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!二叉搜索树的概念
二叉搜索树是一颗排序树,或者是一颗空树如图:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树,并且它可以去重
templatestruct BSTreeNode
{BSTreeNode* _left;
BSTreeNode* _right;
T _key;
BSTreeNode(const T& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
templateclass BSTree
{typedef BSTreeNodeNode;
public:
BSTree()
:_root(nullptr)
{}
private:
Node* _root;
};
二叉搜索树的插入例如插入6和10
bool insert(const T& key)
{if (_root == nullptr)
{_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = cur;
while (cur)
{if (cur->_key >key)
{ parent = cur;
cur = cur->_left;
}
else if (cur->_key< key)
{ parent = cur;
cur = cur->_right;
}
else
return false;
}
cur = new Node(key);
if (parent->_key >key)
parent->_left = cur;
else
parent->_right = cur;
return true;
}
注意:会有树为空的情况需要判断
二叉搜索树的查找查找的代码和插入非常类似,大于根去右边,小于根去左边,最多查找高度次
代码如下:
//Node* find(const T& key)
bool find(const T& key)
{if (_root == nullptr)
return false;
//return nullptr;
Node* cur = _root;
while (cur)
{if (cur->_key >key)
cur = cur->_left;
else if (cur->_key< key)
cur = cur->_right;
else
return true;
//return cur;
}
return false;
//return nullptr;
}
二叉搜索树的删除这里的删除相对于插入和查找来说是比较难的,最重要的是不能改变二叉搜索树的结构,所以这里主要分为两种情况考虑
- 删除只有一个子节点或没有子节点
- 删除左右子节点都存在的
先找到要删除的节点,代码如下:
bool erase(const T& key)
{if (_root == nullptr)
return false;
Node* cur = _root;
Node* parent = cur;
while (cur)
{if (cur->_key >key)
{ parent = cur;
cur = cur->_left;
}
else if (cur->_key< key)
{ parent = cur;
cur = cur->_right;
}
else
{ 删除部分的代码........
}
}
return false;
}
1. 删除只有一个子节点或没有子节点
(1)删除叶子节点(删除9举例)
(2)删除只存在一个子节点的节点(删除8举例)
情况1的删除代码:
else
{if (cur->_left == nullptr)
{if (cur == parent->_left)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
else if (cur->_right == nullptr)
{if (cur == parent->_left)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
else
{情况2部分的代码.......}
delete cur;
}
除上述代码删除的示例外,另外三种情况
所以这里需要仔细处理链接,否则会破坏搜索树的结构
2.删除左右节点都存在的节点
(1)删除根节点
(2)删除其他存在左右子节点的节点
找到的右子树最小节点一定没有左节点,且它的父节点一定不为空;所以在交换或覆盖完数值后,剩下的步骤和情况一一样,并且这里不用单独判断删除的节点存在哪边的子节点(右子树的最小节点一定没有左节点,所以只可能存在右节点)
情况2的删除代码:
else
{Node* min_parent = cur;
Node* min = cur->_right;
while (min->_left)
{min_parent = min;
min = min->_left;
}
cur->_key = min->_key;
if (min == min_parent->_left)
min_parent->_left = min->_right;
else
min_parent->_right = min->_right;
swap(min,cur);
}
以上就是删除主要步骤,但还有个问题就是要考虑只有一个节点,需要操作根节点。
if (_root->_key == key )
{if (_root->_left == nullptr)
_root = _root->_right;
else
_root = _root->_left;
}
总体代码
bool erase(const T& key)
{if (_root == nullptr)
return false;
Node* cur = _root;
Node* parent = cur;
while (cur)
{if (cur->_key >key)
{ parent = cur;
cur = cur->_left;
}
else if (cur->_key< key)
{ parent = cur;
cur = cur->_right;
}
else
{ if (_root->_key == key )
{ if (_root->_left == nullptr)
_root = _root->_right;
else
_root = _root->_left;
}
else if (cur->_left == nullptr)
{ if (cur == parent->_left)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
else if (cur->_right == nullptr)
{ if (cur == parent->_left)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
else
{ Node* min_parent = cur;
Node* min = cur->_right;
while (min->_left)
{min_parent = min;
min = min->_left;
}
cur->_key = min->_key;
if (min == min_parent->_left)
min_parent->_left = min->_right;
else
min_parent->_right = min->_right;
swap(min,cur);
}
delete cur;
return true;
}
}
return false;
}
整体代码
循环写法bool insert(const T& key)
{if (_root == nullptr)
{ _root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = cur;
while (cur)
{ if (cur->_key >key)
{ parent = cur;
cur = cur->_left;
}
else if (cur->_key< key)
{ parent = cur;
cur = cur->_right;
}
else
return false;
}
cur = new Node(key);
if (parent->_key >key)
parent->_left = cur;
else
parent->_right = cur;
return true;
}
bool find(const T& key)
{if (_root == nullptr)
return false;
//return nullptr;
Node* cur = _root;
while (cur)
{ if (cur->_key >key)
cur = cur->_left;
else if (cur->_key< key)
cur = cur->_right;
else
return true;
//return cur;
}
return false;
//return nullptr;
}
bool erase(const T& key)
{if (_root == nullptr)
return false;
Node* cur = _root;
Node* parent = cur;
while (cur)
{ if (cur->_key >key)
{ parent = cur;
cur = cur->_left;
}
else if (cur->_key< key)
{ parent = cur;
cur = cur->_right;
}
else
{ if (_root->_key == key )
{if (_root->_left == nullptr)
_root = _root->_right;
else
_root = _root->_left;
}
else if (cur->_left == nullptr)
{if (cur == parent->_left)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
else if (cur->_right == nullptr)
{if (cur == parent->_left)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
else
{Node* min_parent = cur;
Node* min = cur->_right;
while (min->_left)
{min_parent = min;
min = min->_left;
}
cur->_key = min->_key;
if (min == min_parent->_left)
min_parent->_left = min->_right;
else
min_parent->_right = min->_right;
swap(min,cur);
}
delete cur;
return true;
}
}
return false;
}
//打印搜索树
void print_BSTree()
{_print_BSTree(_root);
cout<< endl;
}
void _print_BSTree(Node* root)
{if (root == nullptr)
return;
_print_BSTree(root->_left);
cout<< root->_key<< " ";
_print_BSTree(root->_right);
}
递归写法1.插入
bool insertR(const T& key)
{return insertR(_root,key)
}
bool _insertR(Node*& root,const T& key)
{if(root == nullptrd)
{root = new Node(key);
return true;
}
if(root->_key >key)
return insertR(root->_left,key);
else if(root->_key< key)
return insertR(root->_right,key);
else
return false;
}
注意,这里可以成功链接上,主要是运用了引用,递的是左右指针的引用
2.查找
Node* findR(const T& key)
{if()
}
bool _insertR(Node*& root, const T& key)
{if (root == nullptr)
{root = new Node(key);
return true;
}
if (root->_key >key)
return _insertR(root->_left, key);
else if (root->_key< key)
return _insertR(root->_right, key);
else
return false;
}
3.删除
bool EarseR(const K& key)
{return EarseR(_root,key);
}
bool _EraseR(Node*& root, const T& key)
{if (root == nullptr)
return false;
if (root->_key >key)
_EraseR(root->_left, key);
else if (root->_key< key)
_EraseR(root->_right, key);
else
{Node* era = root;
if (root->_left == nullptr)
root = root->_right;
else if (root->_right == nullptr)
root = root->_left;
else
{ Node* min = root->_right;
Node* min_parent = root;
while (min->_left)
{ min_parent = min;
min = min->_left;
}
swap(min->_key, root->_key);
return _EraseR(root->_right, key);
}
delete era;
return true;
}
}
这里讲一下删除的思路:
情况一时,操作和递归的插入基本一样,通过使用引用就可直接令要删除节点的父节点与子节点直接相连,也不用再去判断删除节点是父节点的左边还是右边
情况二时,先找到右子树最小节点,再与要删除的节点交换数值,最后递归传递的节点是,要删除节点的左子树(情况二再交换完数值后就可以按照情况一去处理了)
注意:要提前记录一下找到的要删除的节点,中间过程会改变root的值,所以最后删除的是提前记录好的节点
总体代码
bool insertR(const T& key)
{return _insertR(_root,key);
}
Node* findR(const T& key)
{return _findR(_root,key);
}
bool EraseR(const T& key)
{return _EraseR(_root, key);
}
private:
bool _insertR(Node*& root, const T& key)
{if (root == nullptr)
{ root = new Node(key);
return true;
}
if (root->_key >key)
return _insertR(root->_left, key);
else if (root->_key< key)
return _insertR(root->_right, key);
else
return false;
}
Node* _findR(Node* root, const T& key)
{if (root == nullptr)
return nullptr;
if (root->_key >key)
return _findR(root->_left, key);
else if (root->_key< key)
return _findR(root->_right, key);
else
return root;
}
bool _EraseR(Node*& root, const T& key)
{if (root == nullptr)
return false;
if (root->_key >key)
_EraseR(root->_left, key);
else if (root->_key< key)
_EraseR(root->_right, key);
else
{ Node* era = root;
if (root->_left == nullptr)
root = root->_right;
else if (root->_right == nullptr)
root = root->_left;
else
{ Node* min = root->_right;
Node* min_parent = root;
while (min->_left)
{min_parent = min;
min = min->_left;
}
swap(min->_key, root->_key);
return _EraseR(root->_right, key);
}
delete era;
return true;
}
}
二叉搜索树的应用- K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:- 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
- 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
- KV模型:每一个关键码key,都有与之对应的值Value,即
的键值对。该种方式在现实生活中非常常见: - 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文
就构成一种键值对; - 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是
就构成一种键值对
- 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文
c++提供了这种键值对儿的结构,叫做pair
pairp1("one",1);
p1.first就是key
p1.second就是value
make_pair("one",p1); 可以构造一个pair
二叉搜索树的性能分析插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
但是会有极端情况:当插入的值顺着一个方向一直插入(即插入的值一直是最小或大或者接近这种情况),那么二叉树就变成了单树,这时的查找效率就是极低了,即结点越深,则比较次数越多。
- 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N
- 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:
N
2
\frac{N}{2}
2N
当变为单树就失去意义了(查找元素相当于在顺序表中搜索元素,效率低下)。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网页题目:C++初阶学习————二叉树进阶(二叉搜索树)-创新互联
分享URL:http://ybzwz.com/article/ddjgeg.html