1. 程式人生 > 實用技巧 >淺談遞迴--二叉樹遞迴套路(純個人理解)

淺談遞迴--二叉樹遞迴套路(純個人理解)

從大一接觸遞迴開始就覺得遞迴很難,然後就一直避開遞迴這個大頭,最近重拾遞迴,對遞迴有一些更深入的理解了。此篇僅供參考,如有說錯的地方,可以在評論區和我一起探討(這個方法是我學牛客網的左程雲老師的,很推薦大家去報名他的課,不是廣告,大家根據自己的經濟實力去決定)。

首先我們從最簡單的遞迴開始--階乘

public static int fn(int x){
	if (x == 1) {
	     return 1;
	}
	return x*fn(x-1);
}

遞迴的全過程就如下所示。

-->6*fn(5)

-->6*(5*fn(4)

-->6*(5*(4*fn(3)))

-->6*(5*(4*(3*fn(2))))

-->6*(5*(4*(3*(2*fn(1))))))

-->6*(5*(4*(3*(2*1))))

-->6*(5*(4*(3*2)))

-->6*(5*(4*6))

-->6*(5*24)

-->6*120

-->720

遞迴也就是系統自動幫我們壓棧的過程,我們把前面還沒辦法計算出結果的計算壓入棧,等遇到base case(return語句)時,就會彈出一個計算結果,最後一個個依次彈出,這就是整個遞迴的過程。這一段需要自己去畫個棧結果,畫出依次壓入棧和彈棧的過程,才有更深刻的理解。

在這裡因為比較簡單,所以大家應該都能理解,但到了更加複雜的情況的時候,我們會發現,遞迴像玄學一樣無法理解,為什麼突然就能得出結果了。在這裡我先介紹一下二叉樹的遞迴套路。

(二叉樹的遞迴遍歷也能幫助我們去理解遞迴,我們可以去手動去試試看)

首先二叉樹的遞迴套路,就是建立一個類為返回型別,而每一次遞迴都會返回一個“返回型別(ReturnType)",通過這個返回型別,我們就能很簡單地得到結果。我把程式碼放出來大家就懂了。我先舉例一個比較簡單的,判斷一顆二叉樹是否為平衡二叉樹,而判斷是否為平衡二叉樹,我們就需要去判斷每一個節點的左右孩子的高度差,這就很符合我們的遞迴思想,所以我們去遞迴每個節點的時候,我們就返回兩個關鍵資訊,該節點的左孩子是否為平衡二叉樹(boolean isB)和左孩子的最大高度(int hight),該節點的右孩子是否為平衡二叉樹(boolean isB)和右孩子的最大高度(int hight)。

public class IsBanlenceTree {
	private static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}
	private static class ReturnType{
		private boolean isB;
		private int h;
		private ReturnType(Boolean isB,int h){
			this.isB = isB;
			this.h = h;
		}
	}
	private static boolean isB(Node head){
		return process(head).isB;
		
	}
	private static ReturnType process(Node head){
		//base case
		if(head == null){
			return new ReturnType(true, 0);
		}
		//黑盒
		ReturnType leftReturnType = process(head.left);
		if (!leftReturnType.isB) {
			return new ReturnType(false, 0);
		}
		ReturnType rightReturnType = process(head.right);
		if (!rightReturnType.isB) {
			return new ReturnType(false, 0);
		}
		//解黑盒過程
		if (Math.abs(leftReturnType.h - rightReturnType.h)>2 ) {
			return new ReturnType(false, 0);
		}
		int h = Math.max(leftReturnType.h, rightReturnType.h);
		return new ReturnType(true, h+1);
	}
}

這個遞迴方法有三個重要的部分,base case 和遞迴和遞迴返回值,我們可以把這的遞迴當成黑盒去理解,我們先不管為什麼它就會返回值,而“遞迴返回值”的部分也就是我們解黑盒的過程。這就是二叉樹遞迴套路的“理解套路”。但是我在一開始接觸的時候,就很疑惑為什麼這麼遞迴,就能得到我們想要的值,這樣看來遞迴確實是很像玄學。但是我們剛剛說了,遞迴其實就是系統幫我們自動壓棧的過程,所以我們自己去理解的時候,可以畫出一個棧結果出來。

假設我們的二叉樹是長這個樣子

那我們遞迴壓棧過程如下所示

當壓到這裡的時候再繼續要,我們壓入的節點就為空,此時就出現了返回值。

當發生彈棧時,就會繼續進行到ReturnType rightReturnType = process(head.right),而此時的head.right也為空,這時也彈出一個返回型別return new ReturnType(true, 0);而這時你就能理解接黑盒的過程了。

if (Math.abs(leftReturnType.h - rightReturnType.h)>2 ) {
	return new ReturnType(false, 0);
}
   int h = Math.max(leftReturnType.h, rightReturnType.h);
   return new ReturnType(true, h+1);

  這裡的最後一個返回值為什麼它就有值,就是因為我們的棧節點④彈出了它左右孩子的兩個關鍵資訊,此時左右孩子都為平衡二叉樹,他們的最大高度為0,加上自己就為1。所以節點④也返回資訊給到它的父節點②,父節點②如果得到了它的右孩子的資訊,那麼它就有能得到自己的返回型別。這就是遞迴的過程。

而類似的題還有,找到二叉樹的最大距離和晚會最大活躍度。

二叉樹的最大距離的解題思路就是,我需要左樹的資訊,也需要右樹的資訊,存在兩個情況,

①X不參與:Math.max(左樹得到的最大距離,右樹得到的最大距離)

②X參與:左樹的高+右樹的高

所以我們的ReturnType需要有最大距離和最大高度,程式碼如下(以下是左程雲老師的程式碼)

public class MaxDistanceInTree {
	public static class Node {
		public int value;
		public Node left;
		public Node right;
		public Node(int data) {
			this.value = data;
		}
	}
	public static class ReturnType{
		public int maxDistance;
		public int h;
		public ReturnType(int m,int h){
			this.maxDistance = m;
			this.h = h;
		}	
	}

	public static ReturnType maxDistance(Node head) {
		return process(head);
	}
	
	public static ReturnType process(Node head){
		if (head == null) {
			return new ReturnType(0, 0);
		}
		ReturnType leftReturnType = process(head.left);
		ReturnType rightReturnType = process(head.right);
		//三種情況
		int includeHeadDistance = leftReturnType.h +rightReturnType.h +1;
		int p1 = leftReturnType.maxDistance;
		int p2 = rightReturnType.maxDistance;
		//解黑盒
		int resultDistance = Math.max(Math.max(p1,p2),includeHeadDistance);//找到三種情況最大的
		int hitself = Math.max(leftReturnType.h,rightReturnType.h) + 1;//這裡要計算自己的深度是為了遞迴的時候用到
		return new ReturnType(resultDistance, hitself);
	}

  

我每次理解的時候,都會去想壓棧到最後一個彈出棧時彈出的是什麼,由此去想我們解黑盒是要怎麼做,這是我去理解二叉樹遞迴套路的思路,每個人都有自己的理解,你也可以找到適合自己的方式。(最後,推薦一下左程雲老師的課,因為貼了老師的程式碼,而且左程雲老師確實講的特別好,有條件的小夥伴可以去了解一下https://www.nowcoder.com/courses/cover/live/429