1. 程式人生 > >資料結構程式設計回顧(四) 二叉樹的三種非遞迴遍歷以及根節點到任意節點的路徑

資料結構程式設計回顧(四) 二叉樹的三種非遞迴遍歷以及根節點到任意節點的路徑

題目四:求二叉樹上結點的路徑
設計要求:在採用鏈式儲存結構儲存的二叉樹上,以bt 指
向根結點,p 指向任一給定的結點,程式設計實現求出從根結點
到給定結點之間的路徑。
選單內容:
1. 建立二叉樹儲存結構
2. 求二叉樹的前序遍歷
3. 求二叉樹的中序遍歷
4. 求二叉樹的後續遍歷
5. 求指定結點的路徑
6. 退出系統
請選擇:1 – 6:
提示:
【採用非遞迴遍歷的方法】
1. 二叉樹的建立
2. 求指定結點的路徑
3. 二叉樹的前、中、後序非遞迴遍歷演算法
4. 查詢函式

 

 

使用結構體:樹和棧 棧用來實現非遞迴以及路徑

typedef struct Tree {
	char data;
	int run=0;//在後序遍歷的非遞迴中使用 表示這個節點是否被訪問過
	struct Tree *l,*r;
} Tree,*BiTree;
//用棧來儲存指標
typedef struct Stack {
	Tree **top;
	Tree **base;
	int stacksize;//
} Stack;

首先,輸入按照先序遍歷樹的字串,其中若結點為空,則用'#'表示:

遞迴方法建立:

void CreateTree2(BiTree &T) {
	char c;
	cin>>c;
	if(c=='#')
		T=NULL;
	else {
		InitTree(T,c);
		CreateTree2(T->l);
		CreateTree2(T->r);
	}
}

非遞迴方法(程式碼可能有問題 當時註釋掉了  略過略過):

int CreateTree1(BiTree &bt) {
	Stack ss;
	InitStack(ss);
	cout << "按照先序輸入樹,用'#'表示空:" << endl;
	char input[100];
	BiTree p=bt;
	int i=0;
	gets(input);
	int n=strlen(input);
	for(i=0; i<n; i++) {
		if(input[i]!='#') {
			InitTree(p,input[i]);
			push(ss,p);
			p=p->l;
		} else {
			InitTree(p,input[i]);
			if(isempty(ss)||i==n-1) {
				p=bt;
				return 0;
			}

			else
				bt=pop(ss,p);
			if(p->l&&(p->r==NULL)) {
				p=p->r;
			}

			else {
				while(p->l&&p->r) {
					if(isempty(ss))
						return 0;
					else
						bt=pop(ss,p);
				}
				p=p->r;
			}
		}
	}
}

 

先序和中序遍歷的非遞迴方式:

先序:

1.先輸出該結點的值,然後把該節點壓入棧中,然後沿著該節點的左子樹繼續,直到該結點為空;

2.此時如果棧不為空,則彈出棧頂元素,並訪問其右子樹,執行1,否則結束。

中序:

和先序類似,唯一不同的是輸出該結點的時機是在出棧時,而不是在最開始。

void First(BiTree T) {
	Stack sf;
	InitStack(sf);
	Tree *p=T;
	while(p||!isempty(sf)) {
		while(p) {
			cout<<p->data<<' ';
			push(sf,p);
			p=p->l;
		}
		if(!isempty(sf)) {
			p=pop(sf,p);
			p=p->r;
		}
	}
	cout<<endl;
}
void Mid(BiTree T) {
	Stack sm;
	InitStack(sm);
	Tree *p=T;
	while (p||!isempty(sm)) {
		while(p) {
			push(sm,p);
			p = p->l;
		}
		if (!isempty(sm)) {
			p=pop(sm,p);
			cout <<p->data<<' ';
			p = p->r;
		}

	}
	cout<<endl;
}

後序:

後序輸出結點的值是要判斷其左右結點(如果存在)是否都被訪問過才可以。

首先類似中序的方法先沿著左子樹走並壓入棧中,當結點為空時,如果棧不空,則彈出棧頂元素,如果這個元素的右子樹存在並且沒有被訪問過的話,那麼把當前結點壓入棧中,並訪問其右子樹,執行最開始的沿著左子樹的過程,如果右子樹不存在或者右子樹已經被訪問,那麼輸出該結點的值,並值該節點的訪問為1.如果棧空,結束。

void Last(BiTree T) {
	Stack sl;
	InitStack(sl);
	Tree *p=T;

	while (p||!isempty(sl)) {
		while(p&&p->run==0) {

			push(sl,p);
			p = p->l;
		}
		if (!isempty(sl)) {
			p=pop(sl,p);
			//	cout<<2;
			if(p->r&&p->r->run==0) {
				push(sl,p);
				p = p->r;
				//	cout<<3;
			}

			else {
				cout <<p->data<<' ';//
				p->run=1;
				if(isempty(sl)) {
					cout<<endl;
					break;
				}
			}
	}
	}
}

根節點到任意結點的路徑:

有點類似於分治法的思想,如果這個點存在這個樹中(值唯一),那麼必定滿足以下條件的唯一一個:

1.該結點在根節點被找到

2.該節點在根節點的左子樹被找到

3.該節點在根節點的右子樹被找到

 

如果都不滿足,則不在這個樹內。

利用這個思想,判斷樹是否滿足三個條件中的一個,如果滿足,則把根壓入棧中,最後依次彈出,即為路徑。

