算法與數據結構學習(38)-樹結構(遍歷線索化二叉樹)

說明:對上一節的中序線索化的二叉樹, 進行遍歷

分析:因爲線索化後,各個結點指向有變化,因此原來的遍歷方式不能使用,這時需要使用新的方式遍歷線索化二叉樹,各個節點可以通過線型方式遍歷,因此無需使用遞歸方式,這樣也提高了遍歷的效率。 遍歷的次序應當和中序遍歷保持一致。

代碼實現

遍歷線索化二叉樹的方法:threadeList()

public void threadeList() {
		//定義一個變量,存儲當前遍歷的結點,從root開始
		HeroNode node = root;
		while(node !=null) {
			//循環的找到leftTYpe==1的結點
			//後面隨着遍歷而變化,因爲當lieftType==1的時候該節點是按着線索化處理後的有效結點
			while(node.getLeftType() == 0) {
				node = node.getLeft();
			}
			//打印當前結點
			System.out.println(node);
			//如果當前結點的有指針指向的是後繼結點,就一直輸出
			while(node.getRightType() == 1) {
				//獲取當前結點的後繼結點
				node = node.getRight();
				System.out.println(node);
			}
			
			//替換遍歷的結點
			node = node.getRight();
		}
	}
package treeThreadeBinaryTree;

public class ThreadeBinaryTreeDemo {

	public static void main(String[] args) {
		
		//測試中序線索化二叉樹
		
		HeroNode root = new HeroNode(1, "tom");
		HeroNode node2 = new HeroNode(3, "jack");
		HeroNode node3 = new HeroNode(6, "smith");
		HeroNode node4 = new HeroNode(8, "mary");
		HeroNode node5 = new HeroNode(10, "king");
		HeroNode node6 = new HeroNode(14, "dim");
		
		//二叉樹後面我們要遞歸創建,現在手動創建
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(node6);
		
		
		//測試線索化
		ThreadeBinarTree threadeBinarTree = new ThreadeBinarTree();
		threadeBinarTree.setRoot(root);
		threadeBinarTree.threadeNodes();
		
		//測試,以10號測試
		HeroNode leftNode1=node5.getLeft();
		HeroNode rightNode1=node5.getRight();
		System.out.println("10號結點的前驅結點爲="+leftNode1);
		System.out.println("10號結點的後繼結點爲="+rightNode1);
		
		//當線索化二叉樹後不能使用原來的遍歷方式
		System.out.println("使用線索化的方式遍歷線索二叉樹");
		threadeBinarTree.threadeList();
		

	}

}

//定義ThreadeBinarTree實現了線索化功能的二叉樹
class ThreadeBinarTree{
	private HeroNode root;
	
	//爲了實現線索化,需要創建一個當前結點的前驅結點的指針
	//在遞歸進行線索化的時候,pre總是保留前一個結點
	private HeroNode pre = null;
	
	public void setRoot(HeroNode root) {
		this.root = root;
		
	}
	
	
	//重載線索化的方法
	public void threadeNodes() {
		this.threadeNodes(root);
	}
	
	//遍歷線索化二叉樹的一個方法
	public void threadeList() {
		//定義一個變量,存儲當前遍歷的結點,從root開始
		HeroNode node = root;
		while(node !=null) {
			//循環的找到leftTYpe==1的結點
			//後面隨着遍歷而變化,因爲當lieftType==1的時候該節點是按着線索化處理後的有效結點
			while(node.getLeftType() == 0) {
				node = node.getLeft();
			}
			//打印當前結點
			System.out.println(node);
			//如果當前結點的有指針指向的是後繼結點,就一直輸出
			while(node.getRightType() == 1) {
				//獲取當前結點的後繼結點
				node = node.getRight();
				System.out.println(node);
			}
			
			//替換遍歷的結點
			node = node.getRight();
		}
	}
	
	//編寫對二叉樹中序線索化的方法
	/**
	 * 
	 * @param node	需要線索化的結點
	 */
	public void threadeNodes(HeroNode node) {
		//如果node == null不能線索化
		if(node == null) {
			return ;
		}
		
		//(一),先線索化左子樹
		threadeNodes(node.getLeft());
		//(二)線索化當前結點
		
		//處理當前結點的前驅結點
		if(node.getLeft() == null) {
			//讓當前結點的左指針指向前驅結點
			node.setLeft(pre);
			//修改當前結點的左指針類型
			node.setLeftType(1);
		}
		
		//處理後繼結點
		if(pre !=null && pre.getRight() == null) {
			//讓前驅結點的右指針指向當前結點
			pre.setRight(node);
			pre.setRightType(1);
		}
		
		//!!!!!!重要:每次處理一個結點後,讓當前結點指向下一個結點的前驅結點
		pre = node;
		//(三)線索化右子樹
		threadeNodes(node.getRight());
	}
	//刪除結點
	public void delNode(int no) {
		if(this.root != null) {
			//如果只有一個root,立即判斷root是不是就要刪除的結點
			if(root.getNo() == no) {
				root = null;
			}else {
				//遞歸刪除
				root.delNode(no);
			}
		}else {
			System.out.println("空樹無法刪除");
		}
	}
	
	//前序遍歷
	public void preOrder() {
		if(this.root != null) {
			this.root.preOrder();
		}else {
			System.out.println("二叉樹爲空,無法遍歷");
		}
	}
	
	//中序遍歷
	public void infixOrder() {
		if(this.root != null) {
			this.root.infixOrder();
		}else {
			System.out.println("二叉樹爲空,無法遍歷");
		}
	}
	
	//後序遍歷
	public void postOrder() {
		if(this.root != null) {
			this.root.postOrder();
		}else {
			System.out.println("二叉樹爲空,無法遍歷");
		}
	}
	
	//調用前序遍歷查找
	public HeroNode preOrdersearch(int no) {
		if(root != null) {
			return root.preOrdersearch(no);
		}else {
			return null;
		}
	}
	
	//調用中序遍歷查找
	public HeroNode infixOrdersearch(int no) {
		if(root != null) {
			return root.infixOrdersearch(no);
		}else {
			return null;
		}
	}
	
	//調用後序遍歷查找
		public HeroNode postOrdersearch(int no) {
			if(root != null) {
				return root.postOrdersearch(no);
			}else {
				return null;
			}
		}
}


//創建HeroNode
//先創建HeroNode結點
class HeroNode {
	private int no;
	private String name;
	private HeroNode left;// 默認null
	private HeroNode right;// 默認null
	
	//定義兩個屬性
	//規定:
	//1.如果leftType==0,表示指向左子樹,如果爲1表示指向前驅結點
	//2.如果rightType==0,表示指向右子樹,如果爲1表示指向後繼結點
	private int leftType;
	private int rightType;
	
	

	public int getLeftType() {
		return leftType;
	}

	public void setLeftType(int leftType) {
		this.leftType = leftType;
	}

	public int getRightType() {
		return rightType;
	}

	public void setRightType(int rightType) {
		this.rightType = rightType;
	}

	public HeroNode(int no, String name) {
		super();
		this.no = no;
		this.name = name;
	}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public HeroNode getLeft() {
		return left;
	}

	public void setLeft(HeroNode left) {
		this.left = left;
	}

	public HeroNode getRight() {
		return right;
	}

	public void setRight(HeroNode right) {
		this.right = right;
	}

	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + "]";
	}

	// 遞歸刪除結點
	// 規定
	// 1.如果刪除的節點是葉子節點,則刪除該節點
	// 2.如果刪除的節點是非葉子節點,則刪除該子樹
	public void delNode(int no) {
		/**
		 * 思路: 1.因爲我們的二叉樹是單向的,所以我們是判斷當前結點的子節點是否是需要刪除的結點,而不能判斷當前的結點是不是需要刪除的結點
		 * 2.如果當前的結點的做子節點不爲空,並且左子節點就是要刪除的結點,就將this.left = null;並且返回(遞歸刪除結束)
		 * 3.如果當前結點的右子節點不爲空,並且右子節點就是要刪除的結點,就愛那個this.right = null;並且就返回(結束遞歸刪除)
		 * 4.如果第2步和第3步沒有刪除結點,那我們就需要向左子樹遞歸刪除 5.如果第4不也沒有 刪除結點,則應當向右子樹進行遞歸刪除。
		 */
		// 2.如果當前的結點的做子節點不爲空,並且左子節點就是要刪除的結點,就將this.left = null;並且返回(遞歸刪除結束)
		if (this.left != null && this.left.no == no) {
			this.left = null;
			return;
		}
		// 3.如果當前結點的右子節點不爲空,並且右子節點就是要刪除的結點,就愛那個this.right = null;並且就返回(結束遞歸刪除)
		if (this.right != null && this.right.no == no) {
			this.right = null;
			return;
		}
		// 4.如果第2步和第3步沒有刪除結點,那我們就需要向左子樹遞歸刪除
		if (this.left != null) {
			this.left.delNode(no);
		}

		// 5.如果第4不也沒有 刪除結點,則應當向右子樹進行遞歸刪除。
		if (this.right != null) {
			this.right.delNode(no);
		}
	}

	// 編寫前序遍歷方法
	public void preOrder() {
		System.out.println(this);// 先輸出父節點
		// 遞歸向左子樹序遍遍歷
		if (this.left != null) {
			this.left.preOrder();
		}
		// 遞歸向右子樹序遍遍歷
		if (this.right != null) {
			this.right.preOrder();
		}
	}

	// 中序遍歷
	public void infixOrder() {
		// 遞歸左子樹中序遍歷
		if (this.left != null) {
			this.left.infixOrder();
		}
		// 輸出父節點
		System.out.println(this);
		// 遞歸向右子樹中序遍歷
		if (this.right != null) {
			this.right.infixOrder();
		}
	}

	// 後序遍歷
	public void postOrder() {
		// 遞歸左子樹後序遍歷
		if (this.left != null) {
			this.left.postOrder();
		}
		// 遞歸向右子樹後序遍歷
		if (this.right != null) {
			this.right.postOrder();
		}
		// 輸出父節點
		System.out.println(this);
	}

	// 前序遍歷查找
	/**
	 * 
	 * @param no 要查找的no
	 * @return 如果找到就返回node,如果找不到就返回null
	 */
	public HeroNode preOrdersearch(int no) {
		System.out.println("前序遍歷ing");
		// 比較當前結點是不是
		if (this.no == no) {
			return this;
		}

		// 判斷當前結點的左節點是否爲空,如果不爲空,則遞歸前序查找
		// 如果左遞歸前序查找找到該結點,則返回
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.preOrdersearch(no);
		}
		if (resNode != null) {// 說明左子樹找到
			return resNode;
		}

		// 1.如果左遞歸前序查找找到該結點,則返回
		// 2.如果左遞歸前序查找找到該結點,則返回,
		// 否則繼續判斷當前節點的右子節點是否爲空,如果不爲空,則繼續向右遞歸前序查找
		if (this.right != null) {
			resNode = this.right.preOrdersearch(no);
		}
		return resNode;
	}

	// 中序遍歷查找
	public HeroNode infixOrdersearch(int no) {

		// 則判斷當前結點的左節點是否爲空,如果不爲空,則遞歸中序查找
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.infixOrdersearch(no);
		}
		if (resNode != null) {
			return resNode;
		}
		// 如果找到就返回,如果沒有找到就和當前結點作比較,如果相等則返回當前結點,
		System.out.println("中序遍歷ing");
		if (this.no == no) {
			return this;
		}

		// 否則繼續右遞歸中序
		if (this.right != null) {
			resNode = this.right.infixOrdersearch(no);
		}

		return resNode;

	}

	// 後序遍歷查找
	public HeroNode postOrdersearch(int no) {

		// .判斷當前結點的左節點是否爲空,如果不爲空,則遞歸後序查找
		HeroNode resNode = null;
		if (this.left != null) {
			resNode = this.left.postOrdersearch(no);
		}
		if (resNode != null) {
			return resNode;
		}

		// 如果左子樹沒有找到,繼續向右子樹遞歸後序查找
		if (this.right != null) {
			resNode = this.right.postOrdersearch(no);
		}
		if (resNode != null) {
			return resNode;
		}

		// 如果左右子樹都沒有找到,比較當前結點是不是
		System.out.println("後序遍歷ing");
		if (this.no == no) {
			return this;
		}
		return resNode;

	}

}

```![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20200311201358942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L216Y19sb3Zl,size_16,color_FFFFFF,t_70)

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