递归的概念:
直接或间接地调用自己的算法称为递归算法。
分治的基本思想
分治的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些子问题,然后将各个子问题的解合并得到原问题的解
递归分治的四个特性:
1、问题缩小到一定程度可以直接求解
2、(最优子结构性)原问题可分解为多个规模较小的相同子问题
3、子问题的解合并成为原问题的解
4、子问题相互独立
典型问题
1、合并排序
public class MergeSort {
public static void sort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
sort(nums, low, mid);
// 右边
sort(nums, mid + 1, high);
// 左右归并
merge(nums, low, mid, high);
}
}
public static void merge(int[] nums, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low;// 左指针
int j = mid + 1;// 右指针
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (nums[i] < nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = nums[i++];
}
// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = nums[j++];
}
// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
nums[k2 + low] = temp[k2];
}
}
public static void main(String[]args)
{
int arr[]={1,2,7,4,6,5,3};
MergeSort mergeSort=new MergeSort();
mergeSort.sort(arr, 0, 6);
for(int i=0;i<=6;i++)
{
System.out.println(arr[i]);
}
}
}
2、棋盘覆盖
具体的题目是:
有一个 的方格棋盘,恰有一个方格是黑色的,其它为白色。你的任务是用包含3个方格的L型牌覆盖所有白色方格。黑方格不能被覆盖,且任意一个白色方格不能同时被两个或者多个L型牌覆盖。如图为L型牌的4种旋转方式。
核心思想:
将棋盘划分为四个小部分,在交点处放置一个L形牌。这样形成四个相同的子问题。按照此思想进行递归,直到棋盘被划分到2*2.
3、快速排序
public class sort {
public void quickSort(int[] arr, int low, int high) {
if (low < high) {
int index = getIndex(arr, low, high);
//以排好序的元素为界,将原数组分为两部分,递归
quickSort(arr, 0, index - 1);
quickSort(arr, index + 1, high);
}
}
public int getIndex(int[] arr, int low, int high) {
int tmp=arr[low];
int i=low;
int j=high;
int t;
while (true) {
// 当队尾的元素大于等于基准数据时,向前挪动high指针
while(arr[j] >= tmp && i < j)
j--;
while(arr[i] <= tmp && i < j)//再找右边的
i++;
if(i < j)//交换两个数在数组中的位置
{
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
else
break;
}
arr[low] = arr[i];
arr[i] = tmp;
return i;
}
public static void main (String[]args)
{
int a[]={4,5,7,1,3,2,6};
sort test=new sort();
test.quickSort(a,0,6);
for(int i=0;i<=6;i++)
{
System.out.println(a[i]);
}
}
}
4、循环赛日程表
设有n=2^k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次;
(2)每个选手一天只能参赛一次;
(3)循环赛在n-1天内结束。
请按此要求将比赛日程表设计成有n行和n-1列的一个表。在表中的第i行,第j列处填入第i个选手在第j天所遇到的选手。其中1≤i≤n,1≤j≤n-1。8个选手的比赛日程表如下图:
#define _CRT_SECURE_NO_DEPRECATE;
#define _CRT_SECURE_NO_WARNINGS;
#include<stdio.h>
#define N 100
int a[N][N];
void Scheduled(int i, int size)
{
int x, y;
if (size == 2)
{
a[i][1] = i;
a[i + 1][1] = i + 1;
}
else {
Scheduled(i, size / 2);
Scheduled(i + size / 2, size / 2);
}
for (x = i;x<i + size / 2;x++)
for (y = size / 2 + 1;y <= size;y++) {
a[x][y] = a[x + size / 2][y - size / 2];
}
for (x = i + size / 2;x<i + size;x++)
for (y = size / 2 + 1;y <= size;y++) {
a[x][y] = a[x - size / 2][y - size / 2];
}
}
void Print(int n) {
int p, q;
FILE *fp;
fp = fopen("D:\\222017321062109_循环赛.txt", "wb");
for (p = 1;p <= n;p++) {
for (q = 1;q <= n;q++) {
printf("%d\t", a[p][q]);
fprintf(fp, "%d\t", a[p][q]);
}
printf("\n");
fprintf(fp, "\r\n");
}
fclose(fp);
}
int main() {
int n;
printf("请输入n的值(请确保N为2的K次方,K>=0):\n");
scanf_s("%d", &n);
Scheduled(1, n);
Print(n);
return 0;
}