轉換思路
https://zhuanlan.zhihu.com/p/151322731
https://zhuanlan.zhihu.com/p/151380432
舉例
以斐波拉契數列生成爲例,典型的遞歸版本如下
int fibonacci(int n) {
if (n == 0 || n == 1) {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
找到其中的方法調用,標記上 flag,並分析需要的局部變量,將代碼改寫爲
int fibonacci(int n) {
// flag 0 函數入口
if (n == 0 || n == 1) {
return 1;
} else {
int result0 = fibonacci(n - 1);
// flag 1 第一個函數調用結束
// 拿到第一個函數的返回值,保存爲局部遍歷
int localVariable0 = result0;
int result1 = fibonacci(n - 2);
// flag 2 第二個函數調用結束
// 拿到第二個函數的返回值
int localVariable1 = result1;
return localVariable0 + localVariable1;
}
}
可見函數的棧幀爲
{null/*result*/,0/*flag*/,n/*入參*/,null/*第一個局部變量*/,null/*第二個局部變量*/}
由此轉寫爲非遞歸形式
int fibonacciNoRecur(int n) {
Deque<Object[]> stack = new ArrayDeque<>(64); //棧 調用層數最大 64
// 當前函數(fibonacciNoRecur)的棧幀,棧幀size=1,僅用來接收遞歸調用的返回值
Object[] frameOfFibonacciNoRecur = {null};
stack.push(frameOfFibonacciNoRecur); // 入棧
// 函數調用第一層
// 棧幀含義:
// [0] 接收調用函數的返回值
// [1] flag,標記遞歸函數執行的位置
// [2] 入參,只有一個,即 n
// [3] [4] 兩個局部變量
Object[] callFrame = {null, 0, n, null, null};
stack.push(callFrame);
while (stack.size() > 1) {// 如果棧大於 1,表示遞歸沒有結束
Object[] frame = stack.peek();// 獲取當前棧幀,peek()不退棧
int arg0 = (int) frame[2];// 當前入參
int flag = (int) frame[1];// 當前 flag 標誌位
switch (flag) {
case 0: // flag == 0 函數入口
if (arg0 == 0 || arg0 == 1) {
stack.pop();// 退棧
stack.peek()[0] = 1;// 將返回值給調用者,即此時棧頂棧幀 0 號位置
} else {
//函數調用,開闢棧空間
stack.push(new Object[]{null, 0, arg0 - 1, null, null});
frame[1] = 1;// 修改當前棧幀的 flag=1
}
break;
case 1: // flag == 1 第一個函數調用結束
int return1 = (int) frame[0];// 拿到返回值,位於當前棧幀的 0 號位置
frame[3] = return1;// 將返回值存入局部變量
stack.push(new Object[]{null, 0, arg0 - 2, null, null});// 第二次函數調用
frame[1] = 2;// 修改當前棧幀的 flag=2
break;
case 2:
int return2 = (int) frame[0];// 拿到返回值,位於當前棧幀的 0 號位置
frame[4] = return2;// 將返回值存入局部變量
int localVariable0 = (int) frame[3];// 取出局部變量
int localVariable1 = (int) frame[4];
// 退棧
stack.pop();// 退棧
stack.peek()[0] = localVariable0 + localVariable1;// 將返回值給調用者,即此時棧頂棧幀 0 號位置
}
}
// 遞歸調用結束,此時棧頂即當前函數(fibonacciNoRecur)的棧幀
frameOfFibonacciNoRecur = stack.pop();
return (int) frameOfFibonacciNoRecur[0];
}