C++实现二叉搜索树

5 篇文章 0 订阅
4 篇文章 0 订阅

一、 概念

    1.  性质

       二叉搜索树是数据结构中树形结构一种。它或者是一棵空树,或者具有以下特性:

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

       下图1 为一个搜索二叉树:

图1 二叉搜索树实例

         对这个二叉树进行中序遍历的结果为:0 1 2 3 4 5 6 7 8 9

   2. 结构

         二叉搜索树是能够高效地进行插入、删除、查询的数据结构,其时间复杂度为O(logn).

   3. 操作 

         搜索二叉树需要的实现的操作有,查找、删除、插入、构造函数、拷贝构造函数、析构函数、赋值运算符重载。

 

二、 操作解析

    1. 查找

        根据搜索二叉树的特性,左子树节点的值<根节点的值<右子树节点的值即可实现二叉树的查找。其查找过程为:

                        若根节点不为空:

                               如果根节点val == 目标值 , 返回真;

                               如果根节点val < 目标值, 在其右子树找;

                               如果根节点val > 目标值,在其左子树找;

                         否则返回假

    2. 插入

       说明一下,搜索二叉树的插入位置都是度为0或1的节点。即使插入的数字距离度为0或1的距离远,经过调整之后任然插入的是度为0或1的节点。插入可有以下情况及其过程:

        1> 树为空,则直接插入

        2> 树不为空,首先进行二叉树的搜索操作,找到插入位置,以及插入位置的父亲节点。记录父亲节点的目的是为了方便判断插入左子树还是右子树。

   3. 删除

        首先查找要删除的数据是否在二叉树中,如果不存在,返回flase;否则删除的节点可能存在以下四种情况:

            1> 要删除的节点为叶子节点。

                   找到节点进行删除即可。

            2> 要删除的节点度为1,即只有孩子,或者只有有孩子。

                   以下图2 删除 8 节点为例:只是其中一例,有左孩子的情况在代码中进行实现。

图2 示例

 

            3> 要删除的节点度为2,即左右孩子都存在。

                  以删除以下图3 中7为例:

图3 示例

         其删除的过程为:

               a. 找到次删除节点的子树中左子树的最右节点或者右子树的最左节点

               b. 将删除的节点值替换成 a 中找到的节点值

               c. 删除 a 中找到的节点。(该步即为删除度为1或0的节点)

         具体的是实现在以下代码中进行展示。

4. 构造函数、 析构函数、赋值运算符重载的操作和普通类的显示函数的操作类似,在下面代码中进行展示。

 

三、 C++代码实现

#include <iostream>
using namespace std;

// 创建一个节点
template <class T>

struct BSTNode {
	T _val;
	BSTNode<T>* _left;
	BSTNode<T>* _right;

	// 构造
	BSTNode (const T& val = T())
		:_val(val)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template <class T>
class BSTree {
public:
	typedef BSTNode<T> Node;

	// 查找函数
	Node* find(const T& val) {
		Node* cur = _root;
		while (cur) {
			if (cur->_val == val) {
				return cur;
			}
			if (cur->_val < val) {
				cur = cur->_right;
			}
			if (cur->_val > val) {
				cur = cur->_left;
			}
		}
		return nullptr;  // 未找到,返回空
	}

