棧與遞歸的實現

棧與遞歸

棧還有一個重要應用是在程序設計語言中實現遞歸。一個直接調用自己或通過一系列的調用語句間接的調用自己的函數,稱為遞歸函數。

遞歸是程序設計中一個強有力的工具。
其一,有很多數學函數是遞歸定義的,如大家熟悉的階乘函數:

Fact(n)= 1 若n=0
n*Fact(n-1) 若n>0

2階Fibonacci數列:

Fib(n)= 0 若n=0
1 若n=1
Fib(n-1) + Fibz(n - 2) 其他情形

Ackerman函數:

Ack(m,n)= n+1 若m=0
Ack(m-1,1) 若n=0
Ack(m-1,Ack(m,n-1)) 其他情形

其二,有的數據結構,如二叉樹、廣義表等,由于結構本身固有的遞歸特性,則它們的操作可遞歸的描述;
其三,還有一類問題,雖然問題本身沒有明顯的遞歸結構,但用遞歸求解比迭代求解更簡單,如八皇后問題、Hanoi塔問題。

n階Hanoi塔問題:
假設有3個分別命名為X、Y、Z的塔座,在塔座X上插有n個直徑大小各不同、依小到大編號為1,2,...,n的圓盤。現要求將X軸上的n個圓盤移至塔座Z上并仍按同樣順序疊排,圓盤移動時必須遵循下列規則:
1.每次只能移動一個圓盤
2.圓盤可以插在X 、Y、Z中的任一塔座上
3.任何時刻都不能將一個較大的圓盤壓在較小的圓盤上。

如何實現移動圓盤的操作呢?
當n=1時,問題比較簡單,只要將編號為1的圓盤從塔座X直接移至塔座Z上即可;

當n>1時,需利用塔座Y作為輔助塔,若能設法將壓在編號為n的圓盤之上的n-1個圓盤從塔座X移至塔座Y上,則可先將編號為n的圓盤從塔座X移至塔座Z上,然后再將塔座Y上的n-1個圓盤移至塔座Z上。

而如何將n-1個圓盤從一個塔座移至另一個塔座的問題是一個和原問題具有相同特征屬性的問題,只是問題的規模小1,因此可以用同樣的方法求解。

TowerOfHanoi.c利用了前面的C封裝的順序棧對象 用線性表表示的順序棧
實現了Hanoi塔遞歸移動的過程,每一步執行的過程以及x、y、z三個軸的狀態均可以看到。

github源碼

TowerOfHanoi.c文件

#include <stdio.h>
#include <malloc.h>
#include "LinearListStack.h"

void hanoi(int n,LinearListStack *x,LinearListStack *y,LinearListStack *z){
    char elem;
    if(n == 1){
        x->pop(x,&elem);
        z->push(z,&elem); //將編號為1的圓盤從x移到z
        //******************************打印步驟需要,非執行過程*********************
        printf("move %c from %c to %c\n",elem,*(x->This->base),*(z->This->base));
        x->risePrint(x);
        y->risePrint(y);
        z->risePrint(z);
        printf("\n");
        //***************************************************************************
    }else{
        hanoi(n-1,x,z,y);//將x上編號為1至n-1的圓盤移到y,z做輔助塔
        x->pop(x,&elem);
        z->push(z,&elem);//將編號為n的圓盤從x移到z
        //******************************打印步驟需要,非執行過程*********************
        printf("move %c from %c to %c\n",elem,*(x->This->base),*(z->This->base));
        x->risePrint(x);
        y->risePrint(y);
        z->risePrint(z);
        printf("\n");
        //***************************************************************************
        hanoi(n-1,y,x,z);//將y上編號為1至n-1的圓盤移到z,x做輔助塔
    }
}

int main(void)
{
    int i;
    char elem;
    LinearListStack *x = InitLinearListStack();
    LinearListStack *y = InitLinearListStack();
    LinearListStack *z = InitLinearListStack();
    elem = 'x';
    x->push(x,&elem);
    elem = ':';
    x->push(x,&elem);
    elem = 'y';
    y->push(y,&elem);
    elem = ':';
    y->push(y,&elem);
    elem = 'z';
    z->push(z,&elem);
    elem = ':';
    z->push(z,&elem);
    for(i=9;i>0;i--){
        elem = i+0x30;
        x->push(x,&elem);
    }
    hanoi(9,x,y,z);
    DestroyLinearListStack(x);
    DestroyLinearListStack(y);
    DestroyLinearListStack(z);
    return 0;
}

編譯:

gcc LinearListStack.c LinearListStack.h TowerOfHanoi.c -o TowerOfHanoi

運行TowerOfHanoi:

move 1 from x to z
x:98765432
y:
z:1

move 2 from x to y
x:9876543
z:1
y:2

move 1 from z to y
z:
x:9876543
y:21

move 3 from x to z
x:987654
y:21
z:3

move 1 from y to x
y:2
z:3
x:9876541

move 2 from y to z
y:
x:9876541
z:32

move 1 from x to z
x:987654
y:
z:321

move 4 from x to y
x:98765
z:321
y:4

move 1 from z to y
z:32
x:98765
y:41

move 2 from z to x
z:3
y:41
x:987652

move 1 from y to x
y:4
z:3
x:9876521

move 3 from z to y
z:
x:9876521
y:43

move 1 from x to z
x:987652
y:43
z:1

move 2 from x to y
x:98765
z:1
y:432

move 1 from z to y
z:
x:98765
y:4321

move 5 from x to z
x:9876
y:4321
z:5

move 1 from y to x
y:432
z:5
x:98761

move 2 from y to z
y:43
x:98761
z:52

move 1 from x to z
x:9876
y:43
z:521

move 3 from y to x
y:4
z:521
x:98763

