1. 程式人生 > >二叉樹學習之非遞迴遍歷

二叉樹學習之非遞迴遍歷

二叉樹遞迴遍歷可謂是學過資料結構的同仁都能想一下就能寫出來,但在應聘過程我們常常遇到的是寫出一個二叉樹非遞迴遍歷函式,接著上篇文章寫二叉樹的非遞迴遍歷,先難後易,一步一步的來.

  先上程式碼:

#include "binarytree.h"
#include <stack>
#include <queue>

#ifndef RECU
#warning("RECU is not defined")

/**
 *前序遍歷(根左右)
 *
 *1、當前節點為非空,訪問當前節點,壓棧其右子節點,考慮其左子節點
 *2、當前節點為NULL,出棧
 *
 *@param t to visit
 *@param visit point to a func
 */
void pre_order(link t, void (*visit)(link))
{
	std::stack<link> myStack;

	while( t || !myStack.empty() ) {

		if ( t ) {
			visit(t);
			myStack.push(t->rchild);
			t = t->lchild;
		} else {
			t = myStack.top();
			myStack.pop();
		}
	}
}

/**
 *中序序遍歷(左根右)
 *
 *1、當前節點為非空,在訪問當前節點前要先訪問其左子節點,
 *   壓棧當前節點,判斷其左子結點,一直壓棧左子節點
 *2、當前節點為NULL,出棧訪問,其左子結點比當前節點出棧訪問早,
 *   此時當前節點是其右節點的父節點的角色,考慮其右節點
 *
 *在遍歷過程中角色轉換很重要
 *
 *@param t to visit
 *@param visit point to a func
 */
void in_order(link t, void (*visit)(link))
{
	std::stack<link> myStack;

	while( t || !myStack.empty() ) {

		if ( t ) {
			myStack.push(t);
			t = t->lchild;
		} else {
			t = myStack.top();
			myStack.pop();
			visit(t);
			t = t->rchild;
		}
	}
}

/**
 *後序遍歷(左右根)
 *
 *1、由於在訪問當前樹的根結點時,應先訪問其左、右子樹,因而先將根結點入棧,
 *   接著將右子樹也入棧,然後考慮左子樹,重複這一過程直到某一左子樹為空
 *2、如果當前考慮的子樹為空,
 *   1.若棧頂不為空,說明第二棧頂對應的樹的右子樹未處理,
 *   則彈出棧頂,下次迴圈處理,並將一空指標入棧以表示其另一子樹已做處理;
 *   2.若棧頂也為空樹,說明第二棧頂對應的樹的左右子樹或者為空,或者均已做處理,
 *   直接訪問第二棧頂的結點,訪問完結點後,若棧仍為非空,說明整棵樹尚未遍歷完,
 *   則彈出棧頂,併入棧一空指標表示第二棧頂的子樹之一已被處理。
 *
 *@param t to visit
 *@param visit point to a func
 */
void post_order(link t, void (*visit)(link))
{
	std::stack<link> myStack;

	while( 1 ) {
		if ( t ) {
			myStack.push(t);
			myStack.push(t->rchild);
			t = t->lchild;
		} else {
			t = myStack.top();
			myStack.pop();
			if (!t) {
				t = myStack.top();
				myStack.pop();
				visit(t);
				if (myStack.empty())
					break;
				t = myStack.top();
				myStack.pop();
			}
			myStack.push(NULL);
		}
	}
}
#endif

/**
 *層遍歷
 *
 *@param t to visit
 *@param visit point to a func
 */
void level_order(link t, void (*visit)(link))
{
	std::queue<link> myQueue;

	if (t) {
		myQueue.push(t);
		while( !myQueue.empty() ) {
			link tmp = myQueue.front();
			myQueue.pop();
			visit(tmp);
			if (tmp->lchild != NULL)
				myQueue.push(tmp->lchild);
			if (tmp->rchild != NULL)
				myQueue.push(tmp->rchild);
		}
	}
}

