zl程序教程

您现在的位置是:首页 >  后端

当前栏目

如何使用非递归算法实现二叉排序树的建立

算法排序递归 实现 如何 建立 二叉 使用
2023-09-14 09:13:18 时间

1.如何使用非递归算法实现二叉排序树的建立?

2.我们都知道二叉排序树就是二叉树,而二叉树其实就是带有双指针的链表,那么链表是怎么做插入的?下面我们来看一段链表插入值算法:

#include <stdio.h>
#define null NULL
typedef struct Node{
	struct Node *next;//下一指针
	int data;//数据域 
}Node ;

//将数据插入到单链表中 
void insertData(Node *&L,int data){
	Node *r;//新建一个指针 
	Node *q ;
	r = L;//赋值 
	while(r->next!=null){
		if(r->next->data > data ){//找到第一个大于值的节点 			
			q = new Node;//指向一个新的结点
			q->data = data;
			r->next = q;
			q->next = r->next->next; 
		}
		else	r = r->next	;
	} 
	if(r->next == null){
		q = new Node;//指向一个新的结点
		q->data = data;
		r->next = q;
		q->next = null; 
	}	
}

//遍历单链表
void traverseLinkList(Node *L){	
	L = L->next;
	while(L!=null){
		printf("%d ",L->data);
		L = L->next;
	}
} 

int main(){
	Node *L;//单链表的表头
	L = new Node;//这一步,非常重要!指向一个具体结点!
	L->next = null; 
	int number;
	printf("请输入插入的数据个数!\n");	
	scanf("%d",&number);
	int data;
	for(int i = 0;i<number;i++){
		scanf("%d",&data);//输入数据
		insertData(L,data);//往单链表中插入数据 
	}
	//遍历输出 
	traverseLinkList(L);
} 
/*
4
1 2 3 4
*/
 
3.我们来看一下这个插入值的insertData函数

void insertData(Node *&L,int data){
	Node *r;//新建一个指针 
	Node *q ;
	r = L;//赋值 
	while(r->next!=null){
		if(r->next->data > data ){//找到第一个大于值的节点 			
			q = new Node;//指向一个新的结点
			q->data = data;
			r->next = q;
			q->next = r->next->next; 
		}
		else	r = r->next	;
	} 
	if(r->next == null){
		q = new Node;//指向一个新的结点
		q->data = data;
		r->next = q;
		q->next = null; 
	}	
}
(1)我们需要新建一个Node* r,与Node* q;r是为了防止直接使用L导致L成为了指向最后的节点的指针(从而导致不能直接输出了)。故需要使用一个指针r来暂存L的值

(2)这里的指针q是干嘛的呢?是新建插入结点做准备,因为我们需要对插入的值放到节点中,这个q就是指向这个插入的结点。

(3)插入过程是怎么样的?是在判断while(r->next!=null)的基础上,因为我们需要的是对当前的结点判断,而对当前节点的上一结点末尾插入,这就是跟随指针!
但是这里我并没有使用跟随指针,但是跟随指针的思想,大家得有!因为这个在后面二叉树的建树过程中则是至关重要的。

4.现在回到主题,我们来看看这个二叉排序树非递归手法的实现函数

void insertBiTree_2(BiTree *&bt,int da){	
	//先判断树是否为空-->如果为空,则建树,直接返回 
	if(bt==null){	
		bt = new BiTree;//因为在main函数中,bt是null,所以需要先让其指向一个节点,才能在该节点上赋值操作! 
		bt->data = da;
		bt->lChild = null;
		bt->rChild = null;
		return ;
	} 	
	BiTree *pre;//探索指针
	pre = bt;
	
	BiTree *follow;//跟随指针
	follow = bt;
			
	int flag = -1; 
	while(pre!=NULL){	
		if(pre->data <= da ) {//需要循环测试是否为null
			follow = pre;//暂存值 
			pre = pre->rChild; //变化的是follow!!
			flag = 1;
		}
		else if( pre != null && pre->data > da ){//需要循环测试是否为null 
			follow = pre;//暂存值 
			pre = pre->lChild;//变化的是follow!!
			flag = -1;
		}		
	}
	//注意!!如果说是跟随指针发现孩子为null,则添加节点,且将当前指针(pre)的孩子设成temp 
	if(pre == null){
		//下面是新建一个BiTree节点,并将其修改成标志的二叉树节点(左右子树均赋为空) 
		BiTree *temp;	
		temp = new BiTree; 
		temp->data = da;//插入数据
		temp->lChild = null;
		temp->rChild = null;
		
		if(flag == 1){
			follow->rChild = temp;	
			flag = -1; 
		}
		else if(flag == -1){
			follow->lChild = temp;
			flag = -1;
		}
	}	
} 
(1)这里的Pre就是探索指针,什么是探索指针,意思就是判断当前值的大小与插入值大小的判断,而follow指针就是保留当前这个pre,因为pre在做出大小比较之后,需要将其改变成指向左子树或者是右子树的指针。但是我们在插入值的时候,是对follow操作的。【follow名为跟随指针】

