關於遞歸的一些小練習


遞歸

什么是遞歸

在程序中, 所謂的遞歸, 就是函數自己直接或間接的調用自己.

  1. 直接調用自己
  2. 間接調用自己

就遞歸而言最重要的就是跳出結構. 因為跳出了才可以有結果.

所謂的遞歸就是化歸思想

遞歸的調用, 寫遞歸函數, 最終還是要轉換為自己這個函數.

假如有一個函數 f, 如果它是遞歸函數的話, 那么也就是說 函數體內的問題還是轉換為 f 的形式.

遞歸思想就是將一個問題轉換為一個已解決的問題來實現

	function f() {
		... f( ... ) ...
	}

例子: 1, 2, 3, 4, 5, ..., 100

  1. 首先假定遞歸函數已經寫好, 假設是 foo. 即 foo( 100 ) 就是求 1 到 100 的和
  2. 尋找遞推關系. 就是 n 與 n-1, 或 n-2 之間的關系: foo( n ) == n + foo( n - 1 )
	var res = foo( 100 );
	var res = foo( 99 ) + 100;
  1. 將遞推結構轉換為 遞歸體
	function foo( n ) {
		return n + foo( n - 1 );
	}
* 將 求 100 轉換為 求 99
* 將 求 99 轉換為 求 98
* ...
* 將求 2 轉換為 求 1
* 求 1 結果就是 1
* 即: foo( 1 ) 是 1
  1. 將臨界條件加到遞歸體中
	function foo( n ) {
		if ( n == 1 ) return 1;
		return n + foo( n - 1 );
	}

練習: 求 1, 3, 5, 7, 9, ... 第 n 項的結果與前 n 項和. 序號從 0 開始

求第 n 項的

  1. 首先假定遞歸函數已經寫好, 假設是 fn. 那么 第 n 項就是 fn( n )
  2. 找遞推關系: fn( n ) == f( n - 1 ) + 2
  3. 遞歸體
	function fn( n ) {
		return fn( n-1 ) + 2;
	}
  1. 找臨界條件
    • 求 n -> n-1
    • 求 n-1 -> n-2
    • ...
    • 求 1 -> 0
    • 求 第 0 項, 就是 1
  2. 加入臨界條件
	function fn( n ) {
		if ( n == 0 ) return 1;
		return fn( n-1 ) + 2;
	}

前n項和

  1. 假設已完成, sum( n ) 就是前 n 項和
  2. 找遞推關系: 前 n 項和 等於 第 n 項 + 前 n-1 項的和
  3. 得到遞歸體
	function sum( n ) {
		return fn( n ) + sum( n - 1 );
	} 
  1. 找臨界條件
    • n == 1 結果為 1
  2. 得到遞歸函數
	function sum( n ) {
		if ( n == 0 ) return 1;
		return fn( n ) + sum( n - 1 );
	} 

練習: 2, 4, 6, 8, 10 第 n 項與 前 n 項和

第n項

function fn( n ) {
	if ( n == 0 ) return 2;
	return fn( n-1 ) + 2;
}

前n項和

function sum( n ) {
	if ( n == 0 ) return 2;
	return sum( n - 1 ) + fn( n );
}

練習: 數列: 1, 1, 2, 4, 7, 11, 16, … 求 第 n 項, 求前 n 項和.

求第 n 項

  1. 假設已經得到結果 fn, fn( 10 ) 就是第 10 項
  2. 找遞推關系
    • 0, 1 => fn( 0 ) + 0 = fn( 1 )
    • 1, 2 => fn( 1 ) + 1 = fn( 2 )
    • 2, 3 => fn( 2 ) + 2 = fn( 3 )
    • ...
    • n-1, n => fn( n-1 ) + n - 1 = fn( n )
  3. 遞歸體也就清楚了, 臨界條件是 n == 0 => 1
	function fn( n ) {
		if ( n == 0 ) return 1;
		return fn( n-1 ) + n - 1;
	}

