1. 程式人生 > >c++二叉樹的遞迴和非遞迴的前序中序和後序遍歷以及層序遍歷

c++二叉樹的遞迴和非遞迴的前序中序和後序遍歷以及層序遍歷

二叉樹的遞迴版的前序,中序和後序遍歷很簡單也很容易理解,這裡就放一個前序遍歷的例子

//前序遍歷遞迴演算法,遞迴演算法都大同小異,這裡就不一一列舉了
void binaryTree::pro_order(NodeStack::Node *t) {
	NodeStack::Node *h = t;
	if (h != NULL) {
		cout << h->data<<"  ";
		pro_order(h->lchild);
		pro_order(h->rchild);
	}
	else 
		return;
}

中序遍歷和後序遍歷只要稍微換一下遞迴呼叫的位置就行了

非遞迴的二叉樹的遍歷需要用到棧,先放出棧的程式碼

#ifndef  _NodeStack_H_
#define _NodeStack_H_
#include <iostream>
#define MAXSIZE 100
typedef int type;

namespace st {
	class NodeStack {
	public:
		struct Node {
			struct Node *lchild, *rchild;  //左右孩子
			type data;              //存放資料
		};
	private:
		Node NodeData[MAXSIZE];
		int top;        //指向當前有資料的最大的空間
		int size;       //記錄棧的大小
	public:
		NodeStack();
		~NodeStack();
		int push(Node*);    //入棧操作
		Node pop();    //出棧操作
		int empty();   //探空操作
		int full();    //判斷是不是滿的
		void traverse(); //遍歷棧
		int get_top();   //獲取棧頂元素值
	};
	NodeStack::NodeStack() {
		top = -1;
		size = 100;
		for (int i = 0; i < 100; i++) {
			NodeData[i].lchild = NodeData[i].rchild = NULL;
			NodeData[i].data = NULL;
		}

	}
	NodeStack::~NodeStack() {
	}
	//入棧操作,成功返回1,失敗返回0
	int NodeStack::push(Node* n) {
		top++;
		NodeData[top] = *n;
		return 1;
	}
	NodeStack::Node NodeStack::pop() {
		return NodeData[top--];
	}
	int NodeStack::empty() {
		if (top == -1)
			return 1;  //棧是空的
		else
			return 0;  //棧非空
	}
	int NodeStack::full() {
		if (top == size - 1)
			return 1;     //棧是滿的
		else
			return 0;     //棧不是滿的
	}
	void NodeStack::traverse() {
		int t = top;
		std::cout << "棧空間:";
		while (t != -1)
			std::cout << NodeData[t--].data << " ";
		std::cout << std::endl;
	}
	int NodeStack::get_top() {
		return top;
	}
}
#endif // ! _NodeStack_H_

二叉樹的結點結構體是寫在棧的類裡面的

先是前序遍歷

//前序遍歷的非遞迴演算法
void binaryTree::pro_traverse() {
	NodeStack::Node h = *head;
	NodeStack::Node *t = &h;
	cout << "前序遍歷結果如下(非遞迴演算法):" << endl;
	stack.push(t);
	while (!stack.empty()) {
		//只要棧不空
		//stack.traverse();
		 h = stack.pop();
		 cout << h.data << "  ";
		if (h.rchild != NULL) {
			//如果右子樹不空則入棧
			t = h.rchild;
			stack.push(t);
		}
		if (h.lchild != NULL) {
			//如果左子樹不空則入棧
			t = h.lchild;
			stack.push(t);
		}
	}
	cout << endl;
}

然後是中序遍歷

//中序遍歷的非遞迴演算法
/*每個結點在入棧後都先找所有的左孩子,直到沒有左孩子了為止
 *出棧之後找右子樹,如果存在右子樹那麼右子樹也遵循上面的步驟 
 */
void binaryTree::mid_traverse() {
	NodeStack::Node h = *head;
	NodeStack::Node *t = &h;
	//先讓頭結點入棧
	cout << "中序遍歷的結果如下(非遞迴演算法):" << endl;
	stack.push(t);
	while (!stack.empty()) {
		while (t->lchild != NULL) {
			t = t->lchild;
			stack.push(t);
		}
		//接下來出棧
		h = stack.pop();
		cout << h.data <<"  ";
		//如果出棧的結點有右子樹那麼入棧
		if (h.rchild != NULL) {
			t = h.rchild;
			stack.push(t);
			//之後再對右子樹做同樣操作
			while (t->lchild != NULL) {
				t = t->lchild;
				stack.push(t);
			}
		}
	}
	cout << endl;
}

接下來是後序遍歷

//後序遍歷的非遞迴演算法
/*一個結點入棧後它的左右左結點都要入棧,直到沒有左結點了為止
* 到了沒有左結點的時候那麼看看它有沒有右結點,如果有就先把自己入棧,然後右結點按照上一步檢查左結點
* 如果沒有就輸出,然後繼續出棧,重複上面的步驟,這裡需要維持一個記錄入棧次數的陣列,當一個數第三次入棧的時候
* 就不能再入棧了
*/
void binaryTree::pos_traverse() {
	NodeStack::Node h = *head;
	NodeStack::Node *t = &h;
	int num = 0;   //記錄入棧的次數
	cout << "後序遍歷的結果如下(非遞迴演算法):" << endl;
	//先入棧
	stack.push(t);
	//將所有左子樹入棧
	while (t->lchild != NULL) {
		t = t->lchild;
		stack.push(t);
	}
	while (!stack.empty()) {
		h = stack.pop();    //出棧
		if (h.rchild != NULL && isfirst[stack.get_top()+1] == 0) {
			//如果右子樹存在
			t = &h;
			stack.push(t);
			isfirst[stack.get_top()]++;  //第二次入棧次數加一
			t = h.rchild;
			stack.push(t);
			while (t->lchild != NULL) {
				t = t->lchild;
				stack.push(t);
			}
		}
		else {
			//否則直接輸出就好了
			cout << h.data << "  ";
			//徹底出棧了之後就可以記錄入棧次數為0了
			isfirst[stack.get_top() + 1] = 0;
		}
	}
	cout << endl;
}

最後是層序遍歷

//層序遍歷
/*層序遍歷需要用到佇列,我們這裡用陣列模擬,
*先把頭結點入隊,然後逐個出隊,在出隊的同時將左右孩子入隊,我們給隊分配的空間是100,也就是最多支援6層的樹
*2^6=64,2^7=128,對於滿二叉樹的情況就會溢位,所以最多支援6層(頭結點算第0層)
*/
void binaryTree::level_traverse() {
    NodeStack::Node h = *head;
    int head = 0,tail = 0;       //佇列的頭和尾指標,隊空的標誌是head == tail
    quene[head++] = h;           //head指向下一個將要填充的空間
    cout << "層序遍歷的結果是:" << endl;
    //接下來開始出隊
    while (head != tail) {
        h = quene[tail++];
        if (h.lchild != NULL) {
            //左子樹入隊
            quene[head++] = *h.lchild;
        }
        if (h.rchild != NULL) {
            quene[head++] = *h.rchild;
        }
        cout << h.data << "  ";
    }
    cout << endl;
}


下面貼完整的程式碼

#include <iostream>
#include "NodeStack.h"      //結點棧,樹結點的定義也在這個標頭檔案裡
using namespace std;
using namespace st;        //這個名稱空間就是指定NodeStack.h的,如果在那個標頭檔案沒有使用這個名稱空間那麼就不需要這一句
typedef int type;
//先寫一個普通的二叉查詢樹
class binaryTree {
private:
	//樹的結點的結構體
	
	NodeStack::Node *head;                 //頭指標
	NodeStack stack;            //結點棧
	int isfirst[100];                //用於記錄是否是第一次入棧(用於後序遍歷的非遞迴演算法)
	NodeStack::Node quene[100];            //模擬佇列的陣列,用於層序遍歷
	//數的結點的棧,用於非遞迴遍歷樹
public:
	binaryTree();
	~binaryTree();
	NodeStack::Node* get_head();                //獲取頭指標
	void destroy(NodeStack::Node* );  //刪除結點
	int insert();                    //插入操作
	void pro_traverse();             //前序遍歷這棵二叉樹
	void mid_traverse();             //中序遍歷這棵二叉樹
	void pos_traverse();             //後序遍歷這棵二叉樹
	void level_traverse();           //層序遍歷
	//下面是遍歷的遞迴演算法
	void pro_order(NodeStack::Node*);
};
//建構函式,分配一個頭結點的空間和棧的空間
binaryTree::binaryTree() {
	//初始化頭指標
	head = new NodeStack::Node();
	head->lchild = head->rchild = NULL;
	head->data = NULL;
	for (int i = 0; i < 100; i++) {
		isfirst[i] = 0;  //全部初始化為0
		quene[i].lchild = quene[i].rchild = NULL;
		quene[i].data = NULL;
	}
	
}
//解構函式,收回所有分配的空間
binaryTree::~binaryTree() {
	NodeStack::Node *t = head;
	destroy(t);
	head = new NodeStack::Node();
	head->lchild = head->rchild = NULL;
	head->data = NULL;
}
void binaryTree::destroy(NodeStack::Node *t) {
	if (t->rchild != NULL) 
		destroy(t->rchild);
	if (t->lchild != NULL)
		destroy(t->lchild);
	delete t;
}
NodeStack::Node* binaryTree::get_head() {
	return this->head;
}
//插入操作,規定左子樹的數比根節點小,右子樹的數比根節點大
int binaryTree::insert() {
	cout << "請輸入要插入的結點的資料:";
	int num;
	cin >> num;
	if (head->data == NULL) {
		head->data = num;
		return 1;
	}
	else {
		NodeStack::Node *t = new NodeStack::Node();
		NodeStack::Node *pre = NULL;
		NodeStack::Node *h = head;
		t->lchild = t->rchild = NULL;
		t->data = num;
		while (h != NULL) {
			if (h->data > t->data) {
				pre = h;
				h = h->lchild;
			}
			else {
				pre = h;
				h = h->rchild;
			}
		}
		if (pre->data > t->data)
			pre->lchild = t;
		else
			pre->rchild = t;
		return 1;
	}
}
//前序遍歷的非遞迴演算法
void binaryTree::pro_traverse() {
	NodeStack::Node h = *head;
	NodeStack::Node *t = &h;
	cout << "前序遍歷結果如下(非遞迴演算法):" << endl;
	stack.push(t);
	while (!stack.empty()) {
		//只要棧不空
		//stack.traverse();
		 h = stack.pop();
		 cout << h.data << "  ";
		if (h.rchild != NULL) {
			//如果右子樹不空則入棧
			t = h.rchild;
			stack.push(t);
		}
		if (h.lchild != NULL) {
			//如果左子樹不空則入棧
			t = h.lchild;
			stack.push(t);
		}
	}
	cout << endl;
}
//中序遍歷的非遞迴演算法
/*每個結點在入棧後都先找所有的左孩子,直到沒有左孩子了為止
 *出棧之後找右子樹,如果存在右子樹那麼右子樹也遵循上面的步驟 
 */