bool Find(BiTree T,char c){
	if(T==NULL)
	return 0;


	if(T->data==c||Find(T->l,c)||Find(T->r,c)){
		push(sq,T);
		count++;
		return 1;
	}

	return 0;


}
void jiedian(BiTree T){
		InitStack(sq);
	char d1;
	cout<<"請輸入指定的結點:";
	cin>>d1;

 Find(T,d1);
 if(isempty(sq)){
 	cout<<"cannot find.";
 }
 while(!isempty(sq)){
 	BiTree p=pop(sq,p);
 	cout<<p->data;

 	if(count!=1)
 	cout<<"->";
	count--;
 }
 cout<<endl;
}

 

 

 

 

 

 

完整程式碼:

#include <iostream>
#include <windows.h>
#include<stdio.h>
#include <string.h>
#define SIZE 100
#define incre 10
using namespace std;
typedef struct Tree {
	char data;
	int run=0;
	struct Tree *l,*r;
} Tree,*BiTree;
//用棧來儲存指標
typedef struct Stack {
	Tree **top;
	Tree **base;
	int stacksize;//
} Stack;
int isempty(Stack s) {
	if(s.top==s.base)
		return 1;
	else
		return 0;
}

void InitStack(Stack &s) {
	s.base=(Tree **)malloc(SIZE*sizeof(Tree));
	s.top=s.base;
	s.stacksize=SIZE;
}
void push(Stack &s,Tree *p) {
	*s.top++=p;

}
Tree *pop(Stack &s,Tree *p) {
	if(s.top==s.base)
		exit(-1);
	p=*--s.top;
	return p;
}

void InitTree(BiTree &T,char c) {
	T=(BiTree)malloc(sizeof(Tree));
	T->data=c;
	T->l=NULL;
	T->r=NULL;
}
void First(BiTree T) {
	Stack sf;
	InitStack(sf);
	Tree *p=T;
	while(p||!isempty(sf)) {
		while(p) {
			cout<<p->data<<' ';
			push(sf,p);
			p=p->l;
		}
		if(!isempty(sf)) {
			p=pop(sf,p);
			p=p->r;
		}
	}
	cout<<endl;
}
void Mid(BiTree T) {
	Stack sm;
	InitStack(sm);
	Tree *p=T;
	while (p||!isempty(sm)) {
		while(p) {
			push(sm,p);
			p = p->l;
		}
		if (!isempty(sm)) {
			p=pop(sm,p);
			cout <<p->data<<' ';
			p = p->r;
		}

	}
	cout<<endl;
}
void Last(BiTree T) {
	Stack sl;
	InitStack(sl);
	Tree *p=T;

	while (p||!isempty(sl)) {
		while(p&&p->run==0) {

			push(sl,p);
			p = p->l;
		}
		if (!isempty(sl)) {
			p=pop(sl,p);
			//	cout<<2;
			if(p->r&&p->r->run==0) {
				push(sl,p);
				p = p->r;
				//	cout<<3;
			}

			else {
				cout <<p->data<<' ';//
				p->run=1;
				if(isempty(sl)) {
					cout<<endl;
					break;
				}
			}
	}
	}
}
int CreateTree1(BiTree &bt) {
	Stack ss;
	InitStack(ss);
	cout << "按照先序輸入樹,用'#'表示空:" << endl;
	char input[100];
	BiTree p=bt;
	int i=0;
	gets(input);
	int n=strlen(input);
	for(i=0; i<n; i++) {
		if(input[i]!='#') {
			InitTree(p,input[i]);
			push(ss,p);
			p=p->l;
		} else {
			InitTree(p,input[i]);
			if(isempty(ss)||i==n-1) {
				p=bt;
				return 0;
			}

			else
				bt=pop(ss,p);
			if(p->l&&(p->r==NULL)) {
				p=p->r;
			}

			else {
				while(p->l&&p->r) {
					if(isempty(ss))
						return 0;
					else
						bt=pop(ss,p);
				}
				p=p->r;
			}
		}
	}
}
void CreateTree2(BiTree &T) {
	char c;
	cin>>c;
	if(c=='#')
		T=NULL;
	else {
		InitTree(T,c);
		CreateTree2(T->l);
		CreateTree2(T->r);
	}
}

	Stack sq;
    int count;
bool Find(BiTree T,char c){
	if(T==NULL)
	return 0;


	if(T->data==c||Find(T->l,c)||Find(T->r,c)){
		push(sq,T);
		count++;
		return 1;
	}

	return 0;


}
void jiedian(BiTree T){
		InitStack(sq);
	char d1;
	cout<<"請輸入指定的結點:";
	cin>>d1;

 Find(T,d1);
 if(isempty(sq)){
 	cout<<"cannot find.";
 }
 while(!isempty(sq)){
 	BiTree p=pop(sq,p);
 	cout<<p->data;

 	if(count!=1)
 	cout<<"->";
	count--;
 }
 cout<<endl;
}
int main() {
	BiTree bt=NULL;
	BiTree p=bt;
	cout << "按照先序輸入樹,用'#'表示空:" << endl;
	CreateTree2(bt);
	cout<<"先序遍歷:";
	First(bt);
	cout<<"中序遍歷:";
	Mid(bt);
	cout<<"後序遍歷:";
	Last(bt);
    jiedian(bt);
	return 0;
}