IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 搜索二叉树(Key-Value) -> 正文阅读

[数据结构与算法]搜索二叉树(Key-Value)

搜索二叉树保证了对于每个根节点,左子树上所有节点值小于根节点,右子树上,所有节点的值大于根节点。由此性质保证了二叉搜索树高效的查找效率,一棵优秀的二叉搜索树可达到搜索效率log(N)的级别,但搜索二叉树如果在创建时是以有序插入,那么它会退化为单链表,搜索二叉树的性质决定了其先序遍历结果一定是按照Key递增顺序。

节点结构如下

	template<class K,class V>
	class SBTreeNode{
	public:
		SBTreeNode():left(nullptr),right(nullptr),key(0),value(0){
		}
		SBTreeNode(K key = 0, V value = 0){
			this->key = key;
			this->value = value;
			this->left = nullptr;
			this->right = nullptr;
		}
		SBTreeNode<K, V>*left;
		SBTreeNode<K, V>*right;
		K key;
		V value;
	};

template<class K,class V>
	class SBTree{
	public:
		SBTree():root(nullptr){

		}
	private:
		SBTreeNode<K, V>*root;
	};

创建一个搜索二叉树:如果要加入的这个节点Key值大于目前根节点,则一定要插入到这个根节点的右子树上,如果要加入的Key值小于目前根节点,就要插入到这个根节点的左子树。如果相等,就不插入,当前根为空的时候就是这个Key值对应节点的位置。如果Key值等于当前根节点的Key值停止插入,达到去重目的。

		void insert( K k,V v=0){//二叉树的插入,由于要使用根,所以用子函数实现
			_insert(this->root, k,v);
		}
        bool _insert(SBTreeNode<K, V>*& root,K k,V v ){//插入子函数
			if (root == nullptr){//到了要插入的位置
				root = new SBTreeNode<K,V>(k, v);
				return true;
			}
			if (k > root->key){//往右子树插入
				return _insert(root->right, k, v);
			}
			else if (k < root->key){//往左子树插入
				return _insert(root->left, k, v);
			}
			else{
				return false;//遇到相同的Key返回失败结果
			}
		}

先序遍历:

		vector<V> InOrder(){//先序遍历,返回value组成的vector
			vector<V>v;
			_InOrder(this->root, v);
			return v;
		}
        void _InOrder(const SBTreeNode<K, V>*root,vector<V>&v){//前序遍历,顺便将value放入vector
			if (root == nullptr){
				return;
			}
			_InOrder(root->left,v);
			v.push_back(root->value);
			_InOrder(root->right,v);
		}

根据Key查找(返回相应节点地址):

SBTreeNode<K, V>* find(K key){//按Key查找
    return _find(root,key);
}
SBTreeNode<K, V>* _find(SBTreeNode<K,V>*root,K key)const{
    if (root == nullptr||root->key==key){//如果没找到返回空
        return root;
	}
	return key > root->key ? _find(root->right, key) : _find(root->left, key);//key大于根往右找,key小于根往左找
}

按Key删除节点:对于两个孩子节点中有至少一个为空的情况和两个孩子都为空的情况分开处理

假设当前节点叫做P

如果P的两个孩子都为空直接删除P即可

如果P的两个孩子中有一个为空,那么让P的父亲连接上P不为空的那个节点

如果P的两个孩子都不为空,我们采取覆盖删除,选取有孩子的最左节点Q(由于SBTree的性质,这个节点的Key一定是右孩子中最小的),将Q的value和Key与P交换,这是要删的节点就是右孩子的最左节点

