遞歸算法總結

下面總結一些常見的遞歸編程題:
1.使用遞歸逆序打印單鏈表:


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>using namespace std;

struct Node
{
    Node(int data)
    :_data(data)
    , _next(NULL)
    {}
    int _data;
    Node* _next;
};

void PrintFromTail2Head(Node* pHead)
{
    if (pHead)
    {
        PrintFromTail2Head(pHead->_next);
        cout << pHead->_data << "  ";
    }
}

void Test1()
{
    Node n1(1);
    Node n2(2);
    Node n3(3);
    n1._next = &n2;
    n2._next = &n3;
    PrintFromTail2Head(&n1);
}

int main()
{
    Test1();
    return 0;
}

2.逆序銷燬單鏈表中的節點:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

struct Node
{
    Node(int data)
    :_data(data)
    , _next(NULL)
    {}
    int _data;
    Node* _next;
};


void DestoryFromTail2Head(Node*& pHead)
{
    if (pHead)
    {
        DestoryFromTail2Head(pHead->_next);
        delete pHead;
        pHead = NULL;
    }
}


void Test1()
{
    Node* n1 = new Node(1);
    Node* n2 = new Node(2);
    Node* n3 = new Node(3);
    n1->_next = n2;
    n2->_next = n3;
    DestoryFromTail2Head(n1);
}

int main()
{
    Test1();
    return 0;
}

3.//在單鏈表中從後向前查找某個值爲data的節點

//在單鏈表中查找某個值爲data的節點
Node* FindData(Node* pHead, int data)
{
    if (pHead)
    {
        Node* res = FindData(pHead->_next, data);//需要記錄每次遞歸結束時的返回值
        if (res)
        {
            return res;
        }
        else
        {
            if (pHead->_data == data)
            {
                return pHead;
            }
        }
    }
    return NULL;
}

void Test1()
{
        Node* n1 = new Node(1);
        Node* n2 = new Node(2);
        Node* n3 = new Node(3);
        n1->_next = n2;
        n2->_next = n3;
        Node* p = FindData(n1, 2);

}

int main()
{
    Test1();
    return 0;
}

4.遞歸逆序打印數組

void PrintArray(int array[],int size)
{
    if (size)
    {
        size--;
        cout << array[size]<<" ";
        PrintArray(array, size);
    }
}

void Test1()
{
    int array[] = { 9, 2, 8, 4, 3, 7, 5 };
    PrintArray(array, 7);
}

int main()
{
    Test1();
    return 0;
}

5.遞歸算法進行二分查找

int BinaryFind(int *arr, int left, int right, int data)
{

    if (left <= right)
    {
        int mid = (left & right) + ((left ^ right) >> 1);

        if (arr[mid] == data)
        {
            return mid;
        }
        else if (arr[mid] < data)
        {
            return  BinaryFind(arr, mid + 1, right, data);//注意加上return
        }
        else
        {
            return  BinaryFind(arr, left, mid - 1, data);
        }
    }
    return -1;
}

void Test1()
{
    int array[] = { 0,1,2,3,5,6,7};
    int ret = BinaryFind(array, 0, 6, 6);
    cout << ret << endl;
}

6.判斷一個字符串是否爲迴文字符串(例如:1221 121)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

bool IsPlalindrome(char * str, int len)
{
    if (len <= 1)
    {
        return true;
    }
    else
    {
        if (str[0] != str[len - 1])
        {
            return false;
        }
        IsPlalindrome(str + 1, len - 2);
    }
}

void Test1()
{
    char* str = "1216";
    cout << IsPlalindrome(str, 4) << endl;
}

int main()
{
    Test1();
    return 0;
}

7.尾遞歸:
優勢:
①重複次數減少了;
②棧環境只開闢一個。

將斐波那契數列的遞歸形式改寫爲尾遞歸:

int Fib(int first, int second, int N)
{
    if (N < 3)
    {
        return 1;
    }
    else if (N == 3)
    {
        return first + second;
    }
    return Fib(second, first + second, N - 1);
}

void Test1()
{
    cout << Fib(1, 1, 20) << endl;;
}

