1. 程式人生 > >使用java實現二叉樹的非遞迴遍歷

使用java實現二叉樹的非遞迴遍歷

在前面的一片部落格中已經介紹了二叉樹遍歷的一些概念以及注意事項,如果有疑惑的可以回過頭看一看。

這裡我們主要討論的是使用非遞迴的演算法實現二叉樹的遍歷

前序遍歷:

思路:

1.使用一個棧來儲存元素,剛開始的時候將棧頂元素壓入棧

2.當棧不為空的時候做如下操作:彈出棧頂元素並遍歷,依次將出棧結點的右孩子和左孩子入棧

程式碼:

//非遞迴前序遍歷二叉樹
	public static void printTree1(Node root){
		SequenceStack<Node> stack=new SequenceStack<Node>();
		List<Node> list=new ArrayList<Node>();
		//首先先把根節點壓棧
		stack.push(root);
		//當棧不為空的時候迴圈遍歷
		while(!stack.isEmpty()){
			//彈出棧頂元素
			Node node=stack.pop();
			//遍歷棧頂元素
			list.add(node);
			//如果棧頂元素的右孩子不為空,則進棧
			if(node.right!=null){
				stack.push(node.right);
			}
			//如果棧頂元素的左孩子不為空,則進棧
			if(node.left!=null){
				stack.push(node.left);
			}
			
		}
		System.out.println(list);
	}

中序遍歷:

思路:

1.剛開始先把根節點壓入棧,往後每次判斷節點不為空則入棧

2.彈出棧頂元素並遍歷,判斷棧頂元素是否存在右孩子結點,根據結果進行相應的操作

             有:繼續將右孩子結點壓棧

             無:繼續出棧

程式碼:

//採用非遞迴中序遍歷二叉樹
	public static void printTree(Node root){
		SequenceStack<Node> stack=new SequenceStack<Node>();
		List<Node> list=new ArrayList<Node>();
		Node node=root;
		//剛開始先把根節點壓入棧,往後每次判斷節點不為空則壓棧
		do {
			while(node!=null){
				stack.push(node);
				node=node.left;
			}
			node=stack.pop();
			list.add(node);
			//如果出棧的結點存在右節點,則指向該節點
			if(node.right!=null){
				node=node.right;
			}else{//否則設定為空,下次迴圈繼續出棧
				node=null;
			}//當棧不為空,或者當前節點引用不為空時迴圈
		} while (!stack.isEmpty()||node!=null);
		System.out.println(list);
	}

後序遍歷:

思路:

1.將根節點以及左結點依次入棧

2.獲取棧頂元素,對棧頂元素進行右孩子判斷,諾為空,則遍歷棧頂元素並彈出棧,否則指向右孩子結點

3.重複1-2兩步

      重點:這裡需要注意pre變數在遍歷過程中的作用(下方程式碼有詳細註釋該變數的作用)

程式碼:

//非遞迴實現後序遍歷
	public static void printTree2(Node root){
		SequenceStack<Node> stack=new SequenceStack<Node>();
		List<Node> list=new ArrayList<Node>();
		//定義兩個節點變數node和pre(這裡需要注意pre節點的作用,下方的註釋有詳細地介紹)
		Node node=root;
		Node pre=null;
		do {
			//將左結點一次壓入棧
			while(node!=null){
				stack.push(node);
				node=node.left;
			}
			//獲取棧頂節點
			node = stack.get();
			//如果棧頂節點的右孩子不為空,說明還得把右子樹壓入棧中,這裡需要注意
			//node.right!=pre這個條件,因為我們在遍歷的過程中,對於(子樹)根節點的判斷會存在兩次
			//第一次是彈出左孩子節點後,對根節點進行是否有右孩子的判斷,如果有,則將右孩子壓棧
			//第二次是彈出右孩子節點後,這時候因為迴圈的原因(程式碼的原因),我們再次對根節點進行了右孩子判斷,
			//所以這裡就必須得判斷該右孩子節點是否在之前的迴圈中已經判斷過了,如果判斷過了,則彈出根節點,否則壓入右孩子節點。
			//總的來說,pre節點的作用是用來防止重複遍歷右孩子節點的。
			if(node.right!=null&&node.right!=pre){
				//node指向右孩子節點
				node=node.right;
			}else{//只要有一個條件不滿足則執行
				//彈出棧頂元素
				node=stack.pop();
				//遍歷棧頂元素
				list.add(node);
				//將pre指向當前彈出的元素,用來做下次對根節點的再次判斷時,右孩子不重複遍歷的一個條件
				pre=node;
				//將node設定為null,防止根節點重複壓入左孩子節點。
				node=null;
			}
			
		} while (node!=null||!stack.isEmpty());
		System.out.println(list);
	}