move 1 from z to y
z:52
x:98763
y:41

move 2 from z to x
z:5
y:41
x:987632

move 1 from y to x
y:4
z:5
x:9876321

move 4 from y to z
y:
x:9876321
z:54

move 1 from x to z
x:987632
y:
z:541

move 2 from x to y
x:98763
z:541
y:2

move 1 from z to y
z:54
x:98763
y:21

move 3 from x to z
x:9876
y:21
z:543

move 1 from y to x
y:2
z:543
x:98761

move 2 from y to z
y:
x:98761
z:5432

move 1 from x to z
x:9876
y:
z:54321

move 6 from x to y
x:987
z:54321
y:6

move 1 from z to y
z:5432
x:987
y:61

move 2 from z to x
z:543
y:61
x:9872

move 1 from y to x
y:6
z:543
x:98721

move 3 from z to y
z:54
x:98721
y:63

move 1 from x to z
x:9872
y:63
z:541

move 2 from x to y
x:987
z:541
y:632

move 1 from z to y
z:54
x:987
y:6321

move 4 from z to x
z:5
y:6321
x:9874

move 1 from y to x
y:632
z:5
x:98741

move 2 from y to z
y:63
x:98741
z:52

move 1 from x to z
x:9874
y:63
z:521

move 3 from y to x
y:6
z:521
x:98743

move 1 from z to y
z:52
x:98743
y:61

move 2 from z to x
z:5
y:61
x:987432

move 1 from y to x
y:6
z:5
x:9874321

move 5 from z to y
z:
x:9874321
y:65

move 1 from x to z
x:987432
y:65
z:1

move 2 from x to y
x:98743
z:1
y:652

move 1 from z to y
z:
x:98743
y:6521

move 3 from x to z
x:9874
y:6521
z:3

move 1 from y to x
y:652
z:3
x:98741

move 2 from y to z
y:65
x:98741
z:32

move 1 from x to z
x:9874
y:65
z:321

move 4 from x to y
x:987
z:321
y:654

move 1 from z to y
z:32
x:987
y:6541

move 2 from z to x
z:3
y:6541
x:9872

move 1 from y to x
y:654
z:3
x:98721

move 3 from z to y
z:
x:98721
y:6543

move 1 from x to z
x:9872
y:6543
z:1

move 2 from x to y
x:987
z:1
y:65432

move 1 from z to y
z:
x:987
y:654321

move 7 from x to z
x:98
y:654321
z:7

move 1 from y to x
y:65432
z:7
x:981

move 2 from y to z
y:6543
x:981
z:72

move 1 from x to z
x:98
y:6543
z:721

move 3 from y to x
y:654
z:721
x:983

move 1 from z to y
z:72
x:983
y:6541

move 2 from z to x
z:7
y:6541
x:9832

move 1 from y to x
y:654
z:7
x:98321

move 4 from y to z
y:65
x:98321
z:74

move 1 from x to z
x:9832
y:65
z:741

move 2 from x to y
x:983
z:741
y:652

move 1 from z to y
z:74
x:983
y:6521

move 3 from x to z
x:98
y:6521
z:743

move 1 from y to x
y:652
z:743
x:981

move 2 from y to z
y:65
x:981
z:7432

move 1 from x to z
x:98
y:65
z:74321

move 5 from y to x
y:6
z:74321
x:985

move 1 from z to y
z:7432
x:985
y:61

move 2 from z to x
z:743
y:61
x:9852

move 1 from y to x
y:6
z:743
x:98521

move 3 from z to y
z:74
x:98521
y:63

move 1 from x to z
x:9852
y:63
z:741

move 2 from x to y
x:985
z:741
y:632

move 1 from z to y
z:74
x:985
y:6321

move 4 from z to x
z:7
y:6321
x:9854

move 1 from y to x
y:632
z:7
x:98541

move 2 from y to z
y:63
x:98541
z:72

move 1 from x to z
x:9854
y:63
z:721

move 3 from y to x
y:6
z:721
x:98543

move 1 from z to y
z:72
x:98543
y:61

move 2 from z to x
z:7
y:61
x:985432

move 1 from y to x
y:6
z:7
x:9854321

move 6 from y to z
y:
x:9854321
z:76

move 1 from x to z
x:985432
y:
z:761

move 2 from x to y
x:98543
z:761
y:2

move 1 from z to y
z:76
x:98543
y:21

move 3 from x to z
x:9854
y:21
z:763

move 1 from y to x
y:2
z:763
x:98541

move 2 from y to z
y:
x:98541
z:7632

move 1 from x to z
x:9854
y:
z:76321

move 4 from x to y
x:985
z:76321
y:4

move 1 from z to y
z:7632
x:985
y:41

move 2 from z to x
z:763
y:41
x:9852

move 1 from y to x
y:4
z:763
x:98521

move 3 from z to y
z:76
x:98521
y:43

move 1 from x to z
x:9852
y:43
z:761

move 2 from x to y
x:985
z:761
y:432

move 1 from z to y
z:76
x:985
y:4321

move 5 from x to z
x:98
y:4321
z:765

move 1 from y to x
y:432
z:765
x:981

move 2 from y to z
y:43
x:981
z:7652

move 1 from x to z
x:98
y:43
z:76521

move 3 from y to x
y:4
z:76521
x:983

move 1 from z to y
z:7652
x:983
y:41

move 2 from z to x
z:765
y:41
x:9832

move 1 from y to x
y:4
z:765
x:98321

move 4 from y to z
y:
x:98321
z:7654

move 1 from x to z
x:9832
y:
z:76541

move 2 from x to y
x:983
z:76541
y:2

move 1 from z to y
z:7654
x:983
y:21

move 3 from x to z
x:98
y:21
z:76543