	// 插入。 只能插入到度为 0 或 1 的节点中,只有插入是否成功,所有用 bool 数据类型. 
	bool insert(const T& val) {
		// 判断是否为空树, 是空树,则用 val 创建(node)一个树
		if (_root == nullptr) {
			_root = new Node(val);
		}
		Node* cur = _root;
		Node* parent = nullptr;
		// 先查找
		while (cur) {
			parent = cur;
			if (cur->_val == val) {
				return false;
			}
			else if (cur->_val < val) {
				cur = cur->_right;
			}
			else {
				cur = cur->_left;
			}
		}
		// while 走完了,没有 val 节点,就创建一个吧
		cur = new Node(val);
		// 判断 cur 应该放在 parent 的那一边
		if (parent->_val < val) {
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		return true;
	}

	bool erase(const T& val) {
	// 删除
	// 四种情况 叶子节点 左孩子为空  右孩子为空  度为 2 的节点
	//1. 删除叶子节点
	// 先查找
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
			if (cur->_val == val) {
				break;
			}
			if (cur->_val < val) {
				parent = cur;
				cur = cur->_right;
			}
			if (cur->_val > val) {
				parent = cur;
				cur = cur->_left;
			}
		}
		if (cur == nullptr) {  // 没找到要删除的节点
			return false;
		}
		// 如果 cur 是叶子节点
		if (cur->_left == nullptr && cur->_left == nullptr) {
			if (cur == _root) {
				_root = nullptr;
			}
			else {
				if (parent->_right == cur) {
					parent->_right = nullptr;
				}
				else {
					parent->_left = nullptr;
				}
				
			}
			delete cur; // 释放 cur, 以防内存泄漏 
		}
		// 左孩子为空
		else if (cur->_left == nullptr) {
			if (cur == _root) {
				_root = cur->_right;
			}
			else {
				if (parent->_right == cur) {
					parent->_right = cur->_right;
				}
				else if (parent->_left == cur) {
					parent->_left = cur->_right;
				}
			}
			delete cur;
		}
		// 右孩子为空
		else if (cur->_right == nullptr) {
			if (cur == _root) {
				_root = cur->_left;
			}
			else {
				if (parent->_left == cur) {
					parent->_left = cur->_left;
				}
				else {
					parent->_right = cur->_left;
				}
			}
			delete cur;
		}
		// 左右孩子都存在
		else {
			Node* leftMostChid = cur->_right;
			Node* parent = cur;
			while (leftMostChid->_left) {
				parent = leftMostChid;
				leftMostChid = leftMostChid->_left;
			}
			cur->_val = leftMostChid->_val;
			// 判断最左值在父亲的左边还是右边,  有nullptr那就接nullptr
			if (parent->_left = cur) {
				parent->_left = leftMostChid->_right;
			}
			else {
				parent->_right = leftMostChid->_right;
			}
			delete leftMostChid;
		}
		return true;
	}
	// 中序遍历显示结果
	void inorder() {
		_inorder(_root);
		cout << endl;
	}
	void _inorder(Node* root) {
		if (root) {
			_inorder(root->_left);
			cout << root->_val << ' ';
			_inorder(root->_right);
		}
	}

	// 默认构造
	BSTree() :_root(nullptr) {};

	// 拷贝构造
	Node* copyTree(Node* root) {
		if (root) {
			Node* cur = new Node(root->_val);
			cur->_left = copyTree(root->_left);
			cur->_right = copyTree(root->_right);
			return cur;
		}
		return nullptr;
	}

	BSTree(const BSTree<T>& bst):_root(nullptr){
		_root = copyTree(bst._root);
	}

	//析构函数
	void destory(Node* root) {
		if (root) {
			destory(root->_left);
			destory(root->_right);
			delete root;
		}
	}
	~BSTree() {
		destory(_root);
	}
	// 赋值运算符
	BSTree<T>& operator=(BSTree<T> bst)
	{
		swap(_root, bst._root);
		return *this;
	}

private:
	Node* _root = nullptr;
};

void test() {
	BSTree <int> bst;
	bst.insert(0);
	bst.insert(5);
	bst.insert(6);
	bst.insert(3);
	bst.insert(7);
	bst.insert(1);
	bst.inorder();
	// 拷贝
	BSTree<int> p(bst); 
	p.inorder();
	// 删除
	bst.erase(3);
	bst.inorder();
	bst.erase(6);
	bst.inorder();
}

int main() {
	test();
	system("pause");
	return 0;
}

其运行结果如下:

0 1 3 5 6 7
0 1 3 5 6 7
0 1 5 6 7
0 1 5
请按任意键继续. . .

结果证明所述功能已经实现。

代码只是实现本文的所述功能,当然还有更优化的代码;如果该代码有错误,也请同志们多多指教,多多交流

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值