在非遞迴遍歷函式中我們用到了堆疊和佇列,為幾種注意力到一件事上,在堆疊和佇列的實現上,本人第一時間想到的是拿來主義,到github去下載別人的原始碼來實現.
下載了一個版本都沒達到想要的效果,於是乎目標轉移到C++ STL上,最終版本是在C原始檔上實現二叉樹的遞迴函式,在CPP檔案中實現二叉樹的非遞迴函式.所以在make的時候定義一個變數recu=y來應用遞迴函式,其他情況則應用非遞迴函式。

由於C與C++公用,那麼咱們的標頭檔案就要動點手腳了,否就會有意外情況出現

/* binarytree.h */
#ifndef BINARYTREE_H
#define BINARYTREE_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct node *link;
/**
 *節點中的資料型別重定義
 */
typedef unsigned char TElemType;

struct node { 
	TElemType item; 
	link lchild, rchild;
};

link init(TElemType VLR[], TElemType LVR[], int n);

void pre_order(link t, void (*visit)(link));
void in_order(link t, void (*visit)(link));
void post_order(link t, void (*visit)(link));
#ifndef RECU
void level_order(link t, void (*visit)(link));
#endif

void pprint(link t);
int count(link t);
int depth(link t);
void destroy(link t);


/**
*http://www.cnblogs.com/bizhu/archive/2012/08/19/2646328.html 演算法圖解
*
*二叉排序樹(Binary Sort Tree)又稱二叉查詢樹(Binary Search Tree),亦稱二叉搜尋樹,
*它或者是一棵空樹;或者是具有下列性質的二叉樹:
*(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
*(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
*(3)左、右子樹也分別為二叉排序樹;
*(4)排序二叉樹的中序遍歷結果是從小到大排列的.
*
*二叉查詢樹相比於其他資料結構的優勢在於查詢、插入的時間複雜度較低,為O(log n)。
*二叉查詢樹是基礎性資料結構,用於構建更為抽象的資料結構,如集合、multiset、關聯陣列等。
*
*搜尋,插入,刪除的複雜度等於樹高,期望O(log n),最壞O(n)(數列有序,樹退化成線性表)
*改進版的二叉查詢樹可以使樹高為O(logn),如SBT,AVL,紅黑樹等.
*
*程式來源於Linux C程式設計一站式學習
*/
link bstSearch(link t, TElemType key);
link bstInsert(link t, TElemType key);
link bstDelete(link t, TElemType key);

/**
 *http://baike.baidu.com/view/593144.htm?fr=aladdin
 *平衡二叉樹
 */
#ifdef __cplusplus
}
#endif
#endif

對於__cplusplus這個玩意糾結了很久,模模糊糊知道他是幹什麼用的,具體放在什麼地方糾結了好一陣子,最後一狠心自己動手編譯試一下,暫且只定義響應的空函式,看看編譯連線是否OK,出人意料萬事OK。由此看來動手能力決定一切啊。

對Makefile檔案改動如下:

#if you want to use recursive func,please make recu=y
ifeq (y, $(recu))
	CFLAGS += -DRECU
endif

ifeq (y, $(debug))
	CFLAGS += -g
endif

CC = gcc
CPLUS = g++
CFLAGS += -Wall
TARGET = tree

all:$(TARGET)

.c.o:
	$(CC) $(CFLAGS) -o 
[email protected]
-c $< .cpp.o: $(CPLUS) $(CFLAGS) -o [email protected] -c $< $(TARGET): non_binarytree.o binarytree.o main.o $(CPLUS) $(CFLAGS) -o [email protected] $^ test: @./tree > result.txt && python result.py | tree -b2 .PHONY: all clean clean: $(RM) $(TARGET) *.o

