二叉搜索树

小学期学的算法和做OJ题的各种经验总结

二叉搜索树:(二分查找)

特征:

保证每个节点的左子树所有点都比自己小,右子树所有点都比自己大。

使用:

  1. 查找某个数是否在集合中:从根出发,比当前结点小就找左儿子,否则找右儿子。
  2. 插入:查找应该放的位置,直到这个位置是空的,放入。
  3. 删除:
    谁来填这个点的位置呢?
    1. 首先,新值应该还要维持与父节点的大小关系,所以只能从父节点的单侧子树(即图中红框)中找,
    2. 然后看红框内这棵子树,新的根要满足比左边的都大比右边的都小,所以应该要么左子树中最大的(橙色圈,也即中序遍历中10的前继结点)要么找右子树中最小的(蓝色圈,也即中序遍历中10的后继结点)(满足比左边的都大,比有右边的都小,而且更贴近被删除的值)作为新的根,让父节点指向新的子根。(如果父节点不存在,即被删除的就是总根,那么让新的根作为总根)
    3. 所以如果两个子树都存在,就随便搜其中一个(或者取两者中更靠近被删除的值的),否则,哪个子树存在就搜哪个子树,否则,没有孩子,直接删。
    4. 最大值:一直找右儿子直到没有右儿子;最小值:一直找左儿子直到没有左儿子
      图示

遍历方式:

  • 前序遍历 (PreOrder): 按照 根 - 左 - 右 的顺序遍历(根-左树-右树)
  • 中序遍历 (InOrder): 按照 左 - 根 - 右 的顺序遍历(从左往右 左-中-右)(左树-根-右树)
    • 二叉搜索树中序遍历的结果就是从小到大排序的结果
  • 后序遍历 (PostOrder): 按照 左 - 右 - 根 的顺序遍历(左树-右树-根)
  • 求最小值(一直找左儿子直到没有左儿子)、最大值(一直找右儿子直到没有右儿子)、树的高度(hp = max( hlch, hrch ) + 1)

实现:

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

struct Node {
	int val;
	Node *lch, *rch;
};

struct Tree {
	Node *root;
	Tree() { this->root = nullptr;}

	//循环写法
	void insert(int x) {
		//打包成节点
		Node *node = new Node;
		node->val = x;
		node->lch = nullptr;
		node->rch = nullptr;
		//找x的位置
		if (root == nullptr) {
			root = node;
			return;
		}

		Node *parx = root;
		while (parx) {
			if (x < parx->val) {
				if(parx->lch) parx = parx->lch;
				else {
					parx->lch = node;
					break;
				}
			}
			else {
				if(parx->rch) parx = parx->rch;
				else {
					parx->rch = node;
					break;
				}
			}
		}
		
		return;
	}

	bool find(int x) {
		if (this->root == nullptr) return false;
		Node *tmp = this->root;
		while (tmp) {
			if (x < tmp->val) tmp = tmp->lch;
			else if(x > tmp->val)tmp = tmp->rch;
			else return true;
		}
		return false;
	}
	
	//调用的时候一定要保证x存在,否则会出问题
	//返回的是删完pos之后新的根
	Node *remove(Node *pos, int x) {
		//以被删点的前继结点优先交换
		//先找x对应的pos
		if (pos == nullptr) return nullptr; //没找到x
		else {
			if (x < pos->val) pos->lch = remove(pos->lch, x);
			else if (x > pos->val) pos->rch = remove(pos->rch, x);
			else {
				//pos就是x的位置
				
				if (pos->lch) {
					//有左子树
					//优先找左子树中最大的点(一直找右儿子直到没有右儿子)
					
					Node *maxpar = pos->lch;//max的父亲
					
					//注意是提前搜索两层,终止的时候找到的是max的父节点
					while (maxpar->rch->rch) {
						maxpar = maxpar->rch;
					}
					Node *max = maxpar->rch;
					
					//把max放上去之前,maxpar继承max的遗产
					//max一定没有右儿子
					//如果max有左儿子,给maxpar,否则max什么都不剩,maxpar置null
					maxpar->rch = max->lch;

					//把max放上去
					max->lch = pos->lch;
					max->rch = pos->rch;
					
					delete pos;

					return max;//新的根就是max
				}
				else if(pos->rch) {
					//没有就找右子树的最小点

					Node *minpar = pos->rch;//min的父亲

					//注意是提前搜索两层,终止的时候找到的是min的父节点
					while (minpar->lch->lch) {
						minpar = minpar->lch;
					}
					Node *min = minpar->lch;

					//把min放上去之前,minpar继承min的遗产
					//min一定没有左儿子
					//如果min有右儿子,给minpar,否则min什么都不剩,minpar置null
					minpar->lch = min->rch;

					//把min放上去
					min->lch = pos->lch;
					min->rch = pos->rch;

					delete pos;

					return min;//新的根就是min
				}
				else {
					//pos即没有左子树也没有右子树,直接删就好
					delete pos;
					return nullptr;
				}
			}
			//x在搜下一层,本层根不变
			return pos;
		}
		
	}

