学习随记三十二——不使用递归完成二叉查找树

不使用递归完成二叉查找树

相较于使用递归,不使用递归实现二叉查找树的操作稍显复杂,但是效率要高,其中最复杂的是删除操作。

结点声明:
typedef struct TreeNode{
ElementType data=0;
struct TreeNode* Left=nullptr;
struct TreeNode* Right=nullptr;
}TreeNode;
typedef TreeNode* Bintree;

使用的函数:
1、插入函数:int Insert(Bintree*,ElementType)
函数参数:传入一个结点指针的地址,此处使用了二级指针,目的是为了改变指针的指向。
具体思路:先判断传入的二级指针指向的指针是否是空指针,如果是空指针就为其开辟空间并将带插入元素赋给其数据域;若非空,则将待插入元素不断与结点元素比较,若带插入元素大则移动结点在结点右子树中找合适位置,反之在左子树中找合适位置,直到移动结点指针为空说明到合适位置了,为其开辟空间并使其父结点指向它。
函数代码:

int Insert(Bintree* root,ElementType x){
	Bintree parent=nullptr,cur=*root;
	if(*root==nullptr){
		(*root)=new TreeNode;
		(*root)->data=x;
		return 1;
	}else{
		while(cur){
			if(x>cur->data){
				parent=cur;
				cur=cur->Right;
			}else if(x<cur->data){
				parent=cur;
				cur=cur->Left;
			}else{
				cout<<"Data collision."<<endl;
				return 0;
			}
		}
		cur=new TreeNode;
		cur->data=x;
		if(parent->data>x)parent->Left=cur;
		else parent->Right=cur;
	}
}

2、查找函数:Bintree Find(Bintree,ElementType,int);
函数参数:传入根结点用来遍历,带查找元素,以及选择是返回带查找元素的结点地址还是它的父结点地址,是为了方便后续删除操作。
具体思路:循环根结点直到根结点为空,用一个结点记录根结点位置,若待查找元素比根结点小则根结点向左移动,反之向右移动;若找到待查找元素则根据传入的函数第三个参数选择返回的结点,若为0则返回待查找元素所在的结点地址,为1则返回这个结点的位置,若循环结束则说明这个元素不在这个树中则打印相关提示信息。

整体代码:

Bintree Find(Bintree root,ElementType x,int choice){
	Bintree father=root;
	while(root){
		if(root->data!=x){
		father=root;
		if(root->data<x)root=root->Right;
		else if(root->data>x)root=root->Left;
		}else{
			if(choice==0)return father;
			else if(choice==1)return root;
			else return nullptr;
		}
	}
	cout<<"Not found."<<endl;
	return nullptr;
}

3、判断函数
void Verdict(Bintree father,Bintree son) 不考虑存在数据相等
函数参数:传入一个结点和它的父结点;
基本思路:主要是为了删除操作方便,如果父结点大于子结点且子节点左子树不空则使父结点左指针指向子结点的左儿子,若空则左指针指向子结点的右儿子;如果父结点小于子结点且子节点左子树不空则使父结点右指针指向子结点的左儿子,若空则右指针指向子结点的右儿子。
原因:这是我这个程序最精彩的地方也是删除操作的关键所在。 我之所以这么设计这个函数的原因就是为了删除函数的操作,删除结点时有三种情况,一是待删除的结点是树叶则可直接删除,使用这个函数则相当于使待删除函数的父结点指向待删除函数的指针指向其右结点,因为结点在声明时为其赋了默认值为空指针,相当于将父结点指向子结点的指针置空,然后释放子结点即可;二是待删除的结点只有一个子树,为什么先考虑左子树是否为空?因为在后面情况三查找最小结点时最小的结点没有左子树,有左子树则说明是情况二,左子树非空,右子树为空。 这时使用这个函数使待删除的结点的父结点指向待删除结点的指针指向待删除结点的左子树即可。三是待删除的结点有两个子结点,这时在这个待删除的结点的右子树中找到最小的结点,再找到其父结点然后对这个最小的结点与其父结点使用这个函数,相当于使其父结点指向其右子树(因为最小的结点没有左子树),若右子树为空相当于将其父结点指向它的指针置空,不为空则将其父结点与其右儿子连接起来。
整体代码:

void Verdict(Bintree father,Bintree son){//不考虑相等的情况
	if(father->data>son->data){
		if(son->Left) father->Left=son->Left;
		else father->Left=son->Right;
	}else if(father->data<son->data){
		if(son->Left) father->Right=son->Left;
		else father->Right=son->Right;
	}else cout<<"abnormal condition."<<endl;
}

4、删除函数
void Delete(Bintree root,ElementType x)
函数参数:根结点地址,和待删除的元素
基本思路:Verdict函数的原因
函数代码;