(2)下面给出一段典型的错误代码,当然这个代码也是由作者本人写出来的。。。。

//插入数据-->建立一棵二叉树
//如果你硬要使用这种方式来创建一棵二叉树,那么你需要一个头结点 
void insertBiTree_2(BiTree *&temp,int da){	
	//BiTree *temp;
	//temp = bt;
		
	//先找到合适的位置--->如果说temp不为null 
	//这里是一个while(temp!=null)循环的原因:可能是在左子树,也可能是右子树 
	while(temp!=NULL){	
		if(temp->data < da ) {//需要循环测试是否为null
			temp = temp->rChild;
		}
		else if( temp != null && temp->data > da ){//需要循环测试是否为null 
			temp = temp->lChild;
		}		
	}
	if(temp == null){
		temp = new BiTree; 
		temp->data = da;//插入数据
		temp->lChild = null;
		temp->rChild = null;	
	}	
} 

这里的错误有很多:

(1)难道是真的直接对temp做修改?而不需要做一个暂存值的保护?--->需要另行申请一个指针,指向bt,而不是直接使用

(2)在while()循环里面,如果添加的是右子树的叶子结点,那么这里的if(temp->data > da)将会出现错误!【因为这里的temp->data不存在,即temp==null】

(3)当然,最关键的错误在于,我们没有使用跟随指针,导致每次出现的结果都是temp  == null,然后无法建立一棵完整的数,从而导致无法输出。

6.下面给出该程序的源代码

#include <stdio.h>
#define null NULL
typedef struct BiTree{
	struct BiTree *lChild,*rChild;//左右子指针
	int data;//数据域 
}BiTree;

//先序遍历二叉树
void preOrderBiTree(BiTree *T){
	if(T!=null){
		printf("%d ",T->data);	
	}
	else	return ;//否则终止递归--->递归结束边界 
	preOrderBiTree(T->lChild);//递归左子树 
	preOrderBiTree(T->rChild);//递归右子树 
}

void insertBiTree_2(BiTree *&bt,int da){	
	//先判断树是否为空-->如果为空,则建树,直接返回 
	if(bt==null){	
		bt = new BiTree;//因为在main函数中,bt是null,所以需要先让其指向一个节点,才能在该节点上赋值操作! 
		bt->data = da;
		bt->lChild = null;
		bt->rChild = null;
		return ;
	} 	
	BiTree *pre;//探索指针
	pre = bt;
	
	BiTree *follow;//跟随指针
	follow = bt;
			
	int flag = -1; 
	while(pre!=NULL){	
		if(pre->data <= da ) {//需要循环测试是否为null
			follow = pre;//暂存值 
			pre = pre->rChild; //变化的是follow!!
			flag = 1;
		}
		else if( pre != null && pre->data > da ){//需要循环测试是否为null 
			follow = pre;//暂存值 
			pre = pre->lChild;//变化的是follow!!
			flag = -1;
		}		
	}
	//注意!!如果说是跟随指针发现孩子为null,则添加节点,且将当前指针(pre)的孩子设成temp 
	if(pre == null){
		//下面是新建一个BiTree节点,并将其修改成标志的二叉树节点(左右子树均赋为空) 
		BiTree *temp;	
		temp = new BiTree; 
		temp->data = da;//插入数据
		temp->lChild = null;
		temp->rChild = null;
		
		if(flag == 1){
			follow->rChild = temp;	
			flag = -1; 
		}
		else if(flag == -1){
			follow->lChild = temp;
			flag = -1;
		}
	}	
} 

int main(){ 
	BiTree *bt;//新建一个BiTree型头指针 
	bt = null;//初始化为null 
	
	int number;//表示节点数
	scanf("%d",&number);//输入number
	int i;
	int da;
	for(i = 0;i< number;i++){
		scanf("%d",&da);		
		insertBiTree_2(bt,da);//从bt的左子树中插入值到树中 
	} 	
	//遍历输出二叉树 
	preOrderBiTree(bt);
}
/**
7
8 6 5 7 10 8 11
*/