牛客算法--第七章

牛客算法–第七章

題目一
分別用遞歸和非遞歸方式實現二叉樹先序、中序和後序遍歷
【題目】
用遞歸和非遞歸方式,分別按照二叉樹先序、中序和後序打印所有的節點。我們約定:先序遍歷順序爲根、左、右;中序遍歷順序爲左、根、右;後序遍歷順序爲左、右、根。

前序遍歷爲“根左右”:

遞歸C++版本:

void preorder(node* root) 
{
	if(root==NULL)
	{
		return;	//到達空樹,遞歸邊界 
	}
	
	//訪問根結點root,例如將其數據域輸出 
	printf("%d\n",root->value);
	
	//訪問左子樹
	preorder(root->lchild);
	
	//訪問右子樹
	preorder(root->rchild); 
	 
	
	
}

非遞歸JAVA版本:


public static void preOrderUnRecur(Node head){
	
	System.out.print("preorder: ");
	
	if(head != null)
	{
		Stack<Node> stack = new Stack<Node>();
		stack.add(head);
		
		while(!stack.isEmpty()){
			
			head = stack.pop();
			Systemp.out.print(head.value + " ");
			
			if(head.right != null)
			{
				stack.push(head.right);
				
			}
			
			if(head.left != null)
			{
				stack.push(head.left)
				
			}
			
			
		}
		
		
		
		
	}
	
	
	System.out.println();
	
	
}

非遞歸版本核心思想就是用棧。
因爲先序遍歷的要求是“根左右”,而棧的結構是完成數據先進後出的。所以我們應該先判斷一個結點右子樹是否爲空,如果不爲空,就將其壓入棧中;然後再判斷一個結點左子樹是否爲空,如果不爲空,就將其壓入棧中。這樣就保證了一個結點的左子樹先於右子樹彈出棧。我們遍歷的過程就是彈出棧的過程,我們總是彈出棧頂最高的結點,如果該結點有左右子結點,就將其子結點按“先右後左”的順序壓入棧中;如果沒有的話,也就是葉子結點,因爲有if語句的判斷作用,它沒有子結點會在這一回合壓入,並且會將其自身從棧中彈出。

中序遍歷爲“左根右”:

遞歸C++版本:

void inorder(node* root)
{
	
	if(root == NULL)
	{
		return;
	}
	
	inorder(root->lchild);
	
	printf("%d\n",root->value);
	
	inorder(root->rchild);
	
	
	
	
}


非遞歸JAVA版本:



public static void inOrderUnRecur(Node head){
	
	System.out.print("inorder: ");
	
	if(head != null)
	{
		
		Stack<Node> stack = new Stack<Node>();
		
		while(!stack.isEmpty() || head != null ){
			
			if(head != null){
				
				stack.push(head);
				head = head.left;
				
			}else{
				
				head = stack.pop();
				System.out.print(head.value + " " );
				head = head.right;
				
				
			}
			

		}

	}
	
	System.out.println();
	
}

非遞歸中序遍歷的順序因爲是“左根右”,我們實際上把一顆樹想成下面:

    /
   /    /
  /    / /
 / /    /

就是想成一排排左斜線的樣子。
就是有一絲絲“左傾”的分子都要讓他入棧。啥意思呢?就是第一優先級是“左左”,第二優先級是“右左”。

後序遍歷爲“左右根”:

遞歸C++版本:

void postorder(node* root)
{
	if(root == NULL)
	{
		return;
		
	} 
	
	postorder(root->lchild);
	postorder(root->rchild);
	
	printf("%d\n",root->value);
	
	
}



非遞歸JAVA版本:

非遞歸後序遍歷的核心思想是用兩個棧stack1和stack2.後序遍歷要求的是“左右根”,我們看一下它的逆序,就是“根右左”,那麼,如何做呢?就是前面非遞歸先序遍歷中,棧中先壓右後壓左,最終實現了“根左右”,現在我們棧中先壓左後壓右,自然就可以得到“根右左”了。接下來第二個stack2的作用實際上就是起一個逆序的作用,最終就可以得到“左右根”的效果了。當然,實現逆序,也不一定用棧,我們可以存儲在容器中,然後進行reverse就可以了。

提前預告,下面的題目二和題目三都是套路題,要掌握。

題目二
找到二叉樹中的最大搜索二叉子樹
【題目】
給定一棵二叉樹的頭節點 head,已知其中所有節點的值都不一樣,找到含有節點最多的搜索二叉子
樹,並返回這棵子樹的頭節點。
【要求】
如果節點數爲 N,要求時間複雜度爲 O(N),額外空間複雜度爲 O(h),h 爲二叉樹的高度。

首先,要弄明白最大子樹最大拓撲結構的**區別。**一個點包括左右子樹都滿足條件,這個點爲根結點的樹纔算是一顆符合條件的子樹。

假如我們有一個函數F(…)可以返回以下4個信息:

