二叉排序树BST

本文详细介绍了二叉排序树的基本操作,包括插入、查找和删除。插入操作分为递归和非递归两种实现方式;查找操作通过比较节点值逐层向下搜索;删除操作涉及三种情况,需要找到父节点并考虑中序前驱或后继节点。此外,文章还提供了相应的代码实现,并给出了考研方向的提示。
摘要由CSDN通过智能技术生成

talk is cheap,show me the code money

二叉排序树:要么是空树,要么是 左子树结点值 < < < 根结点值 < < < 右子树结点值 的二叉树。
< < < < < <中序遍历可以得到一个递增的序列。

1.插入构建

二叉排序树的插入只会插入到空的结点上面,形成新的叶结点。
每次可以比较,向下移动至正确的空结点上。如果有相同值的结点,那么插入失败。
递归插入:

//递归插入
bool Insert(BSTree &T, int x) {
	if (T == NULL) {
		T = new BSTNode;	
		T->data = x;
		T->lchild = T->rchild = NULL;
		return 1;
	}
	else if (x == T->data)
		return 0;
	else if (x < T->data)
		return Insert(T->lchild, x);     //递归的是T->lchild/rchild,是在原二叉树上生成结点
	else
		return Insert(T->rchild, x);
}

非递归插入:

//非递归插入
bool Insert2(BSTree &T, int x) {
	if (T == NULL) {
		T = new BSTNode;
		T->data = x;
		T->lchild = T->rchild = NULL;
		return 1;
	}
	BSTNode* p = T;
	while (p) {
		if (p->data > x) {
			if(p->lchild)
				p = p->lchild;
			else {
				p->lchild = new BSTNode;      //在原二叉树(二叉链表)上面生成结点,需要用p->lchild/rchild才行
				p->lchild->data = x;			//正如链表生成结点递归的是T->next
				p->lchild->lchild = p->lchild->rchild = NULL;
				return 1;
			}
		}
		else if (p->data < x) {
			if(p->rchild)
				p = p->rchild;
			else {
				p->rchild = new BSTNode;
				p->rchild->data = x;
				p->rchild->lchild = p->rchild->rchild = NULL;
				return 1;
			}
		}
		else
			return 0;
	}
}

插入构建排序二叉树:

void Create_BST(BSTree& T, int* m, int n) { //m保持了结点值
	T = NULL;								//n为结点数
	int i = 0;
	while (i < n) {
		Insert2(T, m[i]);
		i++;					 //记得工作指针后移啊,老是忘记
	}
}

2.查找

二叉排序树的查找从根节点开始比较,逐层向下,最坏情况是单分支二叉排序树,需要比较n次。
递归查找:

//递归查找
BSTNode* Search(BSTree T, int x) {
	if (T->data == x)
		return T;
	else if (T->data > x)
		return Search(T->lchild, x);
	else
		return Search(T->rchild, x);
}

非递归查找:

//非递归查找
BSTNode* Search2(BSTree T, int x) {
	while (T && T->data != x) {
		if (T->data > x)
			T = T->lchild;
		else
			T = T->rchild;
		//T = T->data > x ? T->lchild : T->rchild;
	}
	return T;
}

3.删除

分三种情况:

  1. 叶结点,直接删除
  2. 只有左/右子树,直接让左/右子树代替自己
  3. 有左、右子树,用中序前驱/后继结点代替自己。

1.找父结点:

\qquad 因为二叉排序树是二叉链表存储的,众所周知,链表要想删除结点必须要找到其父结点才行,若原地删除,父结点的子指针就会变成野指针。

递归找父结点:

//找父结点
BSTNode* father(BSTree T, BSTNode* p) {
	if (T == NULL)
		return NULL;
	if (T->lchild == p || T->rchild == p)
		return T;
	else
		return father(T->lchild, p) ? father(T->lchild, p) : (father(T->rchild, p) ? father(T->rchild, p) : NULL);
}

层序遍历找父结点:

BSTNode* father2(BSTree T, BSTNode* p) {    //层序找到父结点
	BSTree Q[100];
	int front = 0, rear = 0;
	BSTNode* q = T;
	Q[rear++] = T;
	while (front!=rear) {
		q = Q[front++];
		if (q->lchild == p || q->rchild == p)
			return q;
		else {
			if (q->lchild)	Q[rear++] = q->lchild;
			if (q->rchild)	Q[rear++] = q->rchild;
		}
	}
	return NULL;
}

2.找中序前驱/后继

因为要保证删除之后仍然保持排序特性,我们只可用其中序前驱、后继替代,只有中序遍历依旧是递增序列。中序前驱、后继也是左、右子树中值最解决p的结点。

中序前驱——左子树最右边的结点

prior = p->lchild;
while (prior->rchild) 
	prior = prior->rchild;

中序后继——右子树最左边的结点

next = p->rchild;
while (next->lchild) 
	next = next->lchild;

3.删除
所谓3的替代,如B替代A,就是将B值赋给A,然后递归删除B。
以中序后继为例:

//删除
void Delete(BSTree& T, BSTNode* &p) {
	BSTNode* f = father(T, p);    //父结点
	if (!p->lchild && !p->rchild) {   //为叶子,直接删除
		if (f->lchild == p)   //为其父结点的左子
			f->lchild = NULL;
		else                  //为其父结点的右子
			f->rchild = NULL;
		delete(p);
	}
	else if (p->lchild && !p->rchild) { //只有左子树
		if (f->lchild == p) 
			f->lchild = p->lchild;
		else 
			f->rchild = p->lchild;
		delete(p); 
	}
	else if (p->rchild && !p->lchild) { //只有右子树
		if (f->lchild == p)
			f->lchild = p->rchild;
		else
			f->rchild = p->rchild;
		delete(p);
	}
	else {								//两颗子树都有
		BSTNode* next = p->rchild;		//next为其中序的后继
		while (next->lchild)
			next = next->lchild;
		p->data = next->data;           //赋值给自己
		Delete(T, next);                //递归删除其中序后继
	}
}

考研向

二叉排序树选择题居多,算法题几乎没有
1.给定序列判断是否为二叉排序树
2.二叉排序树 T1,删除结点 A,再将结点 A 添加进去得到二叉排序树 T2:
\qquad 若 A 是 T1 叶结点,则 T1 与 T2 相同
\qquad 若 A 不是 T1 叶结点,则 T1 与 T2 不同
上述结论仅适用于二叉排序树,不适用于平衡树!
具体指向👉


测试代码,无聊可以跑一跑

int main()
{
    BSTree T;
    int m[] = { 3,5,1,4,2,7,0,6 };
    Create_BST(T, m, 8);
    InOrder(T);     cout << endl;
    LevelOrder(T);  cout << endl;
    cout << endl;

    int x = 5;
    BSTNode* p = Search(T, x);      //查找值为x的结点
    cout << p->data << endl;

    BSTNode* f=father(T, p);       //找到其父节点
    cout << f->data << endl;

    Delete(T, p);                  //删除该结点
    InOrder(T);     cout << endl;
}

原始蠢方法找前驱后继:
当你不知道怎么办的时候,用非递归遍历从栈里面找准没错,就是太蠢了。

BSTNode* Inprior(BSTree T, int x) {	//找到中序前驱
	BSTree S[30];
	int top = -1;
	BSTNode* pre = NULL;    //保存前驱
	BSTNode* p = T;
	while (p || top != -1) {
		if (p) {
			S[++top] = p;
			p = p->lchild;
		}
		else {
			p = S[top--];
			if (p->data != x)
				pre = p;	//保存上一个出栈元素
			else
				break;
			p = p->rchild;
		}
	}
	return pre;
}
BSTNode* Innext(BSTree T, int x) {   //中序后继
	BSTree S[50];
	int top = -1;
	BSTNode* p = T;
	int tag = 0;
	while (p || top != -1) {
		if (p) {
			S[++top] = p;
			p = p->lchild;
		}
		else {
			p = S[top--];
			if (tag == 1)
				return p;
			if (p->data == x)
				tag = 1;
			p = p->rchild;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值