如果從 1 開始表示, 那么第 n 項為

  1. 假設已經得到結果 fn, fn( 10 ) 就是第 10 項
  2. 找遞推關系
    • 1, 2 => fn( 1 ) + 0 = fn( 2 )
    • 2, 3 => fn( 2 ) + 1 = fn( 3 )
    • 3, 4 => fn( 3 ) + 2 = fn( 4 )
    • ...
    • n-1, n => fn( n-1 ) + n - 2 = fn( n )
  3. 臨界條件 n == 1 => 1

前n項和

	function sum( n ) {
		if ( n == 0 ) return 1;
		return sum( n - 1 ) + fn( n );
	}

如果從 0 開始

0  1  2  3  4  5   6
1, 1, 2, 4, 7, 11, 16,

如果從 1 開始

1  2  3  4  5  6   7
1, 1, 2, 4, 7, 11, 16,

練習: Fibonacci 數列: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
求其第 n 項.

遞推關系 fn(n) == fn( n- 1) + fn( n - 2)

	function fib( n ) {
		if ( n == 0 || n == 1 ) return 1;
		return fib( n - 1 ) + fib( n - 2 );
	}

階乘

階乘是一個運算, 一個數字的階乘表示的是從 1 開始 累乘到這個數字. 例如 3! 表示 1 * 2 * 3.
5! 就是 1 * 2 * 3 * 4 * 5. 規定 0 沒有階乘, 階乘 從 1 開始.

求 n 的階乘

	function foo ( n ) {
		if ( n == 1 ) return 1;
		return foo( n - 1 ) * n;
	}

求冪

求冪就是求 某一個數 幾次方

2*2 2 的 平方, 2 的 2 次方

求 n 的 m 次方

最終要得到一個函數 power( n, m )

n 的 m 次方就是 m 個 n 相乘 即 n 乘以 (m-1) 個 n 相乘

	function power ( n, m ) {
		if ( m == 1 ) return n;
		return power( n, m - 1 ) * n;
	}

深拷貝

如果要實現深拷貝那么就需要考慮將對象的屬性, 與屬性的屬性, ... 都拷貝過來

如果要實現:

  1. 假設已經實現 clone( o1, o2 ), 將對象 o2 的成員拷貝一份交給 o1
  2. 簡單的算法, 將 o2 的屬性拷貝到 o1 中去
	function clone( o1, o2 ) {
		for ( var k in o2 ) {
			o1[ k ] = o2[ k ];
		}
	}
  1. 找遞推關系, 或叫划歸為已經解決的問題
    • 假設方法已經實現, 問一下, 如果 o2[ k ] 是對象
    • 繼續使用這個方法
    • 因此需要考慮的是 o2[ k ] 如果是引用類型, 再使用一次 clone() 函數
    • 如果 o2[ k ]不是引用類型, 那么 就直接賦值
	function clone( o1, o2 ) {
		for ( var k in o2 ) {
			if ( typeof o2[ k ] == 'object' ) {
				o1[ k ] = {};
				clone( o1[ k ] , o2[ k ] );
			} else {
				o1[ k ] = o2[ k ];	
			}
		}
	}

復雜實現: clone( o ) -> newObj

	function clone( o ) {
		var temp = {};
		for ( var k in o ) {
			if ( typeof o[ k ] == 'object' ) {
				temp[ k ] = clone( o[ k ] );
			} else {
				temp[ k ] = o[ k ];
			}
		}
		return temp;
	}

請用 遞歸實現 getElementsByClassName

<div>
	<div>1
		<div class="c">2</div>
		<div>3</div>
	</div>
	<div class="c">4</div>
	<div>5
		<div>6</div>
		<div class="c">7</div>
	</div>
	<div>8</div>
</div>
  1. 如果實現一個方法 byClass( node, 'c', list ), 表示在某一個節點上查找符合 class 屬性為 c 的元素
  2. 在當前元素的子元素中查找, 如果有符合要求的, 存儲到一個數組中
  3. 首先遍歷 子節點, 然后看子節點是否還有子節點, 如果沒有直接判斷, 如果有再遞歸
	function byClass( node, className, list ) {
		var arr = node.childNodes;
		
		for ( var i = 0; i < arr.length; i++ ) {
			if ( arr[ i ].className == className ) {
				list.push( arr[ i ] );
			}
			
			if ( arr[ i ].childNodes.length > 0 ) {
				byClass( arr[ i ], className, list );
			}
		}
	}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM