二叉搜索树

一.二叉搜索树

1.性质

二叉搜索树或是一颗空树,或者满足以下性质

1)若左子树不为空,则左子树上所有节点的值小于等于根节点的值

2)若右子树不为空,则右子树上所有节点的值大于等于根节点的值

3)左右子树都是二叉搜索树

注:二叉搜索树可以插入相同的值,也可以插入不同的值,需要根据使用场景来定

2.时间复杂度:O(N)

最优情况下:O(logN),最坏情况下:O(N)

二.二叉搜索树的模拟实现

注:小编在这里实现的是插入不同的值

1.使用场景1:key

1)节点创建

template<class K>
struct BSTNode
{
	K _key;
	BSTNode<K>* _left;
	BSTNode<K>* _right;

	//初始化
	BSTNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

2)插入

(1)若树为空,直接将新节点做为根节点

(2)若树不为空,key>当前节点的值向右走;key<当前节点的值向左走;当找到合适的空位置后,插入。注意此过程需要用一个指针来标记当前节点的父节点,以保证最后插入时可以知道插在哪里

(3)代码实现

bool insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
	}

	Node* cur = _root;
	Node* parent = cur;//记录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;
		}
	}
	//判断新节点该插在父节点的左侧还是右侧
	if (parent->_key < key)
	{
		parent->_right = new Node(key);
	}
	else
	{
		parent->_left = new Node(key);
	}
	return true;

}

3)遍历

(1)采用中序遍历的形式,二叉搜索树遍历完将会是升序数组的形式

(2)代码实现

public:	
    void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr) return;

		_InOrder(root->_left);
		cout << root->_key <<" ";
		_InOrder(root->_right);
	}

4)查找

bool Find(const K& key)
{
	if (_root == nullptr) return false;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)
		{
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			cur = cur->_right;
		}
		else
		{
			return true;
		}
	}
	return false;
}

5)删除

(1)判断待删除元素是否在二叉搜索树内,若不存在直接返回false,若是存在进行下面的操作

(2)若是待删除节点N左右孩子均为空,将N节点的父节点对应的孩子指针置为空,直接删除N节点

(3)若是待删除节点N左孩子为空,让N节点的父节点对应的孩子指针指向N的右孩子,直接删除N节点

(4)若是待删除节点N右孩子为空,让N节点的父节点对应的孩子指针指向N的左孩子,直接删除N节点

(5)若是待删除节点N左右孩子均不为空,找一个替代节点(右子树中的最左节点或是左子树的最右节点)放在N的位置,直接删除N节点

(6)代码实现

bool Erase(const K& key)
{
	if (!Find(key)) return false;

	Node* cur = _root;
	Node* parent = cur;//记录cur的父节点位置
	while (cur)//找出要出入的位置的父节点
	{
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			if(cur->_left == nullptr)//左为空
			{
				//确定cur是parent的左孩子还是右孩子
				if (cur->_key > parent->_key) parent->_right = cur->_right;
				else parent->_left = cur->_right;
				delete cur;
			}
			else if(cur->_right == nullptr)//右为空
			{
				//确定cur是parent的左孩子还是右孩子
				if (cur->_key > parent->_key) parent->_right = cur->_left;
				else parent->_left = cur->_left;
				delete cur;
			}
			else//左右均不为空
			{
				//找一个替代节点
				Node* replace = cur->_right;
				Node* replaceparent = cur;
				while (replace->_left)
				{
					replaceparent = replace;
					replace = replace->_left;
				}
				cur->_key = replace->_key;
				//删除replace指向的节点
				//判断代替指针是他父亲的左孩子还是右孩子
				if (replaceparent->_right==replace)
				{
					replaceparent->_right = replace->_right;
				}
				else
				{
					replaceparent->_left = replace->_right;
				}
				
				delete replace;
			}
			return true;
		}
	}
	return false;
}

6)拷贝构造:利用前序遍历的思想

BSTree(const BSTree<K>& t)
{
	_root = copy(t._root);
}

Node* copy(Node* root)
{
	if (root == nullptr) return nullptr;

	Node* newroot = new Node(root->_key);
	newroot->_left = copy(root->_left);
	newroot->_right = copy(root->_right);

	return newroot;

}

7)析构:采用后序遍历的方法逐个删除节点,以保证不会出现找不到孩子的情况

~BSTree()
{
	destory(_root);
	_root = nullptr;
}

void destory(Node* root)
{
	if (root == nullptr) return;

	destory(root->_left);
	destory(root->_right);
	delete root;

}

8)构造

//强制构造
BSTree() = default;

9) 赋值

BSTree<K>& opreator = (const BSTree<K>&t)
{
	swap(_root,t._root);
    return *this;
}

2.使用场景2:key/value

1)使用规则:

仍是根据key的大小来查,增,删节点,key的值不可改动,但是value的值可以改动

注:此情况下的二叉搜索树与key场景下的二叉搜索树大体相同,只有部分细节不同,下面小编之就不同部分给出说明

2)节点

template<class K,class V>
struct BSTNode
{
	K _key;
	V _val;
	BSTNode<K,V>* _left;
	BSTNode<K,V>* _right;

	//初始化
	BSTNode(const K& key,const V& val)
		:_key(key)
		,_val(val)
		, _left(nullptr)
		, _right(nullptr)
	{}
};

3)插入

bool Insert(const K& key,const V& val)
{
	if (_root == nullptr)
	{
		_root = new Node(key,val);
	}

	Node* cur = _root;
	Node* parent = cur;//记录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;
		}
	}
	//判断新节点该插在父节点的左侧还是右侧
	if (parent->_key < key)
	{
		parent->_right = new Node(key,val);
	}
	else
	{
		parent->_left = new Node(key,val);
	}
	return true;

}

4)遍历数组

void _InOrder(Node* root)
{
	if (root == nullptr) return;

	_InOrder(root->_left);
	cout << root->_key << ":"<<root->_val<<" ";
	_InOrder(root->_right);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值