1.基本思想:”分而治之“,将一个复杂问题分解成两个或多个相同或相似的子问题,再把子问题分解成更小的子问题…直到最后子问题可以简单的直接求解,原问题的解即为所有子问题的解的合并。
2.经典例子:
1.汉诺塔:
public class Hanoitower {
public static void main(String[] args) {
hanoiTower(3, 'A', 'B', 'C');
}
private static void hanoiTower(int num, char a, char b, char c) {
//如果只有一个盘子
if (num == 1) {
System.out.println("第1个盘子从" + a + "->" + c);
} else {
//如果有n>=2个,可抽象成两个盘,一个是最下面的一个盘,另一个是上面的所有盘
//将上面的盘从A->B
hanoiTower(num - 1, a, c, b);
//移动最下面的盘从A->C
System.out.println("第" + num + "个盘从" + a + "->" + c);
//将上面的盘从B->C
hanoiTower(num - 1, b, a, c);
}
}
}
2.二分查找(递归版):
public class BinarySearch {
public static void main(String[] args) {
int[] a = {
1, 2, 3, 4, 5};
System.out.println(search(a, 1));
System.out.println(search(a, 3));
System.out.println(search(a, 5));
System.out.println(search(a, 9));
}
public static boolean search(int[] a, int key) {
return inSearch(a, 0, a.length - 1, key);
}
private static boolean inSearch(int[] a, int low, int high, int key) {
int mid;
while (low <= high) {
mid = (low + high) / 2;
if (key == a[mid]) {
return true;
} else if (key < a[mid]) {
return inSearch(a, 0, mid - 1, key);
} else {
return inSearch(a, mid + 1, high, key);
}
}
return false;
}
}
3.归并排序(递归版):
目标:使序列有序。
基本思想(图解):初始序列含有n个元素,则可看成是n个有序子序列,每个子序列的长度为1,然后两两归并,得到Math.ceil(n/2)个长度为2或1的有序子序列;再两两归并,…,如此重复,直到得到一个长度为n的有序序列位置。
代码实现:
public class MergeSort {
public static void sort(int[] a) {
int n = a.length - 1;
MSort(a, a, 1, n);
}
//将SR[s..n]归并为有序的TR1[s..n]
private static void MSort(int[] SR, int[] TR1, int s, int n) {
int m;
int[] TR2 = new int[n + 1]; // SR, TR1, TR2等长
if (s == n) {
TR1[s] = SR[s];
} else {
m = (s + n) / 2; // 将SR[s..n]平均分为SR[s..m]和SR[m+1..n]
MSort(SR, TR2, s, m); // 递归将SR[s..m]归并为有序的TR2[s..m]
MSort(SR, TR2, m + 1, n); // 递归将SR[m+1..n]归并为有序的TR2[m+1..n]
Merge(TR2, TR1, s, m, n); // 将TR2[s..m]和TR2[m+1..n]归并到TR1[s..n]
}
}
// 将有序的SR[s..m]和SR[m+1..n]归并为有序的TR[i..n]
private static void Merge(int[] SR, int[] TR, int s, int m, int n) {
int j, k, l; // k为左块的起始下标,j为右块的起始下标
for (k = s, j = m + 1; s <= m && j <= n; k++) {
//SR中记录由小到大归并入TR
if (SR[s] < SR[j]) {
TR[k] = SR[s++];
} else {
TR[k] = SR[j++];
}
}
if (s <= m) {
for (l = 0; l <= m - s; l++) {
TR[k + l] = SR[s + l]; // 将剩余的SR[s..m]复制到TR
}
}
if (j <= n) {
for (l = 0; l <= n - j; l++) {
TR[k + l] = SR[j + l]; // 将剩余的SR[j..m]复制到TR
}
}
}
}
4.快速排序(递归版):
目标:使序列有序。
基本思想:通过一趟排序将待排序记录分割成独立的两部分,其中每一部分记录的关键字均比另一部分记录的关键字小,则可以分别对着两部分记录继续进行排序,以达到整个序列有序的目的。
代码实现:
public class QuickSort {
public static void sort(int[] a) {
int n = a.length - 1;
QSort(a, 1, n);
}
private static void QSort(int[] a, int low, int high) {
int pivot; // 枢轴的下标,将某个数放在此位置,使得它左边的值都比它小,右边的都比它大
if (low < high) {
pivot = Partition(a, low, high); // 将a[low..high]一分为二,算出枢轴下标pivot
QSort(a, low, pivot - 1); // 对低子表递归排序
QSort(a, pivot + 1, high); // 对高子表递归排序
}
}
// 交换顺序表a中子表的记录,使枢轴记录到为,并返回其位置
private static int Partition(int[] a, int low, int high) {
int pivotkey = a[low]; // 用子表的第一个记录作枢轴记录
while (low < high) {
// 从表的两端交替向中间扫描
while (low < high && a[high] >= pivotkey) {
high--;
}
swap(a, low, high); // 将比枢轴值小的记录交换到低端
while (low < high && a[low] <= pivotkey) {
low++;
}
swap(a, low, high); // 将比枢轴值大的记录交换到高端
}
return low; // 最终low == high,所有返回枢轴所在位置
}
private static void swap(int[] a, int x, int y) {
int temp;
temp = a[x];
a[x] = a[y];
a[y] = temp;
}
}