二叉搜索树分析以及实现(C++)

本文详细介绍了二叉搜索树的基本概念、性质及操作方法,包括查找、插入和删除等核心算法,并提供了C++实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

  • 二叉搜索树的定义:
    二叉搜索树(英语:Binary Search Tree),也称为二叉查找树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree)。一棵空树或者具有下列性质的二叉树就是二叉搜索树:

1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3. 任意节点的左、右子树也分别为二叉查找树;
4. 没有键值相等的节点。

  • 二叉搜索树的操作:

1. 查找

例如要查找 key 为 6 的结点,那么这棵二叉搜索树的根节点不为空,且根节点的 key 值为 8;大于要查找的 key 值, 则去树的左子树查找,此时当前结点的 key 值为 3, 小于要查找的结点的 key 值,则去以当前结点的右子树去查找,此时则找到了需要查找的 key 值结点。

二叉搜索树的查找方式就像是在一张一个方向对折了 N 次的纸张上面寻找某一块区域,此时这张纸被分为了 n 个小区域,也就是2 ^ N 次方个小区域,那么寻找某一块的情况平均就是求解这个 N 的次数,也就是 O(logN)。也就是查找的时间复杂度。

但是如果这颗二叉搜索树树退化的严重,那么它就相当于是链表,那么查找某个结点事件复杂度就变为了O(n)。

2. 插入结点

例如插入结点key值为16。此时的根节点不为空,且16大于根节点的key,则直接去右子树去继续寻找插入位置,此时比对发现任然大于结点key值10, 继续同样的操作,发现大于14,则插入14的右子树,并且返回true。

3. 删除

二叉搜索树的删除分了很多场景,因为删除完某一个结点之后,还要保持二叉搜索树的性质,所以二叉搜索树的删除比较繁琐。

删除的步骤:(难点)

(1)找

首先利用二叉搜索树的查找,找到要删除的结点。

(2)删

当删除的cur结点左子树为空时:

情景一:

当前要删除的结点就是这棵树的根节点,且当前结点的左子树为空,右子树不为空时。那么删除之后它的右子树直接变为根节点。

if (cur->_left == nullptr){
    if (parent == nullptr){
        _root = cur->_right;
	}
}
delete cur;

情景二:

当前要删除的结点非根节点,但仍然是左子树为空。

if (parent->_left == cur)
    parent->_left = cur->_right;
else
	parent->_right = cur->_right;

delete cur;

当删除的cur结点右子树为空时:

情景一:

当前要删除的结点就是这棵树的根节点,且当前结点的右子树为空,左子树不为空时。那么删除之后它的左子树直接变为根节点。

if (parent == nullptr){
    _root = cur->_left;
}

delete cur;

情景二:

当前要删除的结点非根节点,但仍然是右子树为空。

if (parent->_left == cur)
    parent->_left = cur->_left;
else
	parent->_right = cur->_left;

delete cur;

当要删除的结点左右结点都不为空时:

此时的情况,就是寻找右子树的最左边的那个结点,然后交换。因为此时的右子树都比cur大,但是右子树的最左子树结点是右子树中最小的,那么这么以交换刚好满足二叉搜索树的特征。最后将要删除的结点删掉。

其实这种情况也可以寻找左子树的最右节点,因为左子树比cur小,则左子树的最右节点大于任何的左子树的结点。最后将要删除的结点删掉。

{	// 如果左右都不为空,寻找cur的右子树的最左节点,与之交换,然后删除
    Node* tmp = cur->_right;
	Node* prev = cur;
	while (tmp->_left){
		prev = tmp;
		tmp = tmp->_left;
	}
	swap(cur->_key, tmp->_key);// 交换两个结点的值

	if (prev->_left == tmp)
		prev->_left = nullptr;
	else
		prev->_right = nullptr;

	delete tmp;
}

return true;

二叉搜索树的实现:

#pragma once

#include <iostream>
using namespace std;

template<class T>
struct SearchTreeNode{
	SearchTreeNode<T>* _left;
	SearchTreeNode<T>* _right;
	T _key;
	SearchTreeNode(const T& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

template<class T>
class BinarySearchTree{
	typedef SearchTreeNode<T> Node;

public:

	BinarySearchTree()
		:_root(nullptr)
	{}
	// 遍历二叉树
	void _PreOrder(Node* tree){
		if (tree == nullptr)
			return;
		cout << tree->_key << " ";
		_PreOrder(tree->_left);
		_PreOrder(tree->_right);
		
	}

	void preOrder(){
		_PreOrder(_root);
	}




	bool Insert(const T& x){
		if (_root == nullptr){
			_root = new Node(x);
			return true;
		}


		Node* cur = _root;
		Node* parent = nullptr;
		//寻找 x 应该插入的位置
		while (cur){
			parent = cur;
			if (x > cur->_key){
				cur = cur->_right;
			}
			else if (x < cur->_key){
				cur = cur->_left;
			}
			else{
				return false;
			}
		}

		// 与插入结点的父节比较点开始插入
		cur = new Node(x);
		if (x > parent->_key){
			parent->_right = cur;
		}
		else{
			parent->_left = cur;
		}

		return true;
	}


	void InOrder(){
		_InOrder(_root);
	}

	void _InOrder(Node* root){
		if (root == nullptr){
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

	const Node* Find(const T& x){
		// ??? 找的时候要不要判断空???
		if (_root == nullptr){
			return nullptr;
		}

		Node* cur = _root;
		while (cur){
			if (x > cur->_key){
				cur = cur->_right;
			}
			else if (x < cur->_key){
				cur = cur->_left;
			}
			else{
				return cur;
			}
		}

		return cur;
	}


	bool Remove(const T& x){
		Node* cur = _root;
		Node* parent = nullptr;

		while (cur){
			if (cur->_key > x){
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < x){
				parent = cur;
				cur = cur->_right;
			}
			else{
				if (cur->_left == nullptr){
					if (parent == nullptr){
						_root = cur->_right;
					}
					else{
						if (parent->_left == cur)
							parent->_left = cur->_right;
						else
							parent->_right = cur->_right;
					}
					delete cur;
				}
				else if (cur->_right == nullptr){	// 如果cur的右孩子为空
					if (parent == nullptr){
						_root = cur->_left;
					}
					else{
						if (parent->_left == cur)
							parent->_left = cur->_left;
						else
							parent->_right = cur->_left;
					}
					delete cur;
				}
				else{	// 如果左右都不为空,寻找cur的右子树的最左节点,与之交换
					Node* tmp = cur->_right;
					Node* prev = cur;
					while (tmp->_left){
						prev = tmp;
						tmp = tmp->_left;
					}
					swap(cur->_key, tmp->_key);// 交换两个结点的值

					if (prev->_left == tmp)
						prev->_left = nullptr;
					else
						prev->_right = nullptr;

					delete tmp;
				}
				return true;
			}
		}
		return false;
	}

private:
	Node* _root;
};


//测试用例
void Test(){
	BinarySearchTree<int> tree;
	tree.Insert(1);
	tree.Insert(5);
	tree.Insert(7);
	tree.Insert(3);
	tree.Insert(2);

	/*tree.Remove(1);
	tree.Remove(5);
	tree.Remove(7);
	tree.Remove(3);
	tree.Remove(2);
*/
	tree.InOrder();
	//tree.preOrder();

}

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值