1. 程式人生 > >遞迴函式的理解

遞迴函式的理解

前言:

有很多複雜演算法都包含了遞迴演算法,特別是關於樹形資料結構遍歷的情景,所以正確深入理解遞迴演算法是很有必要的。

一、遞迴函式的基本概念
遞迴函式機制理解:呼叫函式的靜態和動態機制理解:呼叫函式和被呼叫函式雖然是同一個靜態程式碼,但是執行時被函式執行的棧空間獨立於呼叫函式的棧空間,呼叫點不同,函式狀態棧地址也不同,所以執行時呼叫函式和被呼叫函式在程式碼副本還是資料副本上都是完全不同的,只有通過返回值和呼叫點進行聯絡
遞迴的呼叫形式:直接遞迴呼叫F1->F1,間接遞迴F1->F2->F1,很多情況下是直接遞迴呼叫。
遞迴函式優劣點:遞迴函式比較消耗棧資源,實現簡單(內部呼叫邏輯卻比較複雜),但是效率沒有非遞迴版本效率高。

二、遞迴函式內部呼叫邏輯理解:
淺層次理解:

1.遞迴函式需要一個終止條件。
2.遞迴函式到終止條件,會沿路出棧對稱返回。
深層次理解:
I、深究內部過程,呼叫點和變數暫存器:壓棧過程,呼叫點關聯,除了呼叫點傳入引數和返回值外的程式碼變數都因被拷貝了一份而作用域無效

遞迴函式就是函式呼叫和被呼叫的壓棧關係,只有找到都會遞迴呼叫,所以遞迴遍歷都是深度遍歷

II、區分終止條件:遞迴終止條件多樣性,區分那些是終止條件那些不是

遞迴函式的終止條件就是在不斷的遞迴呼叫下最後會發生不再呼叫遞迴函式的情況,可以是if判斷也可以是for/while判斷。

III、理解出棧返回過程:終止後出棧呼叫點返回,繼續執行函式體內因返回點沒有得到執行的程式碼


一般遞迴是為了遍歷或者查詢,如果查詢終止條件在遞迴遍歷前面,那麼不用全部遍歷找到就會返回;如果查詢終止條件在遞迴遍歷後面,那麼需要全部遍歷才會返回


三、一個遍歷3ds Max匯出節點的遞迴演算法的模擬示例

RecursiveFunc.h檔案:

#ifndef RECURSIVEFUNC_H_
#define RECURSIVEFUNC_H_
#include <string>
#include <list>
using namespace std;
typedef struct tagMaxTreeNode
{
	int m_nIndex;
	string m_strName;
	tagMaxTreeNode(int nIndex, const string& strName)
	{
		m_nIndex =  nIndex;
		m_strName = strName;
	}
	void AddChild(tagMaxTreeNode* pChild)
	{
		m_listChild.push_back(pChild);
	}
	list<tagMaxTreeNode*> m_listChild;
}MaxTreeNode;

template <class T>
class MaxTree
{
public:
	MaxTree()
	{
		m_root = NULL;
	}
	~MaxTree()
	{}
	void AddNode(T *pParent, T *pChild)
	{
		if( pParent == NULL )
		{
			m_root = pParent;
			return;
		}
		else
		{
			pParent->AddChild(pChild);
		}
	}

	void BrowseF( T* pNode)
	{
		printf("BrowseF:Recursive Call nIndex: %d\n", pNode->m_nIndex);
		// 這裡if是終止條件
		if( pNode->m_nIndex < 10 )
		{
			printf("BrowseF:Condition Hit nIndex: %d\n", pNode->m_nIndex);
			return;
		}
		list<T*>::iterator itrChild = pNode->m_listChild.begin();
		// 這裡for也是是終止條件,因為pNode是子節點的了
		for(; itrChild != pNode->m_listChild.end(); ++itrChild)
		{
			BrowseF( *itrChild);
		}
	}

	void BrowseL( T* pNode)
	{
		printf("BrowseF:Recursive Call nIndex: %d\n", pNode->m_nIndex);
		list<T*>::iterator itrChild = pNode->m_listChild.begin();
		// 這裡for是終止條件,因為pNode是子節點的了 
		for(; itrChild != pNode->m_listChild.end(); ++itrChild)
		{
			BrowseF( *itrChild);
		}
		// 這裡if不是終止條件
		if( pNode->m_nIndex < 10 )
		{
			printf("BrowseF:Condition Hit nIndex: %d\n", pNode->m_nIndex);return;
		}
	}
private: T* m_root;
};
#endif