	//遍历,前序中序后序
	void preOrder(Node *pos) {
		//根-左树-右树
		if (!pos) return;
		cout << pos->val << " ";
		preOrder(pos->lch);
		preOrder(pos->rch);
	}
	void inOrder(Node *pos) {
		//左树-根-右树
		if (!pos) return;
		inOrder(pos->lch);
		cout << pos->val << " ";
		inOrder(pos->rch);
	}
	void postOrder(Node *pos) {
		//左树-右树-根
		if (!pos) return;
		postOrder(pos->lch);
		postOrder(pos->rch);
		cout << pos->val << " ";
	}
	//求最值
	//最大值:一直找右儿子直到没有右儿子;最小值:一直找左儿子直到没有左儿子
	int maxVal(Node *pos) {
		if (pos->rch) return maxVal(pos->rch);
		return pos->val;
	}
	int minVal(Node *pos) {
		if (pos->lch) return minVal(pos->lch);
		return pos->val;
	}

	//递归写法
	//pos是子树的根,返回插入完x之后这个子树的新根
	//默认x与已有值不重复
	Node* insert_rec(Node *pos, int x) {
		//找x的位置
		if (pos == nullptr) {
			//子根没东西,可以放
			//打包成节点
			Node *node = new Node;
			node->val = x;
			node->lch = nullptr;
			node->rch = nullptr;
			if (!this->root)  this->root = node;
			return node;
		}
		else {
			//子根已经有了,找它的左右方向,该子树的根不变
			if (x < pos->val) pos->lch = insert_rec(pos->lch, x);
			else pos->rch = insert_rec(pos->rch, x);
			return pos;
		}
	}
	//递归写法
	//p=pos,从p开始找
	bool find_rec(Node *p, int x) {
		if (p == nullptr) return false;
		if (x < p->val) {
			return find_rec(p->lch, x);
		}
		else if (x > p->val) {
			return find_rec(p -> rch, x);
		}
		else return true;
	}

};

int main() {
	Tree tree;
	int arr[] = { 6,3,10,2,5,8,15,7,9,12,16 };
	for (int i = 0;i < 11;i++) tree.insert(arr[i]);//tree.insert_rec(tree.root, arr[i]); 
	cout << "normal finding 6: " << (tree.find(6) ? "found!" : "not found!") << endl;
	cout << "normal finding 20: " << (tree.find(20) ? "found!" : "not found!") << endl;
	cout << "recrrence finding 6: " << (tree.find_rec(tree.root,6)? "found!" : "not found!") << endl;
	cout << "recrrence finding 20: " << (tree.find_rec(tree.root,20)? "found!" : "not found!") << endl;
	cout << "Pre Order: "; tree.preOrder(tree.root); cout << endl;
	cout << "In Order: "; tree.inOrder(tree.root); cout << endl;
	cout << "Post Order: "; tree.postOrder(tree.root); cout << endl;
	cout << "min: " << tree.minVal(tree.root) << endl;
	cout << "max: " << tree.maxVal(tree.root) << endl;
	cout << "removing 10. " << endl;  tree.remove(tree.root, 10);
	cout << "Pre Order: "; tree.preOrder(tree.root); cout << endl;
	cout << "In Order: "; tree.inOrder(tree.root); cout << endl;
	cout << "Post Order: "; tree.postOrder(tree.root); cout << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值