递归程序巧妙地完成了回溯,那么如何将递归程序转换成非递归呢?
一般来说,递归的时候一定要分清哪些是可以直接由顶进行到底的(不需要回溯,即手头可以处理好的事件),哪些是需要回溯的(这部分可能根据回溯的顺序来排回溯的优先级,需要标注不同的状态,即待办事件),贴网上某人的博文,觉得总结的非常好。
通用法则为:
以下是如何把快速排序由递归程序转换为非递归程序:
递归的版本为:
void quicksort1(int a[],int l,int r)
{//标准版
int i=l,j=r,mid=a[(i+j)/2];
while(i<=j){
while(a[i]>mid) i++;
while(a[j]<mid) j--;
if(i<=j){
swap(a[i],a[j]);
i++;j--;
}
}
if( i<r ) quicksort1(a,i,r);
if( j>l ) quicksort1(a,l,j);
}
一般来说,再次调用递归存在向栈push元素的可能,但并不是绝对的。。。要弄清当前事情全部处理完,就要回溯,会从栈顶取,因此区分当前事情是否处理完特别重要。。。贴一个错误的代码:
void quicksort2(int a[],int l,int r)
{//错误版本
stack<sValue> s;
sValue sv;
sv.first=l;
sv.second=r;
s.push(sv);
while(!s.empty()){
sv=s.top();
s.pop();
int i=sv.first,j=sv.second,mid=a[(i+j)/2];
while(i<=j){
while(a[i]>mid) i++;
while(a[j]<mid) j--;
if(i<=j){
swap(a[i],a[j]);
i++;j--;
}
}
if(i<r){
sv.first=i; sv.second=r; s.push(sv);
}
if(j>l){
sv.first=l; sv.second=j; s.push(sv);
}
}
}
再上一个正确的代码:
void quicksort3(int a[],int l,int r)
{
int i,j,mid;
stack<sValue> s;
sValue sv;
while(l<r||!s.empty()){
while(l<r){
i=l;j=r;mid=a[(i+j)/2];
while(i<=j){
while(a[i]>mid) i++;
while(a[j]<mid) j--;
if(i<=j){
swap(a[i],a[j]);
i++;j--;
}
}
if(j>l){
sv.first=l; sv.second=j; s.push(sv);
}
l=i; r=r;
}
if(!s.empty()){
sv=s.top();
s.pop();
l=sv.first; r=sv.second;
}
}
}
用Python可以写一个更简洁的版本:
from itertools import permutations
def partition(a, l, r):
i, j = l, r
pivot = a[(i+j)//2]
while (i <= j): #Ensure i, j are not at the same position after partition, so i <=j instead of i <j
while (a[i] < pivot):
i = i + 1
while (a[j] > pivot):
j = j - 1
if (i <= j):
a[i], a[j] = a[j], a[i]
i, j = (i + 1), (j - 1)
return i, j
def q1(a, l, r):
i, j = partition(a, l, r)
if (i < r):
q1(a, i, r)
if (j > l):
q1(a, l, j)
return a
def q2(a, l, r):
stack = []
while (stack or l < r):
while (l < r):
i, j = partition(a, l, r)
if (j > l):
stack.append({'left':l, 'right':j})
l, r = i, r
if (stack):
top = stack.pop()
l, r = top['left'], top['right']
return a
for perm in permutations([1,2,3,3,4,5,6], 7):
y = q2(list(perm), 0, 6)
print(y)