move 1 from y to x
y:2
z:76543
x:981

move 2 from y to z
y:
x:981
z:765432

move 1 from x to z
x:98
y:
z:7654321

move 8 from x to y
x:9
z:7654321
y:8

move 1 from z to y
z:765432
x:9
y:81

move 2 from z to x
z:76543
y:81
x:92

move 1 from y to x
y:8
z:76543
x:921

move 3 from z to y
z:7654
x:921
y:83

move 1 from x to z
x:92
y:83
z:76541

move 2 from x to y
x:9
z:76541
y:832

move 1 from z to y
z:7654
x:9
y:8321

move 4 from z to x
z:765
y:8321
x:94

move 1 from y to x
y:832
z:765
x:941

move 2 from y to z
y:83
x:941
z:7652

move 1 from x to z
x:94
y:83
z:76521

move 3 from y to x
y:8
z:76521
x:943

move 1 from z to y
z:7652
x:943
y:81

move 2 from z to x
z:765
y:81
x:9432

move 1 from y to x
y:8
z:765
x:94321

move 5 from z to y
z:76
x:94321
y:85

move 1 from x to z
x:9432
y:85
z:761

move 2 from x to y
x:943
z:761
y:852

move 1 from z to y
z:76
x:943
y:8521

move 3 from x to z
x:94
y:8521
z:763

move 1 from y to x
y:852
z:763
x:941

move 2 from y to z
y:85
x:941
z:7632

move 1 from x to z
x:94
y:85
z:76321

move 4 from x to y
x:9
z:76321
y:854

move 1 from z to y
z:7632
x:9
y:8541

move 2 from z to x
z:763
y:8541
x:92

move 1 from y to x
y:854
z:763
x:921

move 3 from z to y
z:76
x:921
y:8543

move 1 from x to z
x:92
y:8543
z:761

move 2 from x to y
x:9
z:761
y:85432

move 1 from z to y
z:76
x:9
y:854321

move 6 from z to x
z:7
y:854321
x:96

move 1 from y to x
y:85432
z:7
x:961

move 2 from y to z
y:8543
x:961
z:72

move 1 from x to z
x:96
y:8543
z:721

move 3 from y to x
y:854
z:721
x:963

move 1 from z to y
z:72
x:963
y:8541

move 2 from z to x
z:7
y:8541
x:9632

move 1 from y to x
y:854
z:7
x:96321

move 4 from y to z
y:85
x:96321
z:74

move 1 from x to z
x:9632
y:85
z:741

move 2 from x to y
x:963
z:741
y:852

move 1 from z to y
z:74
x:963
y:8521

move 3 from x to z
x:96
y:8521
z:743

move 1 from y to x
y:852
z:743
x:961

move 2 from y to z
y:85
x:961
z:7432

move 1 from x to z
x:96
y:85
z:74321

move 5 from y to x
y:8
z:74321
x:965

move 1 from z to y
z:7432
x:965
y:81

move 2 from z to x
z:743
y:81
x:9652

move 1 from y to x
y:8
z:743
x:96521

move 3 from z to y
z:74
x:96521
y:83

move 1 from x to z
x:9652
y:83
z:741

move 2 from x to y
x:965
z:741
y:832

move 1 from z to y
z:74
x:965
y:8321

move 4 from z to x
z:7
y:8321
x:9654

move 1 from y to x
y:832
z:7
x:96541

move 2 from y to z
y:83
x:96541
z:72

move 1 from x to z
x:9654
y:83
z:721

move 3 from y to x
y:8
z:721
x:96543

move 1 from z to y
z:72
x:96543
y:81

move 2 from z to x
z:7
y:81
x:965432

move 1 from y to x
y:8
z:7
x:9654321

move 7 from z to y
z:
x:9654321
y:87

move 1 from x to z
x:965432
y:87
z:1

move 2 from x to y
x:96543
z:1
y:872

move 1 from z to y
z:
x:96543
y:8721

move 3 from x to z
x:9654
y:8721
z:3

move 1 from y to x
y:872
z:3
x:96541

move 2 from y to z
y:87
x:96541
z:32

move 1 from x to z
x:9654
y:87
z:321

move 4 from x to y
x:965
z:321
y:874

move 1 from z to y
z:32
x:965
y:8741

move 2 from z to x
z:3
y:8741
x:9652

move 1 from y to x
y:874
z:3
x:96521

move 3 from z to y
z:
x:96521
y:8743

move 1 from x to z
x:9652
y:8743
z:1

move 2 from x to y
x:965
z:1
y:87432

move 1 from z to y
z:
x:965
y:874321

move 5 from x to z
x:96
y:874321
z:5

move 1 from y to x
y:87432
z:5
x:961

move 2 from y to z
y:8743
x:961
z:52

move 1 from x to z
x:96
y:8743
z:521

move 3 from y to x
y:874
z:521
x:963

move 1 from z to y
z:52
x:963
y:8741

move 2 from z to x
z:5
y:8741
x:9632

move 1 from y to x
y:874
z:5
x:96321

move 4 from y to z
y:87
x:96321
z:54

move 1 from x to z
x:9632
y:87
z:541

move 2 from x to y
x:963
z:541
y:872

move 1 from z to y
z:54
x:963
y:8721

move 3 from x to z
x:96
y:8721
z:543

move 1 from y to x
y:872
z:543
x:961

move 2 from y to z
y:87
x:961
z:5432

move 1 from x to z
x:96
y:87
z:54321

move 6 from x to y
x:9
z:54321
y:876

move 1 from z to y
z:5432
x:9
y:8761

move 2 from z to x
z:543
y:8761
x:92

move 1 from y to x
y:876
z:543
x:921

move 3 from z to y
z:54
x:921
y:8763

