數據結構基礎之樹、二叉樹

博客:http://blog.csdn.net/zhangerqing(轉載請說明出處)

        本文基礎知識,準備面試的可以樓一眼。

      我們接着上一篇數據結構繼續講解。本章系數據結構之樹與二叉樹,從這章開始,我們就要介紹非線性結構了,這些內容理解起來比線性表稍難一些,我儘量寫的通俗一些,如果讀的過程中有任何問題,請按上述方式聯繫我!

一、樹

      樹形結構是一類重要的非線性結構。樹形結構是結點之間有分支,並具有層次關係的結構。它非常類似於自然界中的樹。樹結構在客觀世界中是大量存在的,例如家譜、行政組織機構都可用樹形象地表示。樹在計算機領域中也有着廣泛的應用,例如在編譯程序中,用樹來表示源程序的語法結構;在數據庫系統中,可用樹來組織信息;在分析算法的行爲時,可用樹來描述其執行過程。本章重點討論二叉樹的存儲表示及其各種運算,並研究一般樹和森林與二叉樹的轉換關係,最後介紹樹的應用實例。

二、二叉樹

        二叉樹(BinaryTree)是n(n≥0)個結點的有限集,它或者是空集(n=0),或者由一個根結點及兩棵互不相交的、分別稱作這個根的左子樹右子樹的二叉樹組成。關於更多概念,請大家自己上網查詢,我們這裏將用代碼實現常見的算法。更多的概念,請訪問:http://student.zjzk.cn/course_ware/data_structure/web/SHU/shu6.2.3.1.htm

1、二叉樹的建立

