递归算法是指直接或者间接调用自身的算法,分治算法是指将一个问题规模分为n个规模小、方便处理的子问题,最后再将这些子问题得到的结果合并。在计算机求解问题的过程中,计算时间与问题的规模呈正比,问题规模越大越难处理,规模越小就容易处理。因此当遇到问题无法处理时,可以考虑能不能将之转化为规模较小的问题,递归和分治经常一起使用。
设a、b和c是3个塔座:开始时,塔座a上有n个自上而下、由小到大地叠在一起圆盘,各圆盘从小到大编号为1, 2, …, n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍按同样顺序叠置,移动圆盘时遵守以下移动规则:
规则1:每次只能移动1个圆盘;
规则2:不允许将较大的圆盘压在较小的圆盘之上;
规则3:在满足移动规则1和2的前提下,可将圆盘移至a、b和c中任一塔座上。
(1) 算法设计思路
①要是n为1的话,直接将圆盘从a移动到b即可
②要是n大于1的话,要将n个圆盘从a移动到b,可以先将上面的n-1个圆盘从a移动到c再将最底下的圆盘移动到b,这样就移动了当前最大的圆盘到了相应位置,接下来就考虑将剩下的n-1个圆盘从c移动到b,此时只要借助a,将c上的n-2歌圆盘移动到a,那么此时就能将第n-1个圆盘从c移动到b,这样一来,每次问题的规模越来越小,直到变成①中的情况。
(2) 算法实现的伪码
函数:move(a, b, c, n)
输入:前三个参数为三座塔,n为个数,即将n个圆盘从a借助c移动到b
输出:输出语句“将圆盘x从x移动到x”
S1: if n==1
S2: then print “将圆盘”+n+“从”+a+“移动到”+b
S3: else move(a, c, b, n-1)//将n-1个圆盘从a借助b移动到c
S4: print “将圆盘”+n+“从”+a+“移动到”+b
S5: move(c, b, a, n-1)//将n-1个圆盘从c借助a移动到b
(3) 算法实现的源码
private static void move(char a, char b, char c, int n) {
if(n == 1) {
System.out.println("将圆盘"+n+"从"+a+"移动到"+ b);
}else {
move(a, c, b, n-1);//调用子问题
System.out.println("将圆盘"+n+"从"+a+"移动到"+b);
move(c, b, a, n-1);
}
}
(4) 算法运行结果
move(‘a’, ‘b’, ‘c’, 1)结果为:
move(‘a’, ‘b’, ‘c’, 3)结果为:
(5) 算法的时间复杂性分析
在该算法中,如果n为1,时间复杂度为O(1)
若n大于1,则开销为将n-1个圆盘从a移动到c,将1个圆盘从a移动到b,将n-1个圆盘从c移动到b,总计为T(n-1)+O(1)+T(n-1)
所以T(n)=2*T(n-1)+T(1)
所以时间复杂度为O(2^n)
归并排序是经典的分治算法
以{28, 4, 94, 32, 15, 76, 23, 56, 85, 12}的排序为例:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] workSpace = {28, 4, 94, 32, 15, 76, 23, 56, 85, 12};
mergeSort(workSpace, 0, workSpace.length-1);
for (int i = 0; i < workSpace.length; i++) {
System.out.print(workSpace[i]+" ");
}
}
public static void mergeSort(int[] workSpace, int first,int last) {
if(first == last) {
//first到last只有一个元素,不用排序
return;
}
int[] newWorkSpace = new int[last - first +1];//存放该范围排序好的数组
int mid = (last + first)/2;//找到中点
//得到左右子问题排好序的结果
mergeSort(workSpace, first, mid);
mergeSort(workSpace, mid+1, last);
int l = first, r = mid+1, n = 0;
while(l <= mid && r <= last) {
//每次都找到最小数放在新数组里面,并且更新选取的最小数的那个数组的下标
newWorkSpace[n++] = workSpace[l] < workSpace[r] ? workSpace[l++] : workSpace[r++];
}
//以下两个数组是为了完整遍历完两个数组
while(l <= mid) {
newWorkSpace[n++] = workSpace[l++];
}
while(r <= last) {
newWorkSpace[n++] = workSpace[r++];
}
//将排好序的数组赋值到原来位置
for (int i = last; i >= first; i--) {
workSpace[i] = newWorkSpace[--n];
}
}
}
归并排序详见百科:
https://baike.baidu.com/item/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/1639015?fr=aladdin