move 1 from x to z
x:92
y:8763
z:541

move 2 from x to y
x:9
z:541
y:87632

move 1 from z to y
z:54
x:9
y:876321

move 4 from z to x
z:5
y:876321
x:94

move 1 from y to x
y:87632
z:5
x:941

move 2 from y to z
y:8763
x:941
z:52

move 1 from x to z
x:94
y:8763
z:521

move 3 from y to x
y:876
z:521
x:943

move 1 from z to y
z:52
x:943
y:8761

move 2 from z to x
z:5
y:8761
x:9432

move 1 from y to x
y:876
z:5
x:94321

move 5 from z to y
z:
x:94321
y:8765

move 1 from x to z
x:9432
y:8765
z:1

move 2 from x to y
x:943
z:1
y:87652

move 1 from z to y
z:
x:943
y:876521

move 3 from x to z
x:94
y:876521
z:3

move 1 from y to x
y:87652
z:3
x:941

move 2 from y to z
y:8765
x:941
z:32

move 1 from x to z
x:94
y:8765
z:321

move 4 from x to y
x:9
z:321
y:87654

move 1 from z to y
z:32
x:9
y:876541

move 2 from z to x
z:3
y:876541
x:92

move 1 from y to x
y:87654
z:3
x:921

move 3 from z to y
z:
x:921
y:876543

move 1 from x to z
x:92
y:876543
z:1

move 2 from x to y
x:9
z:1
y:8765432

move 1 from z to y
z:
x:9
y:87654321

move 9 from x to z
x:
y:87654321
z:9

move 1 from y to x
y:8765432
z:9
x:1

move 2 from y to z
y:876543
x:1
z:92

move 1 from x to z
x:
y:876543
z:921

move 3 from y to x
y:87654
z:921
x:3

move 1 from z to y
z:92
x:3
y:876541

move 2 from z to x
z:9
y:876541
x:32

move 1 from y to x
y:87654
z:9
x:321

move 4 from y to z
y:8765
x:321
z:94

move 1 from x to z
x:32
y:8765
z:941

move 2 from x to y
x:3
z:941
y:87652

move 1 from z to y
z:94
x:3
y:876521

move 3 from x to z
x:
y:876521
z:943

move 1 from y to x
y:87652
z:943
x:1

move 2 from y to z
y:8765
x:1
z:9432

move 1 from x to z
x:
y:8765
z:94321

move 5 from y to x
y:876
z:94321
x:5

move 1 from z to y
z:9432
x:5
y:8761

move 2 from z to x
z:943
y:8761
x:52

move 1 from y to x
y:876
z:943
x:521

move 3 from z to y
z:94
x:521
y:8763

move 1 from x to z
x:52
y:8763
z:941

move 2 from x to y
x:5
z:941
y:87632

move 1 from z to y
z:94
x:5
y:876321

move 4 from z to x
z:9
y:876321
x:54

move 1 from y to x
y:87632
z:9
x:541

move 2 from y to z
y:8763
x:541
z:92

move 1 from x to z
x:54
y:8763
z:921

move 3 from y to x
y:876
z:921
x:543

move 1 from z to y
z:92
x:543
y:8761

move 2 from z to x
z:9
y:8761
x:5432

move 1 from y to x
y:876
z:9
x:54321

move 6 from y to z
y:87
x:54321
z:96

move 1 from x to z
x:5432
y:87
z:961

move 2 from x to y
x:543
z:961
y:872

move 1 from z to y
z:96
x:543
y:8721

move 3 from x to z
x:54
y:8721
z:963

move 1 from y to x
y:872
z:963
x:541

move 2 from y to z
y:87
x:541
z:9632

move 1 from x to z
x:54
y:87
z:96321

move 4 from x to y
x:5
z:96321
y:874

move 1 from z to y
z:9632
x:5
y:8741

move 2 from z to x
z:963
y:8741
x:52

move 1 from y to x
y:874
z:963
x:521

move 3 from z to y
z:96
x:521
y:8743

move 1 from x to z
x:52
y:8743
z:961

move 2 from x to y
x:5
z:961
y:87432

move 1 from z to y
z:96
x:5
y:874321

move 5 from x to z
x:
y:874321
z:965

move 1 from y to x
y:87432
z:965
x:1

move 2 from y to z
y:8743
x:1
z:9652

move 1 from x to z
x:
y:8743
z:96521

move 3 from y to x
y:874
z:96521
x:3

move 1 from z to y
z:9652
x:3
y:8741

move 2 from z to x
z:965
y:8741
x:32

move 1 from y to x
y:874
z:965
x:321

move 4 from y to z
y:87
x:321
z:9654

move 1 from x to z
x:32
y:87
z:96541

move 2 from x to y
x:3
z:96541
y:872

move 1 from z to y
z:9654
x:3
y:8721

move 3 from x to z
x:
y:8721
z:96543

move 1 from y to x
y:872
z:96543
x:1

move 2 from y to z
y:87
x:1
z:965432

move 1 from x to z
x:
y:87
z:9654321

move 7 from y to x
y:8
z:9654321
x:7

move 1 from z to y
z:965432
x:7
y:81

move 2 from z to x
z:96543
y:81
x:72

move 1 from y to x
y:8
z:96543
x:721

move 3 from z to y
z:9654
x:721
y:83

move 1 from x to z
x:72
y:83
z:96541

move 2 from x to y
x:7
z:96541
y:832

move 1 from z to y
z:9654
x:7
y:8321

move 4 from z to x
z:965
y:8321
x:74

move 1 from y to x
y:832
z:965
x:741

move 2 from y to z
y:83
x:741
z:9652

move 1 from x to z
x:74
y:83
z:96521

move 3 from y to x
y:8
z:96521
x:743

move 1 from z to y
z:9652
x:743
y:81