首先,我們採用廣義表建立二叉樹(關於廣義表的概念,請查看百科的介紹:http://baike.baidu.com/view/203611.htm)

我們建立一個字符串類型的廣義表作爲輸入:

String  expression = "A(B(D(,G)),C(E,F))";與該廣義表對應的二叉樹爲:


寫代碼前,我們通過觀察二叉樹和廣義表,先得出一些結論:

  • 每當遇到字母,將要創建節點
  • 每當遇到“(”,表面要創建左孩子節點
  • 每當遇到“,”,表明要創建又孩子節點
  • 每當遇到“)”,表明要返回上一層節點
  • 廣義表中“(”的數量正好是二叉樹的層數

根據這些結論,我們基本就可以開始寫代碼了。首先建議一個節點類(這也屬於一種自定義的數據結構)。

package com.xtfggef.algo.tree;

public class Node {

	private char data;
	private Node lchild;
	private Node rchild;

	public Node(){
		
	}
	public char getData() {
		return data;
	}

	public void setData(char data) {
		this.data = data;
	}

	public Node getRchild() {
		return rchild;
	}

	public void setRchild(Node rchild) {
		this.rchild = rchild;
	}

	public Node getLchild() {
		return lchild;
	}

	public void setLchild(Node lchild) {
		this.lchild = lchild;
	}

	public Node(char ch, Node rchild, Node lchild) {
		this.data = ch;
		this.rchild = rchild;
		this.lchild = lchild;
	}

	public String toString() {
		return "" + getData();
	}
}
根據廣義表創建二叉樹的代碼如下:

public Node createTree(String exp) {
		Node[] nodes = new Node[3];
		Node b, p = null;
		int top = -1, k = 0, j = 0;
		char[] exps = exp.toCharArray();
		char data = exps[j];
		b = null;
		while (j < exps.length - 1) {
			switch (data) {
			case '(':
				top++;
				nodes[top] = p;
				k = 1;
				break;
			case ')':
				top--;
				break;
			case ',':
				k = 2;
				break;
			default:
				p = new Node(data, null, null);
				if (b == null) {
					b = p;
				} else {
					switch (k) {
					case 1:
						nodes[top].setLchild(p);
						break;
					case 2:
						nodes[top].setRchild(p);
						break;
					}
				}
			}
			j++;
			data = exps[j];
		}
		return b;
	}
思路不難,結合上述的理論,自己斷點走一遍程序就懂了!

2、二叉樹的遞歸遍歷

二叉樹的遍歷有三種:先序、中序、後序,每種又分遞歸和非遞歸。遞歸程序理解起來有一定的難度,但是實現起來比較簡單。對於上述二叉樹,其:

    a 先序遍歷

            A B D G C E F

    b 中序遍歷

           D G B A E C F 

    c 後序遍歷

           G D B E F C A

先、中、後序遞歸遍歷如下:

/**
	 * pre order recursive
	 * 
	 * @param node
	 */
	public void PreOrder(Node node) {
		if (node == null) {
			return;
		} else {
			System.out.print(node.getData() + " ");
			PreOrder(node.getLchild());
			PreOrder(node.getRchild());

		}
	}

	/**
	 * in order recursive
	 * 
	 * @param node
	 */
	public void InOrder(Node node) {
		if (node == null) {
			return;
		} else {
			InOrder(node.getLchild());
			System.out.print(node.getData() + " ");
			InOrder(node.getRchild());
		}
	}

	/**
	 * post order recursive
	 * 
	 * @param node
	 */
	public void PostOrder(Node node) {
		if (node == null) {
			return;
		} else {
			PostOrder(node.getLchild());
			PostOrder(node.getRchild());
			System.out.print(node.getData() + " ");
		}
	}
二叉樹的遞歸遍歷實現起來很簡單,關鍵是非遞歸遍歷有些難度,請看下面的代碼:

3、二叉樹的非遞歸遍歷

先序非遞歸遍歷:

public void PreOrderNoRecursive(Node node) {
		Node nodes[] = new Node[CAPACITY];
		Node p = null;
		int top = -1;
		if (node != null) {
			top++;
			nodes[top] = node;
			while (top > -1) {
				p = nodes[top];
				top--;
				System.out.print(p.getData() + " ");
				if (p.getRchild() != null) {
					top++;
					nodes[top] = p.getRchild();
				}
				if (p.getLchild() != null) {
					top++;
					nodes[top] = p.getLchild();
				}
			}
		}
	}
      原理:利用一個棧,先序遍歷即爲根先遍歷,先將根入棧,然後出棧,凡是出棧的元素都打印值,入棧之前top++,出棧之後top--,利用棧後進先出的原理,右節點先於左節點進棧,根出棧後,開始處理左子樹,然後是右子樹,讀者朋友們可以自己走一遍程序看看,也不算難理解!

中序非遞歸遍歷:

public void InOrderNoRecursive(Node node) {
		Node nodes[] = new Node[CAPACITY];
		Node p = null;
		int top = -1;
		if (node != null)
			p = node;
		while (p != null || top > -1) {
			while (p != null) {
				top++;
				nodes[top] = p;
				p = p.getLchild();
			}
			if (top > -1) {
				p = nodes[top];
				top--;
				System.out.print(p.getData() + " ");
				p = p.getRchild();
			}
		}
	}
原理省略。

後續非遞歸遍歷:

public void PostOrderNoRecursive(Node node) {
		Node[] nodes = new Node[CAPACITY];
		Node p = null;
		int flag = 0, top = -1;
		if (node != null) {
			do {
				while (node != null) {
					top++;
					nodes[top] = node;
					node = node.getLchild();
				}
				p = null;
				flag = 1;
				while (top != -1 && flag != 0) {
					node = nodes[top];
					if (node.getRchild() == p) {
						System.out.print(node.getData() + " ");
						top--;
						p = node;
					} else {
						node = node.getRchild();
						flag = 0;
					}
				}
			} while (top != -1);
		}
	}

三、樹與二叉樹的轉換

本人之前總結的:



這部分概念的其他知識,請讀者自己上網查看。

 

看這種文章,讓我回想起大學期末考試前突擊的場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章