void Delete(K key){
    _Delete(root, key);
}
void _Delete(SBTreeNode<K, V>*&root, K key){//删除key的节点
	if (root == nullptr){//如果走到空,说明没有树上没有这个节点,直接返回
		return;
	}
	if (root->key == key){
		if (root->left == nullptr || root->right == nullptr){//如果左右有一个是空
			SBTreeNode<K, V>*del=root;
			if (root->right != nullptr){//如果右子树不是空
				root = root->right;
			}
			else{
				root = root->left;//如果右子树为空而左子树不为空(指向不为空的节点)或左右子树都为空(指向空节点)
			}
			delete del;//直接删除当前节点
			return;
		}
		else{
			SBTreeNode<K, V>*cur=root->right;
			while (cur->left != nullptr){
				cur = cur->left;
			}
			swap(cur->value, root->value);
			swap(cur->key, root->key);//将key-value覆盖后递归到右孩子删除键为key的节点,这个节点此时一定满足上面的情况
			_Delete(root->right,cur->key);
		}
	}
	else if(root->key<key){
		_Delete(root->right, key);
	}
	else{
		_Delete(root->left, key);
	}
}

在删除时由于要修改父节点 left/right 指针的指向,还要判断当前这个节点是父亲的左孩子还是右孩子,所以要在递归函数中将父节点指针信息传递进来,较为麻烦,故这里使用了指针引用,对root的操作相当与直接对父节点指向孩子的指针进行操作。所以不需要额外传递父节点的信息。

完整代码如下:

#pragma once 
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
namespace ljc{
	template<class K,class V>
	class SBTreeNode{
	public:
		SBTreeNode():left(nullptr),right(nullptr),key(0),value(0){
		}
		~SBTreeNode(){
		}
		SBTreeNode(K key = 0, V value = 0){
			this->key = key;
			this->value = value;
			this->left = nullptr;
			this->right = nullptr;
		}
		SBTreeNode<K, V>*left;
		SBTreeNode<K, V>*right;
		K key;
		V value;
	};
	template<class K,class V>
	class SBTree{
	public:
		SBTree():root(nullptr){

		}
		void insert( K k,V v=0){//二叉树的插入,由于要使用根,所以用子函数实现
			_insert(this->root, k,v);
		}
		vector<V> InOrder(){//先序遍历,返回value组成的vector
			vector<V>v;
			_InOrder(this->root, v);
			return v;
		}
		SBTreeNode<K, V>* find(K key){//按Key查找
			return _find(root,key);
		}
		void Delete(K key){
			_Delete(root, key);
		}
	private:
		SBTreeNode<K, V>*root;
		bool _insert(SBTreeNode<K, V>*&root,K k,V v ){//插入子函数
			if (root == nullptr){//到了要插入的位置
				root = new SBTreeNode<K,V>(k, v);
				return true;
			}
			if (k > root->key){//往右子树插入
				return _insert(root->right, k, v);
			}
			else if (k < root->key){//往左子树插入
				return _insert(root->left, k, v);
			}
			else{
				return false;//遇到相同的Key返回失败结果
			}
		}
		void _InOrder(const SBTreeNode<K, V>*root,vector<V>&v){//前序遍历,顺便将value放入vector
			if (root == nullptr){
				return;
			}
			_InOrder(root->left,v);
			v.push_back(root->value);
			_InOrder(root->right,v);
		}
		SBTreeNode<K, V>* _find(SBTreeNode<K,V>*root,K key)const{
			if (root == nullptr||root->key==key){//如果没找到返回空
				return root;
			}
			return key > root->key ? _find(root->right, key) : _find(root->left, key);//key大于根往右找,key小于根往左找
		}
		void _Delete(SBTreeNode<K, V>*&root, K key){//删除key的节点
			if (root == nullptr){
				return;
			}
			if (root->key == key){
				if (root->left == nullptr || root->right == nullptr){
					SBTreeNode<K, V>*del=root;
					if (root->right != nullptr){
						root = root->right;
					}
					else{
						root = root->left;
					}
					delete del;
					return;
				}
				else{
					SBTreeNode<K, V>*cur=root->right;
					while (cur->left != nullptr){
						cur = cur->left;
					}
					swap(cur->value, root->value);
					swap(cur->key, root->key);
					_Delete(root->right,cur->key);
				}
			}
			else if(root->key<key){
				_Delete(root->right, key);
			}
			else{
				_Delete(root->left, key);
			}
		}
	};
}

由于技术局限,难免有不恰之处,如果发现有错误,还望指正。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 12:21:18  更:2022-04-29 12:22:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 7:33:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码