main1.cpp檔案:

#include "stdafx.h"
#include "RecursiveFunc.h"

int _tmain(int argc, _TCHAR* argv[])
{
	MaxTreeNode node1(1, "node1");
	MaxTreeNode node2(10, "node2");
	MaxTreeNode node3(100, "node3");
	MaxTreeNode node4(1000, "node4");
	MaxTreeNode node5(10000, "node5");

	/*MaxTreeNode node1(1000, "node1");
	MaxTreeNode node2(1000, "node2");
	MaxTreeNode node3(100, "node3");
	MaxTreeNode node4(100, "node4");
	MaxTreeNode node5(1, "node5");*/

	MaxTree<MaxTreeNode> TestTree;
	TestTree.AddNode(NULL, &node1);
	TestTree.AddNode(&node1, &node2);
	TestTree.AddNode(&node1, &node3);
	TestTree.AddNode(&node2, &node4);
	TestTree.AddNode(&node3, &node5);
	printf("------------TestTree.BrowseF(&node1);-------\n");
	TestTree.BrowseF(&node1);
	printf("------------TestTree.BrowseL(&node1);-------\n");
	TestTree.BrowseL(&node1);
	
	return 0;
}

輸出結果:

main2.cpp:

#include "stdafx.h"
#include "RecursiveFunc.h"

int _tmain(int argc, _TCHAR* argv[])
{
	/*MaxTreeNode node1(1, "node1");
	MaxTreeNode node2(10, "node2");
	MaxTreeNode node3(100, "node3");
	MaxTreeNode node4(1000, "node4");
	MaxTreeNode node5(10000, "node5");*/

	MaxTreeNode node1(10000, "node1");
	MaxTreeNode node2(1000, "node2");
	MaxTreeNode node3(100, "node3");
	MaxTreeNode node4(100, "node4");
	MaxTreeNode node5(1, "node5");

	MaxTree<MaxTreeNode> TestTree;
	TestTree.AddNode(NULL, &node1);
	TestTree.AddNode(&node1, &node2);
	TestTree.AddNode(&node1, &node3);
	TestTree.AddNode(&node2, &node4);
	TestTree.AddNode(&node3, &node5);
	printf("------------TestTree.BrowseF(&node1);-------\n");
	TestTree.BrowseF(&node1);
	printf("------------TestTree.BrowseL(&node1);-------\n");
	TestTree.BrowseL(&node1);
	
	return 0;
}

輸出結果:


相關推薦

C語言學習第23篇---函式理解

遞迴函式:自己呼叫自己,是子函式實現的基礎。遞迴之所以能實現,是因為函式的每個執行過程在棧中都有自己的形參和區域性變數的副本,這些副本和該函式的其他執行過程不發生關係但是遞迴函式在深層迴圈時效率很低,所

如何僅用函式和棧操作逆序一個棧——你要先用stack實現,再去改成——需要對理解很深刻才能寫出來

/** * 如何僅用遞迴函式和棧操作逆序一個棧 * 題目: * 一個棧依次壓入1,2,3,4,5,那麼從棧頂到棧底分別為5,4,3,2,1。 * 將這個棧轉置後,從棧頂到棧底為1,2,3,4,5,也就是實現棧中元素的逆序, * 但是隻能用遞迴函式來實現,不能用

php函式理解

很多同學在學習遞迴函式的時候會感到頭暈,無法搞清楚遞迴函式的原理和執行機制,本文將給大家詳細講解遞迴函式的執行機制和運用。 那什麼是遞迴函式呢? 遞迴函式即為自呼叫函式,在函式體內直接或間接自己呼叫自己,但需要設定自呼叫的條件,若滿足條件,則呼叫函式本身,若

深入理解函式的呼叫過程

int main(void){  unsigned long number;  printf("Enter an integer(q to quit):/n");  while(scanf("%ul",&number)==1)  {    printf("Binary equivalent :"); 

C語言學習總結(1)-函式理解

啥是遞迴? 即是該函式呼叫它本身自己,這種呼叫過程稱為遞迴。 遞迴可以相當於迴圈,所以想結束遞迴,就必須有終止遞迴的條件測試部分,否則就會出現無限遞迴(即無限迴圈)。同時,這也是使用遞迴的難點

函式理解

前言: 有很多複雜演算法都包含了遞迴演算法,特別是關於樹形資料結構遍歷的情景,所以正確深入理解遞迴演算法是很有必要的。 一、遞迴函式的基本概念 遞迴函式機制理解:呼叫函式的靜態和動態機制理解:呼叫函式

理解C語言函式的逐級返回(return)

遞迴函式,也即呼叫自身的函式。 C Primer Plus中有個例子很棒: /*理解C語言遞迴函式*/ #include<stdio.h> void up_and_down(int); int  main(void) {up_and_down(1);retur