在改動Makefile的工程中遇到了兩個問題:
1、本人認為主程式是C程式,所以在連線的時候用的是gcc,隨後爆出"undefinedreference to '__gxx_personality_v0' " 錯誤。
重來沒有遇到過,只有問度娘,得出的結果如下:
對於 C++ 程式,編譯的時候用 gcc 或者 g++ 都可以。但是在進行連線的時候最好用 g++,因為用 g++ 會自動進行 C++ 標準庫的連線;用 gcc 連線 C++ 程式也可以,但是需要人為指定連線 C++ 標準庫,否則就會出現 undefined reference to `__gxx_personality_v/0' 之類的錯誤。可見-lstdc++ 所對應的是標準C++庫
2、當定義RECU這個巨集後,發現函式重複定義,排查了一下函式定義,在C檔案中函式定義用"#dedef RECU  ****  #endif",在C++檔案中"#ifndef RECU   ...... #endif"隔開了呀,用"#warning()"新增編譯過程中的列印資訊,定義RECU與否總會編譯到非遞迴函式,我去,奇了怪了。
在仔細排查,發現編譯C++檔案時沒有定義RECU。
Makefile內容改動如下:

%.o:%.c
	$(CC) $(CFLAGS) -o [email protected] -c $<

%.o:%.cpp
	$(CPLUS) $(CFLAGS) -o [email protected] -c $<
================改成了================
.c.o:
	$(CC) $(CFLAGS) -o [email protected] -c $<

.cpp.o:
	$(CPLUS) $(CFLAGS) -o [email protected] -c $<

問題是解決了,但這兩種寫法有什麼不同還是沒有個所以然
======================================================================

指令碼是自動化的神器,輸出的序列手動用tree工具轉換成圖形確實麻煩,於是乎想到之前有個博友測試計算公式的效率時用到python:
1、python輸出計算所需要的引數,print函式就OK
2、測試程式用scanf判斷輸入的引數個數是否OK
3、實現就是python程式輸出的結構通過管道送到測試程式
自動化測試用這個方法可能湊效

寫個指令碼一行行的輸出測試程式的結果:

fp = open('result.txt')
for line in fp.readlines():
	print(line)

Makefile新增如下語句:

test:
	@./tree > result.txt && python result.py | tree -b2

測試只需make test就OK

相關推薦

學習

二叉樹遞迴遍歷可謂是學過資料結構的同仁都能想一下就能寫出來,但在應聘過程我們常常遇到的是寫出一個二叉樹非遞迴遍歷函式,接著上篇文章寫二叉樹的非遞迴遍歷,先難後易,一步一步的來.   先上程式碼: #include "binarytree.h" #include <

Java實現後序(好理解)

//不明白的大家可以一起討論!歡迎留言! /** * public class Node { public int data; //樹結點標號 public Node lchild;

對於三種方式的理解

解決二叉樹的很多問題的方案都是基於對二叉樹的遍歷。遍歷二叉樹的前序,中序,後序三大方法算是計算機科班學生必寫程式碼了。其遞迴遍歷是人人都能信手拈來,可是在手生時寫出非遞迴遍歷恐非易事。正因為並非易事,所以網上出現無數的介紹二叉樹非遞迴遍歷方法的文章。可是大家需要的真是那些非遞迴遍歷程式碼和講述嗎?程式碼早

深度

#include <stdio.h> #include <malloc.h> typedef int ElemType; #define MaxSize 50 typedef struct node { ElemType data; struc

資料結構——的建立與

#include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct Node{ //二叉樹的鏈式儲存結點 char data; struct Node *Lch

[linux]的建立及其(C語言實現)

基礎知識 二叉樹的特點: 每一個節點最多有兩棵子樹,所以二叉樹中不存在度大於2的節點,注意,是最多有兩棵,沒有也是可以的 左子樹和右子樹是有順序的,次序不能顛倒,這點可以在哈夫曼編碼中體現, 順序不同編碼方式不同 -即使樹中某個節點中只有一個子樹的花,也要區分它是左子樹還是右子樹

二級指標實現的構造以的三種具體程式碼實現

二級指標實現二叉樹的構造 首先二級指標作為函式引數的作用:在函式外部定義一個指標p,在函式內給指標賦值,函式結束後對指標p生效,那麼我們就需要二級指標。不懂沒有關係,繼續往下看~加油! 在如上的A指向B、B指向C的指向關係中,如果A、B、C都是變數,即C是普通變數,B是一級指標變數,其中存放

的建立及其(C語言實現)

最近在學習資料結構中樹的概念,遲遲不得入門,應該是自己的懶惰和沒有勤加練習導致的,以後應該多加練習 以下是我對二叉樹的一些總結內容 二叉樹的特點有: - 每一個節點最多有兩棵子樹,所以二叉樹中不存在度大於2的節點,注意,是最多有兩棵,沒有也是可以的 左子

的三種

#include <stdio.h> #include <stdlib.h>#define elemTpye inttypedef struct BitNode{elemTpye data;struct BitNode *lchild,*rchild;

資料結構(四)

void Inoder(Bitree root)//二叉樹的中序遍歷非遞迴 { IniStack(&S);//初始化一個棧 p=root; while(!isEmpty(S)||p!=NULL) { if(p!=NULL)//如果當前結點不為空進棧 { pu

資料結構三種

#include <stdio.h> #include <malloc.h> #define MAX 30 typedef struct TiNode {char date;struct TiNode *lchild;struct TiNode *r

的高度() .----美團面=----硬傷 當時沒有答上來

#include <queue> using namespace std; int calculateTreeHeight(Node *root) {  if(root == NULL)   return 0;  int visitedNumber = 0;

連結串列(鏈式)的建立

這裡我採用的是先序非遞迴建立二叉樹。思路很簡單: 首先要有一個結點陣列。 1.取第一個結點,是否為空,不是就作為樹根,壓棧,是空則樹根為空,結束。 2.取下一個結點a。 3.取棧頂結

的建立及

art 先序 popu dsm ostream != 方式 mat trac huangjing 二叉樹的的建立方式為前序 二叉樹有三種遍歷 前序遍歷(NLR) 中序遍歷(LNR) 興許遍歷(LRN) 非遞歸的算法明天補上 代碼為: #include<i

[LeetCode] Binary Tree Zigzag Level Order Traversal 字形層序

Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alt

資料結構篇卷三 -- (With Java)

Nonrecursive Traversal of Binary Tree First I wanna talk about why should we use <code>Stack</code> to implement this algorithm. I think it is

的通用演算法

二叉樹的3中遍歷策略,關鍵在於處理節點的時機不同:前序遍歷是遇到節點時處理,中序是處理完左節點後再處理,而後序是在處理完左右節點後再處理。 使用非遞迴方法實現時,除了記錄當前的節點的訪問棧,還需要記錄當前節點的狀態。對於每一個節點,我們用0來表示尚未處理左右子節點,1表示僅僅處理完畢左節點,2表

(★★★) (統一的解決思路)

轉載:【刷題】二叉樹非遞迴遍歷   stack<Node*> st; void preOrder(Node* root) { Node *cur = root; while (cur || !st.empty()) { while (

的前中後序版本)

各位讀者週末愉快呀,今天我想來說說一道很常見的面試題目 —— 關於二叉樹前中後序遍歷的實現。本文將以遞迴和非遞迴方式實現這 3 種遍歷方式,程式碼都比較短,可以放心食用。 先簡單說明一下這 3 種遍歷方式有什麼不同 —— 對於每種遍歷,樹中每個結點都需要經過 3 次(對於葉結點,其左右子樹視為空子樹),但前

【演算法】(轉)

原文地址 【寫在前面】   二叉樹是一種非常重要的資料結構,很多其它資料結構都是基於二叉樹的基礎演變而來的。對於二叉樹,有前序、中序以及後序三種遍歷方法。因為樹的定義本身就 是遞迴定義,因此採用遞迴的方法去實現樹的三種遍歷不僅容易理解而且程式碼很簡潔。而對於樹的遍歷若採用非遞迴的方法,就要採