void Delete(Bintree root,ElementType x){
	Bintree father=Find(root,x,0);
	Bintree son=Find(root,x,1);
	Bintree min=nullptr;
	if(son->Left==nullptr&&son->Right==nullptr){
		Verdict(father,son);
		delete(son);
	}else if(son->Left==nullptr||son->Right==nullptr){
		Verdict(father,son);
		delete(son);
	}else{
		min=FindMin(son->Right);
		father=Find(son,min->data,0);
		Verdict(father,min);
		son->data=min->data;
		delete(min);
	}
}

5、打印函数
void PrintTree(Bintree root)
函数参数:根结点地址
基本思路:这也是这个程序十分巧妙的地方,创建一个结点p将根结点传给它,再创建一个数据类型为结点指针的栈,循环直到指针p与栈都空,每次循环时先判断这个结点有没有左子树,有左子树则将其压入栈中,如果是第一次循环则相当于将根结点的最左的结点全部压入栈中,然后若栈非空则取栈顶指针给p,然后将这个栈顶指针出栈,输出p指向的元素(这时p是整个树中最小的结点),然后每次使p移动到它的右儿子上,若它的右儿子非空则遍历其右子树将其左结点入栈如此往复直到一个结点的右儿子为空,然后循环之前的操作直到p与栈都空。这种操作很有递归的味道,十分有趣,但受限于现在的编程水平与表达能力可以理解但无法清晰描述出来。
整体代码:

void Verdict(Bintree father,Bintree son){//不考虑相等的情况
	if(father->data>son->data){
		if(son->Left) father->Left=son->Left;
		else father->Left=son->Right;
	}else if(father->data<son->data){
		if(son->Left) father->Right=son->Left;
		else father->Right=son->Right;
	}else cout<<"abnormal condition."<<endl;
}

程序整体代码:

#include<iostream>
#include<stack>
typedef int ElementType;
typedef struct TreeNode{
	ElementType data=0;
	struct TreeNode* Left=nullptr;
	struct TreeNode* Right=nullptr;
}TreeNode;
typedef TreeNode* Bintree;
using namespace std;
int Insert(Bintree*,ElementType);
Bintree Find(Bintree,ElementType,int);
Bintree FindMin(Bintree);
void Delete(Bintree,ElementType);
void Verdict(Bintree,Bintree);
void PrintTree(Bintree);
int main(void){
	Bintree p=nullptr;
	int x;
	while(cin>>x)Insert(&p,x);
	cin.clear();
	cin.ignore();
	cout<<"Please input the number you want to delete."<<endl;
	cin>>x;
	Delete(p,x);
	PrintTree(p);
	return 0;
}
int Insert(Bintree* root,ElementType x){
	Bintree parent=nullptr,cur=*root;
	if(*root==nullptr){
		(*root)=new TreeNode;
		(*root)->data=x;
		return 1;
	}else{
		while(cur){
			if(x>cur->data){
				parent=cur;
				cur=cur->Right;
			}else if(x<cur->data){
				parent=cur;
				cur=cur->Left;
			}else{
				cout<<"Data collision."<<endl;
				return 0;
			}
		}
		cur=new TreeNode;
		cur->data=x;
		if(parent->data>x)parent->Left=cur;
		else parent->Right=cur;
	}
}
Bintree Find(Bintree root,ElementType x,int choice){
	Bintree father=root;
	while(root){
		if(root->data<x){
			father=root;
			root=root->Right;
		}else if(root->data>x){
			father=root;
			root=root->Left;
		}else{
			if(choice==0)return father;
			else if(choice==1)return root;
			else return nullptr;
		}
	}
	cout<<"Not found."<<endl;
	return nullptr;
}
Bintree FindMin(Bintree p){
	if(p!=nullptr)for(;p->Left;p=p->Left);
	return p;
}
void Delete(Bintree root,ElementType x){
	Bintree father=Find(root,x,0);
	Bintree son=Find(root,x,1);
	Bintree min=nullptr;
	if(son->Left==nullptr&&son->Right==nullptr){
		Verdict(father,son);
		delete(son);
	}else if(son->Left==nullptr||son->Right==nullptr){
		Verdict(father,son);
		delete(son);
	}else{
		min=FindMin(son->Right);
		father=Find(son,min->data,0);
		Verdict(father,min);
		son->data=min->data;
		delete(min);
	}
}
void PrintTree(Bintree root){
	Bintree p=root;
	stack<Bintree>str;
	while(p||!str.empty()){
		for(;p;p=p->Left){
		str.push(p);
		}
		if(!str.empty()){
		 p=str.top();
		 str.pop();
		 cout<<p->data<<endl;
		 p=p->Right;
		}
	}
}
void Verdict(Bintree father,Bintree son){//不考虑相等的情况
	if(father->data>son->data){
		if(son->Left) father->Left=son->Left;
		else father->Left=son->Right;
	}else if(father->data<son->data){
		if(son->Left) father->Right=son->Left;
		else father->Right=son->Right;
	}else cout<<"abnormal condition."<<endl;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值