int main()
{
    Test1();
    return 0;
}
``
彙編代碼如下:
int Fib(int N, int first, int second)
{
00C93840  push        ebp
00C93841  mov         ebp,esp
00C93843  sub         esp,0C0h
00C93849  push        ebx
00C9384A  push        esi
00C9384B  push        edi
00C9384C  lea         edi,[ebp-0C0h]
00C93852  mov         ecx,30h
00C93857  mov         eax,0CCCCCCCCh
00C9385C  rep stos    dword ptr es:[edi]
    if (N < 3)
00C9385E  cmp         dword ptr [N],3
00C93862  jge         Fib+2Bh (0C9386Bh)
    {
        return 1;
00C93864  mov         eax,1
00C93869  jmp         Fib+53h (0C93893h)
    }
    if (3 == N)
00C9386B  cmp         dword ptr [N],3
00C9386F  jne         Fib+39h (0C93879h)
    {
        return first + second;
00C93871  mov         eax,dword ptr [first]
00C93874  add         eax,dword ptr [second]
00C93877  jmp         Fib+53h (0C93893h)
    }
    return Fib(N - 1, second, first + second);
00C93879  mov         eax,dword ptr [first]
00C9387C  add         eax,dword ptr [second]
00C9387F  push        eax
    }
    return Fib(N - 1, second, first + second);
00C93880  mov         ecx,dword ptr [second]
00C93883  push        ecx
00C93884  mov         edx,dword ptr [N]
00C93887  sub         edx,1
00C9388A  push        edx
00C9388B  call        Fib (0C91307h)
00C93890  add         esp,0Ch
}

在編譯器中設置以下步驟後:會發現程序的最後根本就不會call Fib函數,而是執行了一條跳轉指令,這時編譯器對代碼進行了優化,
因爲是尾遞歸,所以就不會在後面每次執行遞歸時,都會開闢棧幀,而是會將前一個函數棧幀中的數據進行了修改,所以時間複雜度
減小,而空間複雜度也減小了。因爲遞歸的深度減小了。

修改編譯器下的如下配置:
①將常規裏面的調試信息格式 從用於編輯並繼續的程序庫 修改爲程序數據庫。
②將優化裏面的禁止優化修改爲 最小優化爲O(1)。
③將代碼生成裏的 兩者…改爲 默認值。

經過如上配置後,然後調試代碼,就會發現反彙編代碼 中函數的調用發生了優化。

int Fib(int N, int first, int second)
{
01362642  push        ebp
01362643  mov         ebp,esp
    if (N < 3)
01362645  mov         ecx,dword ptr [N]
01362648  push        esi
01362649  cmp         ecx,3
0136264C  jl          Fib+24h (01362666h)
0136264E  mov         edx,dword ptr [second]
01362651  mov         esi,dword ptr [first]
    }
    if (3 == N)
    {
        return first + second;
    }
    return Fib(N - 1, second, first + second);
01362654  lea         eax,[esi+edx]
01362657  cmp         ecx,3
0136265A  je          Fib+27h (01362669h)
0136265C  dec         ecx
0136265D  mov         esi,edx
0136265F  mov         edx,eax
01362661  cmp         ecx,3
01362664  jge         Fib+12h (01362654h)
    {
        return 1;
01362666  xor         eax,eax
01362668  inc         eax
01362669  pop         esi
}
0136266A  pop         ebp
0136266B  ret
--- 無源文件 -----------------------------------------------------------------------
0136266C  int         3
0136266D  int         3
0136266E  int         3
0136266F  int         3
01362670  int         3
01362671  int         3
01362672  int         3
01362673  int         3
01362674  int         3
01362675  int         3
--- j:\c\classtest2project1\classtest2project1\classtest2.cpp ------------------
    cout << Fib(10, 1, 1) << endl;
01362676  push        13611BDh
0136267B  push        2
0136267D  push        1
0136267F  push        9
01362681  call        Fib (01361172h)
01362686  mov         ecx,dword ptr ds:[136B05Ch]
0136268C  add         esp,0Ch
0136268F  push        eax
01362690  call        dword ptr ds:[136B050h]
01362696  mov         ecx,eax
01362698  call        dword ptr ds:[136B058h]
}
0136269E  ret
---

總結說明:
尾遞歸可以提高空間複雜度和時間複雜度。

發佈了110 篇原創文章 · 獲贊 82 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章