1、递归函数:用函数自身给出定义的函数 2、递归算法:直接或间接地调用自身的算法
1、边界条件 2、递归方程
n ! = { 1 , n = 0 【边界条件】 n ( n − 1 ) ! , n > 0 【递归方程】 n!=\begin{cases} 1&\text{,$n=0$【边界条件】}\\ n(n-1)!&\text{,$n>0$【递归方程】} \end{cases} n!={1n(n−1)!,n=0【边界条件】,n>0【递归方程】
int factorial(int n){ if(n < 0) return false; if(n == 0) return 1; if(n > 0) return n*factorial(n - 1); }F ( n ) = { 1 , n = 0 或 1 【边界条件】 F ( n − 1 ) + F ( n − 2 ) , n > 1 【递归方程】 F(n)=\begin{cases} 1&\text{,$n=0或1$【边界条件】}\\ F(n-1)+F(n-2)&\text{,$n>1$【递归方程】} \end{cases} F(n)={1F(n−1)+F(n−2),n=0或1【边界条件】,n>1【递归方程】
int Fibonacci(int n){ if(n < 0) return false; if(n == 0 || n == 1) return 1; if(n > 1) return Fibonacci(n - 1) + Fibonacci(n - 2); }全排列 A n n A^n_n Ann:n个不同元素按照一定的顺序排列起来 全排列数: f ( n ) = n ! f(n)=n! f(n)=n! 大佬写的算法.
讲正整数n表示成一系列正整数之和
p(n):正整数n的划分数 = q(n,n) q(n,m):最大加数不大于m的划分个数
q ( n , m ) = { 1 ; n = 1 , m = 1 q ( n , n ) ; n < m 1 + q ( n , n − 1 ) ; n = m q ( n , m − 1 ) + q ( n − m , m ) ; n > m > 1 q(n,m)=\begin{cases} 1&\text{;$n=1,m=1$}\\ q(n,n)&\text{;$n<m$}\\ 1+q(n,n-1)&\text{;$n=m$}\\ q(n,m-1)+q(n-m,m)&\text{;$n>m>1$}\\ \end{cases} q(n,m)=⎩⎪⎪⎪⎨⎪⎪⎪⎧1q(n,n)1+q(n,n−1)q(n,m−1)+q(n−m,m);n=1,m=1;n<m;n=m;n>m>1 其中, q ( n − m , m ) q(n-m,m) q(n−m,m)为最大加数为m的划分个数
int q(int n,int m){ if(n == 1 || m == 1) return 1; if(n < m) return q(n,n); if(n == m) return 1 + q(n,n - 1); if(n > m && m > 1) return q(n,m - 1) + q(n - m,m); }有a、b、c三个塔座,塔座a上自下而上、由大到小叠有n个圆盘,圆盘由小到大编号为1、2、···、n。现要将这n个圆盘从塔座a上转到塔座c上,并按同样顺序堆叠。
移动规则: (1)每次只能移动1个圆盘; (2)任何时刻都不允许将较大的圆盘压在较小的圆盘之上; (3)在满足移动规则(1)和(2)的前提下,可将圆盘移至塔座a、b、c中任一塔座上。
void move(int n, char a, char b) { printf("圆盘%d:%c->%c\n", n, a, b); } void hanoi(int n, char a, char b, char c) { if (n == 1) move(n, a, c); //只有一个圆盘,直接将它从塔座a移到塔座c else { hanoi(n - 1, a, c, b); //除最底下那个最大的圆盘,其他n-1个圆盘从塔座a经过塔座c中转到塔座b move(n, a, c); //圆盘n从塔座a移到塔座c hanoi(n - 1, b, a, c); //将塔座b上的n-1个圆盘移到塔座c } } int main() { hanoi(4, 'a', 'b', 'c'); return 0; }使得算法简洁且易于理解。
使得算法运行效率较低。
1、定义栈(本质还是递归); 2、用递推来实现递归函数; 3、通过Cooper、反演变换能将一些递归转化为尾递归,从而迭代求出结果。
分而治之 凡治众如治寡,分数是也。——孙子兵法 1° 将原始问题划分或者归结为规模较小的子问题; 2° 递归或迭代求解每个子问题; 3° 将子问题的解综合得到原问题的解。
问题:给定已排好序的n个元素a[0:n-1],现要在这n个元素中找出一特定元素x 二分搜索:将n个元素分成个数大致相同的两半,取a[n/2]与x进行比较 如果 x = a[n/2],则找到x,算法终止 如果 x < a[n/2],则只要在数组a的左半部继续搜索x 如果 x > a[n/2],则只要在数组a的右半部继续搜索x
int binarySearch(int a[], int x, int n) { //在 a[0] <= a[1] <= …… <= a[n-1]中搜索 x //找到x时返回其在数组中的位置,否则返回-1 int left = 0; int right = n - 1; while (left <= right) { int middle = (left + right) / 2; if (x == a[middle]) return middle; if (x > a[middle]) left = middle + 1; else right = middle - 1; } return -1; //未找到x }大佬写的算法.
大佬写的.
2 k × 2 k 2^k×2^k 2k×2k个方格组成的特殊棋盘中,恰有一个特殊方格与其他方格不同。 用4种不同形态的L型骨牌覆盖除特殊方格以外的所有方格 且任何2个L型骨牌不得重叠覆盖
#define MAXSIZE 8 static int tile = 1; //表示L型骨牌的编号 static int board[MAXSIZE][MAXSIZE]; //表示棋盘 //处理带有特殊棋子的棋盘 //tr、tc分别表示棋盘入口即左上角的行列号 //dr、dc分别表示特殊棋子的行列位置 //size表示棋盘的行数或列数 void chessBoard(int tr, int tc, int dr, int dc, int size) { if (size == 1) return; //一格,没啥好处理的 int t = tile++; //存放L型骨牌号 int s = size / 2; //分割棋盘 /*覆盖左上角子棋盘*/ if (dr < tr + s && dc < tc + s) chessBoard(tr, tc, dr, dc, s); //特殊方格在此棋盘中 else { //此棋盘中无特殊方格 //用t号L型骨牌覆盖右下角 //由于骨牌有四种,处理过程中同一级设置的特殊棋子用相同的骨牌覆盖 board[tr + s - 1][tc + s - 1] = t; //覆盖其余方格 chessBoard(tr, tc, tr + s - 1, tc + s - 1, s); //假设右下角是特殊方格 } /*覆盖右上角子棋盘*/ if (dr < tr + s && dc >= tc + s) chessBoard(tr, tc + s, dr, dc, s); //特殊方格在此棋盘中 else { //此棋盘中无特殊方格 //用t号L型骨牌覆盖左下角 board[tr + s - 1][tc + s] = t; //覆盖其余方格 chessBoard(tr, tc + s, tr + s - 1, tc + s, s); //假设左下角是特殊方格 } /*覆盖左下角子棋盘*/ if (dr >= tr + s && dc < tc + s) chessBoard(tr + s, tc, dr, dc, s); //特殊方格在此棋盘中 else { //用t号L型骨牌覆盖右上角 board[tr + s][tc + s - 1] = t; //覆盖其余方格 chessBoard(tr + s, tc, tr + s, tc + s - 1, s); //假设右上角是特殊方格 } /*覆盖右下角子棋盘*/ if (dr >= tr + s && dc >= tc + s) chessBoard(tr + s, tc + s, dr, dc, s); //特殊方格在此棋盘中 else { //用t号L型骨牌覆盖左上角 board[tr + s][tc + s] = t; //覆盖其余方格 chessBoard(tr + s, tc + s, tr + s, tc + s, s); //假设左上角是特殊方格 }【参考】王道 没图,硬讲。(因为学过了,所以就提一下思想,如果下一次复看的时候不会,再贴图)
快速排序是对冒泡排序的一种改进方法 冒泡排序:每次都能确定最后一个元素 快速排序:每次都能确定中间那个元素 快速排序步骤: 1° 先选取基准枢轴元素pivot,一般取待排序序列a的首个元素 2° i指向待排序序列首,j指向待排序序列末 3° j往前遍历,直到遇到比pivot小的元素,然后a[i]=a[j] 4° j往后遍历,直到遇到比pivot大的元素,然后a[j]=a[i] 5° pivot填到中间那个位置
void QuickSort(ElemType A[], int low, int high) { if (low < high) { //递归跳出的条件 int pivotpos = Partition(A, low, high); //划分 QuickSort(A, low, pivotpos - 1); //依次对两个子表进行递归排序 QuickSort(A, pivotpos + 1, high); } } int Partition(ElemType A[], int low, int high) { ElemType pivot = A[low]; //将当前表中第一个元素设为枢轴,对表进行划分 while (low < high) { //循环跳出条件 while (low < high && A[high] >= pivot) --high; A[low] = A[high]; //将比枢轴小的元素移动到左端 while (low < high && A[low] <= pivot) --low; A[high] = A[low]; //将比枢轴大的元素移动到右端 } A[low] = pivot; //枢轴元素存放到最终位置 return low; //返回存放枢轴的最终位置给定线性序列中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素 大佬写的算法.
大佬写的算法.
n个选手 (1)每个选手必须与其他n-1个选手各赛一次; (2)每个选手一天只能赛一次; (3)当n为偶数时,循环赛进行n-1天;当n为奇数时,循环赛进行n天。
static int a[SIZE][SIZE]; void match(int n) { /*选手从0开始计数*/ if (n == 2) { a[0][0] = 0; a[0][1] = 1; a[1][0] = 1; a[1][1] = 0; } else if (n % 2 == 0) match(n / 2); //偶数个选手 else { n += 1; //奇数个选手,增加一个虚拟选手,即为n号选手 match(n / 2); } Makecopy(n); //合并 } /*n为偶数*/ void Makecopy(int n) { if ((n / 2) % 2 == 0) copy(n); //n/2为偶数的情况 else copyodd(n); //n/2为奇数的情况 } /*n为偶数,n/2为奇数*/ void copy(int n) { int m = n / 2; for (int i = 0; i < m; i++) for (int j = 0; j < m; j++) { a[i + m][j] = a[i][j] + m; //第3部分 a[i][j + m] = a[i + m][j]; //第2部分 a[i + m][j + m] = a[i][j]; //第4部分 } } /*n为偶数,n/2为奇数*/ void copyodd(int n) { int m = n / 2; /*第3部分,即解决第i+1号选手到第n-1号选手的前m+1天的比赛*/ for (int i = 0; i < m; i++) for (int j = 0; j <= m; j++) { if (a[i][j] >= m) { //表示上半区i号选手轮空 //对应下半区i+m号选手也轮空 a[i][j] = m + i; a[m + i][j] = i; } else a[m + i][j] = a[i][j] + m; //相应位置各加m } /*第2、4部分,即解决所有选手后面天的比赛*/ for (int i = 0; i < m; i++) for (int j = 1; j < m; j++) { a[i][m + j] = (i + j) % m + m; //按此规律给出相应的对手号,解决第2部分 a[(i + j) % m + m][m + j] = i; //按此规律给出相应的对手号,解决第4部分 } }(1)问题的规模缩小到一定程序可以容易地解决; (2)问题具有最优子结构性质:问题可以分解为若干个规模较小地相同问题; (3)利用问题分解出地子问题的解可以合并为该问题的解; (4)问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
递归作为一种工具,在实现分治时经常被使用。