深入理解python函式:漢諾塔遊戲

def hanota(n,zhu1,zhu2,zhu3):     if n==1:         print (zhu1+' --> '+zhu3)     else:        hanota(n-1, zhu1, zhu3, zhu2)         pr

十一、Go基礎程式設計:函式函式型別、匿名函式與閉包

1. 遞迴函式 遞迴指函式可以直接或間接的呼叫自身。 遞迴函式通常有相同的結構:一個跳出條件和一個遞迴體。所謂跳出條件就是根據傳入的引數判斷是否需要停止遞迴,而遞迴體則是函式自身所做的一些處理。 //通過迴圈實現1+2+3……+100 func Test01() int { i

函式使用例項

一.建立遞迴陣列 //$fid 父id $v['id'] 子id $v['child'] 子id陣列 //$cate 要迴圈的一維陣列 function digui($cate, $fid = 0){ foreach ($cate as $v)

Python全棧學習筆記day 17:函式之:二分法(老男孩Python全棧學習s9 day17 二分法程式有些問題)

遞迴函式 遞迴 : 在函式中呼叫自身函式 最大遞迴深度預設是997/998 —— 是python從記憶體角度出發做得限制 二分法: 實現程式: 最基礎版:(很多問題:切分導致出現了新列表,無法返回元素在 l 中的位置) l = [2,3,5,10,15,16,

內建函式拓展,函式

1. lambda匿名函式 lambda 就是為了解決一些簡單的需求而設定的一句話函式 舉例: 計算一個數的平方: 用函式寫:def func(n):      return n*n 用匿名函式寫:f = lanbda n: n*n 語法:函式名 = lambda 引數: 函式體 注意: 1.

python摸爬滾打之day14----內建函式,函式

1、匿名函式  用一句話實現的簡單函式.   ret = lambda x : x ** 2      即 函式名 = lambda 形參 : 返回值   print(ret(5))  ----> 25 2、sorted()  ---->

python學習day17 函式

  遞迴函式 http://www.cnblogs.com/Eva-J/articles/7205734.html   def age(n): if n == 4: return 40 elif n >0 and n < 4:

python學習 day014打卡 內建函式二&函式

本節主要內容: 1.lambda匿名函式 2.sorted() 3.filter() 4.map() 5.遞迴函式 6.二分法   一.lambda匿名函式 為了解決一些簡單的需求而設計的一句話函式 # 計算n的n次方 def func(n): retur

函式使用引用傳遞的問題

對於演算法: 使用遞迴方法,刪除無頭結點單鏈表中所有值為x的結點。 考慮下面程式: void Del_X_3(LinkList &L,ElemType x) { LNode *p; if(L==NULL) return ;

php 函式的三種實現方式 php利用函式實現無限級分類

遞迴函式是我們常用到的一類函式,最基本的特點是函式自身呼叫自身,但必須在呼叫自身前有條件判斷,否則無限無限呼叫下去。實現遞迴函式可以採取什麼方式呢?本文列出了三種基本方式。理解其原來需要一定的基礎知識水品,包括對全域性變數,引用,靜態變數的理解,也需對他們的作用範圍有所理解。遞迴函式也是解決無限級分類的一個很

理解和例子

''' # 遞迴函式 - 優點: _直接簡潔 -缺點: _對遞迴有限制超過報錯(100層)[設定遞迴最大層數 import sys sys.setrecursionlimit(設定遞迴的頂層)] _消耗資源大 ''' # 一個簡單的遞迴 ''' X = 0 def fun

【PHP】函式實現運算器

<html> <head> <meta charset="utf-8"/> </head> <body> <!-- 表單提交開始--> <form action="test-digui.php"

11月6日排序函式,匿名函式,回撥函式函式, zip函式

##### 排序sort, sorted的區別: list.sort(func=None, key=None, reverse=False(or True)) 對於reverse這個bool型別引數,當reverse=False時:為正向排序;當reverse=True時:為方向排序。預設為Fal