1.最大搜索子樹頭結點
2.整棵樹的min值
3.整棵樹的max值
4.整棵樹符合條件的size

public static Node biggestSubBST(Node head){
	
	//0->size,1->min,2->max
	int[] record = new int[3];
	
	return posOrder(head,record);

	
}

public static Node posOrder(Node head,int[] record){
	
	if(head == null)
	{
		record[0] = 0;
		record[1] = Integer.MAX_VALUE;
		record[2] = Integer.MIN_VALUE;
		return null;	
		
	}
	
	int value = head.value;
	Node left = head.left;
	Node right = head.right;
	Node lBST = postOrder(left,record);
	int lSize = record[0];
	int lMin = record[1];
	int lMax = record[2];
	
	Node rBST = posOrder(right,record);
	int rSize = record[0];
	int rMin = record[1];
	int rMax = record[2];
	
	record[1] = Math.min(lMin,value);
	record[2] = Math.max(rMax,value);
	
	
	if( left == lBST && right == rBST 
		&& lMax < value && value < rMin )
	{
		record[0] = lSize + rSize +1;
		return head;
		
	}
	
	record[0] = Math.max(lSize,rSize);
	
	return lSize > rSize ? lBST : rBST;
	
	
	
}


題目三
二叉樹節點間的最大距離問題
【題目】
從二叉樹的節點 A 出發,可以向上或者向下走,但沿途的節點只能經過一次,當到達節點 B 時,路
徑上的節點數叫作 A 到 B 的距離。求整棵樹上節點間的最大距離。
【要求】
如果二叉樹的節點數爲 N,時間複雜度要求爲 O(N)。

public static int maxDistance(Node head)}{
	
	int[] record = new int[1];
	return posOrder(head,record);

}


public static int posOrder(Node head,int[] record){
	
	if(head == null){
		
		record[0] =0;
		return 0;
		
	}
	
	int lMax = posOrder(head.left,record);
	int maxFromLeft = record[0];
	
	int rMax = posOrder(head.right,record);
	int maxFromRight = record[0];
	
	int curNodeMax = maxFromLeft + maxFromRight+1;
	
	record[0] = Math.max(maxFromLeft, maxFromRight )+1;
	
	return Math.max(Math.max(lMax,rMax),curNodeMax);
	
	
	
	
}



題目五
在二叉樹中找到累加和爲指定值的最長路徑長度
【題目】
給定一棵二叉樹的頭節點 head 和一個 32 位整數 sum,二叉樹節點值類型爲整型,求累加和爲 sum
的最長路徑長度。路徑是指從某個節點往下,每次最多選擇一個孩子節點或者不選所形成的節點鏈。
,額外空間複雜度爲 O(h),h 爲二叉樹的高度。

總結,解決本題需要先複習以下原來的一個算法原型,求一個數組中指定和爲k的最長子數組。

怎麼求解呢?用到哈希map。

首先在map中插入(0,-1)鍵值對,因爲數組下標是從0開始的,所以提前設置從-1位置開始的值是0。
遍歷一遍數組,在遍歷的過程中進行累加,累加值是key,如果這個key值在map中沒有出現過,就將其加入map中,對應的value值是遍歷到的下標,如果已經出現過了,就不進行理會。

算法的原理就是假設從0到i的累加和爲num1,我們要求以i結尾的累加和爲指定k的最長子數組和,就是從map中找到num1-k對應的key值,找出對應的index,符合條件的最長子數組就是index~i的數組。我們如何求長度呢?i-index+1。
同樣我們在遍歷的過程中可以得出所有以0~n結尾的累加和爲指定k的最長子數組和,所以我們在其中選出一個最大值就可以了。

迴歸本題,樹的操作,和之前的又有一些不同,需要注意數據的保存,在遞歸的過程中自然實現保存。

代碼中的sum就是指定和k,數組中的index,就是對應樹中的level:


public static int getMaxLength(Node head,int sum){
	
	HashMap<Integer,Integer> sumMap = new HashMap<Integer,Integer>();
	sumMap.put(0,0);//important
	return preOrder(head,sum,0,1,0,sumMap);
	
	
}


public static int preOrder(Node head,int sum,int preSum,
				int level ,int maxLen,HashMap<Integer,Integer> sumMap  ){
	
	if(head == null)
	{
		return maxLen;
		
	}
	
	int curSum = preSum + head.value;
	
	if(!sumMap.containsKey(curSum)){
		
		sumMap.put(curSum,level);
	}
	
	if(sumMap.containsKey(curSum - sum)){
		
		maxLen = Math.max(level - sumMap.get(curSum - k),maxLen);
		
	}
	
	maxLen = preOrder(head.left,sum,curSum,level+1,maxLen,sumMap);
	
	maxLen = preOrder(head.right,sum,curSum,level+1,maxLen,sumMap);
	
	
	if(level == sumMap.get(curSum)){
		
		sumMap.remove(curSum);
	}
	
	return maxLen;
	
	
}

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