void binaryTree::mid_traverse() {
	NodeStack::Node h = *head;
	NodeStack::Node *t = &h;
	//先讓頭結點入棧
	cout << "中序遍歷的結果如下(非遞迴演算法):" << endl;
	stack.push(t);
	while (!stack.empty()) {
		while (t->lchild != NULL) {
			t = t->lchild;
			stack.push(t);
		}
		//接下來出棧
		h = stack.pop();
		cout << h.data <<"  ";
		//如果出棧的結點有右子樹那麼入棧
		if (h.rchild != NULL) {
			t = h.rchild;
			stack.push(t);
			//之後再對右子樹做同樣操作
			while (t->lchild != NULL) {
				t = t->lchild;
				stack.push(t);
			}
		}
	}
	cout << endl;
}
//後序遍歷的非遞迴演算法
/*一個結點入棧後它的左右左結點都要入棧,直到沒有左結點了為止
* 到了沒有左結點的時候那麼看看它有沒有右結點,如果有就先把自己入棧,然後右結點按照上一步檢查左結點
* 如果沒有就輸出,然後繼續出棧,重複上面的步驟,這裡需要維持一個記錄入棧次數的陣列,當一個數第三次入棧的時候
* 就不能再入棧了
*/
void binaryTree::pos_traverse() {
	NodeStack::Node h = *head;
	NodeStack::Node *t = &h;
	int num = 0;   //記錄入棧的次數
	cout << "後序遍歷的結果如下(非遞迴演算法):" << endl;
	//先入棧
	stack.push(t);
	//將所有左子樹入棧
	while (t->lchild != NULL) {
		t = t->lchild;
		stack.push(t);
	}
	while (!stack.empty()) {
		h = stack.pop();    //出棧
		if (h.rchild != NULL && isfirst[stack.get_top()+1] == 0) {
			//如果右子樹存在
			t = &h;
			stack.push(t);
			isfirst[stack.get_top()]++;  //第二次入棧次數加一
			t = h.rchild;
			stack.push(t);
			while (t->lchild != NULL) {
				t = t->lchild;
				stack.push(t);
			}
		}
		else {
			//否則直接輸出就好了
			cout << h.data << "  ";
			//徹底出棧了之後就可以記錄入棧次數為0了
			isfirst[stack.get_top() + 1] = 0;
		}
	}
	cout << endl;
}
//前序遍歷遞迴演算法,遞迴演算法都大同小異,這裡就不一一列舉了
void binaryTree::pro_order(NodeStack::Node *t) {
	NodeStack::Node *h = t;
	if (h != NULL) {
		cout << h->data<<"  ";
		pro_order(h->lchild);
		pro_order(h->rchild);
	}
	else 
		return;
}
//層序遍歷
/*層序遍歷需要用到佇列,我們這裡用陣列模擬,
*先把頭結點入隊,然後逐個出隊,在出隊的同時將左右孩子入隊,我們給隊分配的空間是100,也就是最多支援6層的樹
*2^6=64,2^7=128,對於滿二叉樹的情況就會溢位,所以最多支援6層(頭結點算第0層)
*/
void binaryTree::level_traverse() {
	NodeStack::Node h = *head;
	int head = 0,tail = 0;       //佇列的頭和尾指標,隊空的標誌是head == tail
	quene[head++] = h;           //head指向下一個將要填充的空間
	cout << "層序遍歷的結果是:" << endl;
	//接下來開始出隊
	while (head != tail) {
		h = quene[tail++];
		if (h.lchild != NULL) {
			//左子樹入隊
			quene[head++] = *h.lchild;
		}
		if (h.rchild != NULL) {
			quene[head++] = *h.rchild;
		}
		cout << h.data << "  ";
	}
	cout << endl;
}

/*
	測試資料:
				5
			  /	  \
			2	    10
          /  \     /  \
        1      3  7
		         / \
                6   8
*/

int main()
{
	binaryTree bt;
	for(int i=0;i<8;i++)
		bt.insert();
	bt.pro_traverse();
	bt.mid_traverse();
	bt.pos_traverse();
	bt.level_traverse();
	return 0;
}

測試結果: