验证了一下在图书馆脑洞出来的算法实现的正确性
当谈到一个程序的递归与非递归写法时,一般首先想到数据结构中二叉树三种遍历对应的递归与非递归写法。当递归程序中只有一次对自身的调用,也就是线性增长时,很容易解递归。例如二分查找的递归与非递归写法。
int find(int a[],int key,int low,int high){//a为升序序列 if(low<=high){ int mid=(low+high)/2; if(a[mid]==key) return mid; else if(a[mid]<key) return find(a,key,mid+1,high); else return find(a,key,low,mid-1); } return -1; } int find_2(int a[],int key,int low,int high){//a为升序序列 int i=low,j=high; int mid=0; while (i<=j){ mid=(low+high)/2; if(a[mid]==key)return mid; else if(a[mid]<key) i=mid+1; else j=mid-1; } return -1; }
而程序中有多次自身调用时,例如二叉树的遍历、快速排序的递归写法,均为二次调用,此时递归非线性增长,故需要引入数据结构--栈用来保存现场,实现程序的非递归写法。
为了有个二叉树做测试,用递归写了个先序和中序序列确定一颗二叉树:
class node{ public char e; public node left,right; } class child{ public int left_low,left_high; public int right_low,right_high; } public child findInOrder(char in[],int root,int low,int high){ for(int i=low;i<=high;i++){ if(in[i]==root){ child child=new child(); child.left_low=low;child.left_high=i-1; child.right_low=i+1;child.right_high=high; return child; } } return null; } public void PreInBuild(node parent,char pre[],int p_low,char in[],int in_low,int in_high){ if (p_low<pre.length){ parent.e=pre[p_low]; child child= findInOrder(in,pre[p_low],in_low,in_high); if (child.left_low<=child.left_high) { parent.left=new node(); PreInBuild(parent.left, pre, p_low + 1 , in, child.left_low, child.left_high); } if (child.right_low<=child.right_high) { parent.right=new node(); PreInBuild(parent.right, pre, p_low + child.left_high - child.left_low + 2, in, child.right_low, child.right_high); } } }
char pre[]={'a','b','d','e','h','c','f','g','i','k','j'}; char in[]={'d','b','h','e','a','f','c','i','k','g','j'};
对应的先序遍历:
public void preOrder(node tree){ if(tree!=null){ System.out.print(tree.e+""); preOrder(tree.left); preOrder(tree.right); } }
非递归写法:
public void preOrder_2(node tree){ node p=tree; Stack<node>stack=new Stack<>(); while (p!=null||!stack.empty()){ while (p!=null){ System.out.print(p.e+""); stack.push(p); p=p.left; } if(!stack.empty()){ p=stack.pop().right; } } }
接着让我们看一下快排的递归写法:
1 public int patition(int a[],int low,int high){ 2 if(low<high&&high<a.length) { 3 int key = a[high]; 4 while (low < high) { 5 while (low < high && a[low] <= key) 6 low++; 7 a[high] = a[low]; 8 while (low < high && a[high] >= key) 9 high--; 10 a[low] = a[high]; 11 } 12 a[low] = key; 13 return low; 14 } 15 return -1; 16 }
public void quickSort(int a[],int low,int high){ if(low<high&&high<a.length){ int mid=patition(a,low,high); quickSort(a,low,mid-1); quickSort(a,mid+1,high); } }
形式上与二叉树先序遍历的递归写法相似,所以将二叉树节点视为对场景的保存,出栈的也就是我们以线性方式实现非线性时忽略的部分,(在二叉树中是每个节点的右子树,故在快排中是partition后key值的右侧。我们要对右半的起止下标压栈,故有:
class SharpShot{ public int low,high; } public void quickSort_2(int a[],int low,int high){ Stack<SharpShot>stack=new Stack<>(); int i=low,j=high; while (i<j||!stack.empty()){ while (i<j) { int mid = patition(a, i, j); SharpShot shot=new SharpShot(); shot.low=mid+1; shot.high=j; stack.push(shot); j=mid-1; } if(!stack.empty()){ SharpShot shot=stack.pop(); i=shot.low; j=shot.high; } } }
由此可见递归生成k叉树,k>2时大佬们自己体会吧……