二叉搜索树

铺垫知识:

0.0 二叉树的遍历

在这里插入图片描述
在这里插入图片描述

遍历分: 前序遍历、中序遍历、后续遍历

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

整体代码:

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	BTDataType data;
}BTNode;

BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode)); //创建了一个指针变量node
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	node->data = x;
	node->left = node->right = NULL;
	return node;
}

BTNode* CreatBinaryTree()
{
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');

	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;

	return nodeA;
}

// 二叉树前序遍历 
void PreOrder(BTNode* root)
{
	if (root == NULL){
		printf("NULL ");
		return;
	}

	printf("%c ", root->data);  //根
	PreOrder(root->left);       //左子树
	PreOrder(root->right);      //右子树
}

// 二叉树中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL){
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%C ", root->data);
	InOrder(root->right);
}

int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
}

0.1 前序遍历

代码部分

// 二叉树前序遍历 
void PreOrder(BTNode* root)
{
	if (root == NULL){
		printf("NULL ");
		return;  //return是回到调用我的地方
	}

	printf("%c ", root->data);  //根
	PreOrder(root->left);       //左子树
	PreOrder(root->right);      //右子树
}

流程图:
在这里插入图片描述

0.2 中序

代码部分:

void InOrder(BTNode* root)
{
	if (root == NULL){
		printf("NULL ");
		return;
	}

	InOrder(root->left);       //左子树
	printf("%C ", root->data); //根
	InOrder(root->right);      //右子树
}

流程图:
在这里插入图片描述

0.3 后序

作用:搜索查找一个数,最多查找高度次O(N)
概念:(二叉排序树)或者是一棵空树
在这里插入图片描述
int a[] ={5,3,4,1,7,8,2,6,0,9}
性质如下:

  1. 若左子树不为空,则左子树的所有节点 < 根节点的值
  2. 若右子树不为空,则右子树所有节点 > 根节点的值
  3. 左右子树也分别为二叉搜索树
    整体代码:
    BinarySerchTree.h
#pragma once

template<class K> //创建函数模板。k可以是任何类型
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

template<class K>
struct BSTree
{
	typedef BSTreeNode<K>  Node; //将BSTreeNode<K>重命名为Node
public:
	BSTree()
		:_root(nullptr)
	{}

	bool Insert(const K& key)
	{
		if (_root == nullptr) //空树
		{
			_root = new Node(key); //new int(10)动态申请一个int类型的空间并初始化为10
			return true;           //在申请自定义类型的空间时,new会调用构造函数
		}
		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_key < key)  //比你大,往右走
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else //相等则说明,值已经存在
			{
				return false;
			}
		}

		cur = new Node(key);
		//链接在左边还是右边,再进行一次判断
		if (parent->_key < 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);
	}
	//***************************
	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		return false;
	}
private:
	Node* _root;


};

void TestBSTree()
{
	BSTree<int> t;
	int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	//t._Inorder();//无法调用,调用需要根,但根私有,拿不到
	//解决方案,写一个子函数
	t.Inorder();
}

main.c

#include <iostream>
#include "BinarySerchTree.h"
using namespace std;
int main()
{
	TestBSTree();
	return 0;
}

1.0 插入

默认情况下不允许冗余,插入5(5本身已经存在,)故插入会返回FALSE。

  1. 空树 直接插入
  2. 非空树

在这里插入图片描述

bool Insert(const K& key)
	{
		if (_root == nullptr) //空树
		{
			_root = new Node(key);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_key < key)  //比你大,往右走
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else //相等则说明,值已经存在
			{
				return false;
			}
		}

		cur = new Node(key);
		//链接在左边还是右边,再进行一次判断
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;
	}

2.0 走中序 (排序+去重)

//**********解决方法*********
	void Inorder()
	{
		_Inorder(_root);
	}
	void _Inorder(Node* root) //中序
	{
		if (root == nullptr) //空树直接返回
		{
			return;
		}

		_Inorder(root->_left);  //先走左,再走根,后走右
		cout << root->_key << " ";
		_Inorder(root->_right);
	}

3.0 查找

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

4.0 删除

1.没有孩子的节点
2.只有一个孩子的节点 (1.2合成一个情况,直接删除)
3.有两个孩子的节点 (替换法删除,左子树的最大的节点或者右子树的最小节点,进行替换 )

第一种情况:直接删除
删除8:左为空(被删除),让父亲的右指向我的右
删除1:左为空,让父亲的左指向我的右
在这里插入图片描述

在这里插入图片描述

讨论父亲左为空的情况,1)父亲的右指向我的右,2)父亲的左指向我的

左为空,如果父亲被干掉,则让右树为新的根。同理,右也相同。
if (parent == nullptr)
{
_root = cur->_right;
}
在这里插入图片描述

父亲是空情况(只有一个节点)左为空,则右孩子的根则为新的根,
右为空,则左孩子的根为新的根

             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) //如果我的右为空
				{
					if (parent == nullptr) //要对父亲为空进行判断
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
							parent->_left = cur->_left;
						else
							parent->_right = cur->_left;
					}

					delete cur;
				}

左右都不为空

                else                             //左右都不为空
				{
					Node* minParent = cur;       //找右树的最左节点
					Node* min = cur->_right;
					while (min->_left)
					{
						minParent = min;        //min往下走,min给parent
						min = min->_left;
					}

					cur->_key = min->_key;             //把min的值覆盖过去,然后要把min删除(需要找min的父亲)

					if (minParent->_left == min)       //minParent有左树
						minParent->_left = min->_right; //替换法删除(minParent的左指向他的右)这样就把min删除了
					else
						minParent->_right = min->_right; //minParent的右指向min的右

					delete min;               //删除min
				}

假设删除5 替换法,找右树的最左节点
用6去覆盖5,然后在删除7的左树6
删除的话,找右树的最左节点。
在这里插入图片描述

假设删除7。第二种情况:右子树的根即为最左节点(无最左节点)
在这里插入图片描述

                else                             //左右都不为空
				{
					Node* minParent = cur;      
					Node* min = cur->_right;    //找右树的最左节点
					while (min->_left)          
					{
						minParent = min;        //min往下走,min给parent
						min = min->_left;       //找到了右树的最左节点
					}

					cur->_key = min->_key;             //把min的值覆盖过去,然后要把min删除(需要找min的父亲)

					//下面为删除min
					if (minParent->_left == min)       //minParent有左树
						minParent->_left = min->_right; //替换法删除(minParent的左指向他的右)这样就把min删除了
					else
						minParent->_right = min->_right; //minParent的右指向min的右

					delete min;               //删除min
				}

5.0 递归查找

Node* _FindR(Node* root, const K& key)
	{
		if (root == nullptr)
		{
			return nullptr;
		}

		if (root->_key < key)  
		{
			return _FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left, key);
		}
		else
		{
			return root;
		}
	}

6.0 插入

bool _InsertR(Node*& root, const K& key)   //注意此处的Node*& root,引用
	{
		if (root == nullptr)
		{
			root = new Node(key);         //*********
			return true;
		}

		if (root->_key < key)                    //大,则递归去右树去查找
			return _InsertR(root->_right, key);
		else if (root->_key > key)               
			return _InsertR(root->_left, key);
		else                                    //相等,不允许插入
			return false;
	}

在这里插入图片描述

3往下递归,root的值是空,但同时是3的节点的右指针的别名

7.0 搜索树的应用

  1. 搜索 key搜索模型 key/value搜索模型
  2. 排序+去重

搜索-> 在不在

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值