move 2 from z to x
z:965
y:81
x:7432

move 1 from y to x
y:8
z:965
x:74321

move 5 from z to y
z:96
x:74321
y:85

move 1 from x to z
x:7432
y:85
z:961

move 2 from x to y
x:743
z:961
y:852

move 1 from z to y
z:96
x:743
y:8521

move 3 from x to z
x:74
y:8521
z:963

move 1 from y to x
y:852
z:963
x:741

move 2 from y to z
y:85
x:741
z:9632

move 1 from x to z
x:74
y:85
z:96321

move 4 from x to y
x:7
z:96321
y:854

move 1 from z to y
z:9632
x:7
y:8541

move 2 from z to x
z:963
y:8541
x:72

move 1 from y to x
y:854
z:963
x:721

move 3 from z to y
z:96
x:721
y:8543

move 1 from x to z
x:72
y:8543
z:961

move 2 from x to y
x:7
z:961
y:85432

move 1 from z to y
z:96
x:7
y:854321

move 6 from z to x
z:9
y:854321
x:76

move 1 from y to x
y:85432
z:9
x:761

move 2 from y to z
y:8543
x:761
z:92

move 1 from x to z
x:76
y:8543
z:921

move 3 from y to x
y:854
z:921
x:763

move 1 from z to y
z:92
x:763
y:8541

move 2 from z to x
z:9
y:8541
x:7632

move 1 from y to x
y:854
z:9
x:76321

move 4 from y to z
y:85
x:76321
z:94

move 1 from x to z
x:7632
y:85
z:941

move 2 from x to y
x:763
z:941
y:852

move 1 from z to y
z:94
x:763
y:8521

move 3 from x to z
x:76
y:8521
z:943

move 1 from y to x
y:852
z:943
x:761

move 2 from y to z
y:85
x:761
z:9432

move 1 from x to z
x:76
y:85
z:94321

move 5 from y to x
y:8
z:94321
x:765

move 1 from z to y
z:9432
x:765
y:81

move 2 from z to x
z:943
y:81
x:7652

move 1 from y to x
y:8
z:943
x:76521

move 3 from z to y
z:94
x:76521
y:83

move 1 from x to z
x:7652
y:83
z:941

move 2 from x to y
x:765
z:941
y:832

move 1 from z to y
z:94
x:765
y:8321

move 4 from z to x
z:9
y:8321
x:7654

move 1 from y to x
y:832
z:9
x:76541

move 2 from y to z
y:83
x:76541
z:92

move 1 from x to z
x:7654
y:83
z:921

move 3 from y to x
y:8
z:921
x:76543

move 1 from z to y
z:92
x:76543
y:81

move 2 from z to x
z:9
y:81
x:765432

move 1 from y to x
y:8
z:9
x:7654321

move 8 from y to z
y:
x:7654321
z:98

move 1 from x to z
x:765432
y:
z:981

move 2 from x to y
x:76543
z:981
y:2

move 1 from z to y
z:98
x:76543
y:21

move 3 from x to z
x:7654
y:21
z:983

move 1 from y to x
y:2
z:983
x:76541

move 2 from y to z
y:
x:76541
z:9832

move 1 from x to z
x:7654
y:
z:98321

move 4 from x to y
x:765
z:98321
y:4

move 1 from z to y
z:9832
x:765
y:41

move 2 from z to x
z:983
y:41
x:7652

move 1 from y to x
y:4
z:983
x:76521

move 3 from z to y
z:98
x:76521
y:43

move 1 from x to z
x:7652
y:43
z:981

move 2 from x to y
x:765
z:981
y:432

move 1 from z to y
z:98
x:765
y:4321

move 5 from x to z
x:76
y:4321
z:985

move 1 from y to x
y:432
z:985
x:761

move 2 from y to z
y:43
x:761
z:9852

move 1 from x to z
x:76
y:43
z:98521

move 3 from y to x
y:4
z:98521
x:763

move 1 from z to y
z:9852
x:763
y:41

move 2 from z to x
z:985
y:41
x:7632

move 1 from y to x
y:4
z:985
x:76321

move 4 from y to z
y:
x:76321
z:9854

move 1 from x to z
x:7632
y:
z:98541

move 2 from x to y
x:763
z:98541
y:2

move 1 from z to y
z:9854
x:763
y:21

move 3 from x to z
x:76
y:21
z:98543

move 1 from y to x
y:2
z:98543
x:761

move 2 from y to z
y:
x:761
z:985432

move 1 from x to z
x:76
y:
z:9854321

move 6 from x to y
x:7
z:9854321
y:6

move 1 from z to y
z:985432
x:7
y:61

move 2 from z to x
z:98543
y:61
x:72

move 1 from y to x
y:6
z:98543
x:721

move 3 from z to y
z:9854
x:721
y:63

move 1 from x to z
x:72
y:63
z:98541

move 2 from x to y
x:7
z:98541
y:632

move 1 from z to y
z:9854
x:7
y:6321

move 4 from z to x
z:985
y:6321
x:74

move 1 from y to x
y:632
z:985
x:741

move 2 from y to z
y:63
x:741
z:9852

move 1 from x to z
x:74
y:63
z:98521

move 3 from y to x
y:6
z:98521
x:743

move 1 from z to y
z:9852
x:743
y:61

move 2 from z to x
z:985
y:61
x:7432

move 1 from y to x
y:6
z:985
x:74321

move 5 from z to y
z:98
x:74321
y:65

move 1 from x to z
x:7432
y:65
z:981

move 2 from x to y
x:743
z:981
y:652

move 1 from z to y
z:98
x:743
y:6521

move 3 from x to z
x:74
y:6521
z:983

move 1 from y to x
y:652
z:983
x:741

move 2 from y to z
y:65
x:741
z:9832

move 1 from x to z
x:74
y:65
z:98321

move 4 from x to y
x:7
z:98321
y:654

move 1 from z to y
z:9832
x:7
y:6541

move 2 from z to x
z:983
y:6541
x:72

move 1 from y to x
y:654
z:983
x:721

move 3 from z to y
z:98
x:721
y:6543

move 1 from x to z
x:72
y:6543
z:981

move 2 from x to y
x:7
z:981
y:65432

move 1 from z to y
z:98
x:7
y:654321

move 7 from x to z
x:
y:654321
z:987

move 1 from y to x
y:65432
z:987
x:1

move 2 from y to z
y:6543
x:1
z:9872

move 1 from x to z
x:
y:6543
z:98721

move 3 from y to x
y:654
z:98721
x:3

move 1 from z to y
z:9872
x:3
y:6541

move 2 from z to x
z:987
y:6541
x:32

move 1 from y to x
y:654
z:987
x:321

move 4 from y to z
y:65
x:321
z:9874

move 1 from x to z
x:32
y:65
z:98741

move 2 from x to y
x:3
z:98741
y:652

move 1 from z to y
z:9874
x:3
y:6521

move 3 from x to z
x:
y:6521
z:98743

move 1 from y to x
y:652
z:98743
x:1

move 2 from y to z
y:65
x:1
z:987432

move 1 from x to z
x:
y:65
z:9874321

move 5 from y to x
y:6
z:9874321
x:5

move 1 from z to y
z:987432
x:5
y:61

move 2 from z to x
z:98743
y:61
x:52

move 1 from y to x
y:6
z:98743
x:521

move 3 from z to y
z:9874
x:521
y:63

move 1 from x to z
x:52
y:63
z:98741

move 2 from x to y
x:5
z:98741
y:632

move 1 from z to y
z:9874
x:5
y:6321

move 4 from z to x
z:987
y:6321
x:54

move 1 from y to x
y:632
z:987
x:541

move 2 from y to z
y:63
x:541
z:9872

move 1 from x to z
x:54
y:63
z:98721

move 3 from y to x
y:6
z:98721
x:543

move 1 from z to y
z:9872
x:543
y:61

move 2 from z to x
z:987
y:61
x:5432

move 1 from y to x
y:6
z:987
x:54321

move 6 from y to z
y:
x:54321
z:9876

move 1 from x to z
x:5432
y:
z:98761

move 2 from x to y
x:543
z:98761
y:2

move 1 from z to y
z:9876
x:543
y:21

move 3 from x to z
x:54
y:21
z:98763

move 1 from y to x
y:2
z:98763
x:541

move 2 from y to z
y:
x:541
z:987632

move 1 from x to z
x:54
y:
z:9876321

move 4 from x to y
x:5
z:9876321
y:4

move 1 from z to y
z:987632
x:5
y:41

move 2 from z to x
z:98763
y:41
x:52

move 1 from y to x
y:4
z:98763
x:521

move 3 from z to y
z:9876
x:521
y:43

move 1 from x to z
x:52
y:43
z:98761

move 2 from x to y
x:5
z:98761
y:432

move 1 from z to y
z:9876
x:5
y:4321

move 5 from x to z
x:
y:4321
z:98765

move 1 from y to x
y:432
z:98765
x:1

move 2 from y to z
y:43
x:1
z:987652

move 1 from x to z
x:
y:43
z:9876521

move 3 from y to x
y:4
z:9876521
x:3

move 1 from z to y
z:987652
x:3
y:41

move 2 from z to x
z:98765
y:41
x:32

move 1 from y to x
y:4
z:98765
x:321

move 4 from y to z
y:
x:321
z:987654

move 1 from x to z
x:32
y:
z:9876541

move 2 from x to y
x:3
z:9876541
y:2

move 1 from z to y
z:987654
x:3
y:21

move 3 from x to z
x:
y:21
z:9876543

move 1 from y to x
y:2
z:9876543
x:1

move 2 from y to z
y:
x:1
z:98765432

move 1 from x to z
x:
y:
z:987654321

顯然這是一個遞歸函數,在函數的執行函數中,需多次進行自我調用。那么,這個遞歸函數是如何執行的?

先看任意兩個函數之間進行調用的情形:
與匯編程序中主程序和子程序之間的鏈接及信息交換相類似,在高級語言編制的程序中,調用函數和被調用函數之間的鏈接及信息交換需通過棧來執行。

通常,當在一個函數的運行期間調用另一個函數時,在運行被調用函數之前,系統需先完成3件事:
1.將所有的實在參數、返回地址等信息傳遞給被調用函數保存。
2.為被調用函數的局部變量分配存儲區。
3.將控制轉移到被調函數入口。

而從被調用函數返回調用函數之前,系統也完成3件事:
1.保存被調函數的計算結果。
2.釋放被調函數的數據區。
3.依照被調函數保存的返回地址將控制轉移到調用函數。

當有多個函數構成嵌套調用時,按照“后調用先返回”的原則,上述函數之間的信息傳遞和控制轉移必須通過"棧"來實現,即系統將整個程序運行時所需的數據空間安排在一個棧中,每當調用一個函數時,就為它在棧頂分配一個存儲區,每當從一個函數退出時,就釋放它的存儲區,則當前正運行的函數的數據區必在棧頂。

以下代碼為例:

int first(int s, int t);
int second(int d);

int main(){
    int m,n;
    ...
    first(m,n);
    1:...
}

int first(int s, int t){
    int i;
    ...
    second(i);
    2:...
}

int second(int d){
    int x,y;
    ...
}

主函數main中調用了函數first,而在函數first中又調用了函數second,
下圖展示了當前正在執行函數second中某個語句時棧的狀態,圖中1,2表示返回地址:


image.png

下圖展示了從函數second退出之后正執行函數first中某個語句時棧的狀態,圖中1表示返回地址:


image.png

一個遞歸函數的運行過程類似于多個函數的嵌套調用,只是調用函數和被調用函數是同一個函數,因此,和每次調用相關的一個重要概念是遞歸函數運行的“層次”:
假設調用該遞歸函數的主函數為第0層,
則從主函數調用遞歸函數為進入第1層;
從第i層遞歸調用本函數為進入“下一層”,即第i+1層。
反之,退出第i層遞歸應返回至上一層。

為了保證遞歸函數正確執行,系統需設立一個"遞歸工作棧"作為整個遞歸函數運行期間使用的數據存儲區。
每一層遞歸所需信息構成一個“工作記錄”,其中包括所有的實在參數、所有的局部變量以及上一層返回的地址。
每進入一層遞歸,就產生一個新的工作記錄壓入棧頂。
每退出一層遞歸,就從棧頂彈出一個工作記錄,則當前執行層的工作記錄必是遞歸工作棧棧頂的工作記錄,稱這個記錄為“活動記錄”,并稱指示活動記錄的棧頂指針為“當前環境指針”。

由于遞歸函數結構清晰,程序易讀,而且它的正確性容易得到證明,因此,利用允許遞歸調用的語言進行程序設計時,給用戶編制程序帶來很大方便。因為對這樣一類遞歸問題編程時,不需要用戶自己而由系統來管理遞歸工作棧。

理解遞歸

在初學遞歸的時候, 看到一個遞歸實現, 我們總是難免陷入不停的回溯驗證之中, 因為回溯就像反過來思考迭代, 這是我們習慣的思維方式, 但是實際上遞歸不需要這樣來驗證。
比如, 階乘的計算:
階乘的定義: “一個正整數的階乘(英語:factorial)是所有小于或等于該數的正整數的積,并且0的階乘為1。”
代碼實現:

int Fact(int n){
    if(n<=1){
        return 1;
    }else{
        return n * Fact(n - 1);
    }
}

我們怎么判斷這個階乘的遞歸計算是否是正確的呢?
回溯的思考方式是這么驗證的, 比如當n = 4時, 那么Fact(4)等于4 *Fact(3), 而Fact(3)等于3 * Fact(2), Fact(2)等于2 * Fact(1), 等于2 * 1, 所以Fact(4)等于4 * 3 * 2 * 1. 這個結果正好等于階乘4的迭代定義。
用回溯的方式思考雖然可以驗證當n = 某個較小數值是否正確, 但是其實無益于理解。

Paul Graham提到一種驗證方法:
當n=0, 1的時候, 結果正確,
假設函數對于n是正確的, 函數對n+1結果也正確,
如果這兩點是成立的,我們知道這個函數對于所有可能的n都是正確的。

這種方法很像數學歸納法, 也是遞歸正確的思考方式。

如何找到一個適用遞歸算法的問題

上面講了怎么理解遞歸是正確的, 同時可以看到在有遞歸算法描述后, 其實程序很容易寫, 那么最關鍵的問題就是, 我們怎么找到一個問題的遞歸算法呢?

Paul Graham提到, 你只需要做兩件事情:
你必須要示范如何解決問題的一般情況, 通過將問題切分成有限小并更小的子問題。
你必須要示范如何通過有限的步驟, 來解決最小的問題(基本用例)。

如果這兩件事完成了, 那問題就解決了。因為遞歸每次都將問題變得更小, 而一個有限的問題終究會被解決的, 而最小的問題僅需幾個有限的步驟就能解決。

在什么情況下用遞歸

遞歸并不一定適用所有情況, 很多情況用迭代遠遠比用遞歸好了解;其次, 相對來說, 遞歸的效率往往要低于迭代的實現,同時, 內存消耗也會更大。

以上面的階乘算法為例,如果n = 100,很顯然這段程序需要遞歸地調用自身100次。這樣調用深度至少就到了100。棧的大小是有限的,當n變的更大時,有朝一日總會使得棧溢出,從而程序崩潰。除此之外,每次函數調用的開銷會導致程序變慢。所以說這段程序十分不好。

什么是好的遞歸:如果遞歸能夠將問題的規模縮小,那就是好的遞歸。

怎樣才算是規模縮小了呢。舉個例子,比如要在一個有序數組中查找一個數,最簡單直觀的算法就是從頭到尾遍歷一遍數組,這樣一定可以找到那個數。如果數組的大小是N,那么我們最壞情況下需要比較N次,所以這個算法的復雜度記為O(N)。有一個大名鼎鼎的算法叫二分法,它的表達也很簡單,由于數組是有序的,那么找的時候就從數組的中間開始找,如果要找的數比中間的數大,那么接著查找數組的后半部分(如果是升序的話),以此類推,知道最后找到我們要找的數。稍微思考一下可以發現,如果數組的大小是N,那么最壞情況下我們需要比較logN次(計算機世界中log的底幾乎總是2),所以這個算法的復雜度為O(logN)。

簡單的分析一下二分法為什么會快。可以發現二分法在每次比較之后都幫我們排除了一半的錯誤答案,接下去的一次只需要搜索剩下的一半,這就是說問題的規模縮小了一半。而在直觀的算法中,每次比較后最多排除了一個錯誤的答案,問題的規模幾乎沒有縮小(僅僅減少了1)。這樣的遞歸就稍微像樣點了。

重新看階乘的遞歸,每次遞歸后問題并沒有本質上的減小(僅僅減小1),這和簡單的循環沒有區別,但循環沒有函數調用的開銷,也不會導致棧溢出。所以結論是如果僅僅用遞歸來達到循環的效果,那還是改用循環吧。

總結一下,遞歸的意義就在于將問題的規模縮小,并且縮小后問題并沒有發生變化(二分法中,縮小后依然是從數組中尋找某一個數),這樣就可以繼續調用自身來完成接下來的任務。我們不用寫很長的程序,就能得到一個十分優雅快速的實現。

如何寫遞歸

以二分查找算法為例子,首先給出函數原型,返回值是元素在數組中的位置,如果查找失敗返回-1。:

int binarySearch(int *array,int start,int end,int num_wanted)

1.基準情況
基準情況其實就是遞歸的終止條件。其實在實際中,這是十分容易確定的。例如在二分查找中,終止條件就是找到了我們想要的數或者搜索完了整個數組(查找失敗)。

    if(end < start){
        return -1;
    }else if(num_wanted == array[middle]){
        return middle;
    }

2.不斷演進
演進的過程就是我們思考的過程,二分查找中,就是繼續查找剩下的一半數組。

    if(num_wanted > array[middle]){
        index = binarySearch(array,middle + 1,end,num_wanted);
    }else{
        index = binarySearch(array,start,middle - 1,num_wanted);
    }

3.用人的思考方式設計
這條法則我認為是非常重要的,它不會出現在編碼中,但卻是理解遞歸的一條捷徑。它的意思是說,在一般的編程實踐中,我們通常需要用大腦模擬電腦執行每一條語句,從而確定編碼的正確性,然而在遞歸編碼中這是不需要的。遞歸編碼的過程中,只需要知道前兩條法則就夠了。之后我們就會看到這條法則的如何工作的了。
4. 不要做重復的事情
在任何編碼中,這都是不應該出現的事情,但是在遞歸中這更加可怕,可能由于一次多余的遞歸使得算法增加數級復雜度。

完整的二分法的程序:

int binarySearch(int *array,int start,int end,int num_wanted){
    int middle = (end - start)/2 + start; //1
    int index;
    if(end < start){
        return -1;
    }else if(num_wanted == array[middle]){
        return middle;
    }
    if(num_wanted > array[middle]){
        index = binarySearch(array,middle + 1,end,num_wanted); //2
    }else{
        index = binarySearch(array,start,middle - 1,num_wanted); //3
    }
    return index; //4
}

程序中除了1和4都已經在前兩條法則的實現中了。
1不必多說,4是一個比較關鍵的步驟,經常容易被忘記。
這里就用到第3條法則,編寫的時候只要認為2或者3一定會正確運行,并且立刻返回,
不要考慮2和3內部是如何運行的,因為這就是你現在在編寫的。
這樣4該如何處理就是顯而易見的了,在這里只需要將找到的index返回就可以了。

第4條法則在這個例子里并沒有出現,我們可以看一下斐波那契數列的遞歸實現。

long int fib(int n){
    if(n <= 1){
        return 1;
    }else{
        return fib(n-1) + fib(n-2); // 1
    }
}

乍看之下,這段程序很精練,它也是一段正確的遞歸程序,有基準條件、不斷推進。但是如果仔細分析一下它的復雜度可以發現,如果我們取n=N,那么每次fib調用會增加額外的2次fib調用(在1處),即fib的運行時間T(N) = T(N-1) + T(N-2),可以得到其復雜度是O(2^N),幾乎是可見的復雜度最大的程序了。

所以如果在一個遞歸程序中重復多次地調用自身,又不縮小問題的規模,通常不是個好主意。

尾遞歸

到此其實你已經可以寫出任何一個完整的遞歸程序了,雖然上面的例子比較簡單,但是方法總是這樣的。

不過我們可以對遞歸程序再進一步分析。二分查找的遞歸算法中我們注意到在遞歸調用之后僅僅是返回了其返回值,這樣的遞歸稱作尾遞歸

盡管在編寫的時候不必考慮遞歸的調用順序,但真正運行的時候,遞歸的函數調用過程可以分為遞和歸兩部分。
在遞歸調用之前的部分稱作遞,調用之后的部分稱作歸。

而尾遞歸在歸的過程中實際上不做任何事情,對于這種情況可以很方便的將這個遞歸程序轉化為非遞歸程序。

int binarySearch(int *array,int start,int end,int num_wanted){
    int middle;
search:
    middle = (end - start)/2 + start;
    if(end < start){
        return -1;
    }else if(num_wanted == array[middle]){
        return middle;
    }
    if(num_wanted > array[middle]){
        start = middle+1;
        end = end;
        goto search;
        //index = binary_search(array, middle+1, end, num_wanted);
    }else{
        start = start;
        end = middle-1;
        goto search;
        //index = binary_search(array, start, middle-1, num_wanted);
    }
}

上面就是去除遞歸后的二分查找的程序,我們只需要在程序開頭處加上一個標號,在原本遞歸調用處修改參數的值并goto到標號就能完成這個工作。

事實上,如果你寫出了一個尾遞歸程序,在編譯的時候編譯器很可能就這樣幫你優化了。當然這樣的程序非常不符合我們的習慣,它實際上是將遞歸轉化為了循環。

循環還是應當使用while或者for實現,仔細地將上面這段程序改成while循環就成了這樣。

int binarySearch(int *array,int start,int end,int num_wanted){
    int middle = (end - start)/2 + start;
    while(end >= start && num_wanted != array[middle]){
        if(num_wanted > array[middle]){
            start = middle+1;
        }else{
            end = middle-1;
        }
        middle = (end - start)/2 + start;
    }
    if(end < start){
        return -1;
    }else{
        return middle;
    }
}

這樣就更加符合我們的習慣了,它比遞歸的版本速度略有提高,最重要的是不會導致棧溢出。

部分內容摘自怎么寫一個遞歸程序

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375