740 likes | 896 Views
第五章 递归. 栈与递归 递归与回溯 广义表. 一、栈和递归. 递归定义 递归函数. 递归定义. 先定义最基本的概念 再用已定义的概念定义新的概念. 例 命题演算公式的定义 ( 1 ) 单个命题变元符号是命题 ( 2 ) 如果 A,B 是命题,则 (┐A), (A∧B), (A∨B), (A→B), ( A←→B) 都是公式. 递归定义. 先定义最基本的概念 再用已定义的概念定义新的概念. 例 标识符的定义
E N D
第五章 递归 • 栈与递归 • 递归与回溯 • 广义表
一、栈和递归 • 递归定义 • 递归函数
递归定义 • 先定义最基本的概念 再用已定义的概念定义新的概念 例 命题演算公式的定义 (1) 单个命题变元符号是命题 (2) 如果A,B是命题,则 (┐A), (A∧B), (A∨B), (A→B), (A←→B) 都是公式
递归定义 • 先定义最基本的概念 再用已定义的概念定义新的概念 例 标识符的定义 (1)单个英文字母是标识符 (2)标识符后缀一个数字 或一个英文字母是标识符
递归函数的定义 • 一个算法可以分解成 若干相同的小算法 分解到某简单的子算法时终止 • 有一个或几个终止条件 递归:由其前面的值求当前值 递归必须导致终止条件
递归函数的例 例 函数 xn x0=1 xn=x*xn-1n>0 xn=xn-1/x n<0 例 函数 n! 0!=1 n!=n*(n-1)!n>0
递归函数的例 函数 C(n, m) C(0, m)=1, C(m, m)=1. C(n, m)=C(n-1,m-1)+C(n, m-1)
#include <iostream.h> // compute n! = n*(n-1)*(n-2)...(2)(1), 0!=1 recursively long Factorial(long n) { // if n == 0, then 0! = 1; otherwise, n! = n*(n-1)! if (n == 0) return 1; else return n * Factorial(n - 1); }
void main (void) { int i, n; // enter 4 positive integers and compute n! for each cout << "Enter 4 positive integers: "; for (i = 0; i < 4; i++) { cin >> n; cout << n << "! = " << Factorial(n) << endl; } }
/* <Run > Enter 4 positive integers: 0 7 1 4 0! = 1 7! = 5040 1! = 1 4! = 24 */
递归函数 先操作 后遍历 例 void Fucnc(char ch) { if(ch<=‘z’) { cout<<ch; Func(ch+1); } } 调用 Func(‘a’); 输出 abcdefghijklmnopqrstuvwxyz
递归函数 先遍历 后操作 例 void Fucnc(char ch) { if(ch<=‘z’) { Func(ch+1); cout<<ch;} } 调用 Func(‘a’); 输出 zyxwvutsrqponmlkjihgfedcba
递归函数 操作 遍历 操作 例 void Fucnc(char ch) { if(ch<=‘z’) { cout<<ch; Func(ch+1); cout<<ch;} } 调用 Func(‘a’); 输出 abcdefghijklmnopqrstuvwxyz zyxwvutsrqponmlkjihgfedcba
递归函数 遍历 操作 遍历 例 void Fucnc(char ch) { if(ch<=‘d’) { Func(ch+1); cout<<ch; Func(ch+1); } } 调用 Func(‘a’); 输出 dcdbdcdadcdbdcd
河内塔问题将A塔上的金盘移到B塔上 !要求 一次只能移动一个盘 大盘不能压小盘
河内塔问题 设n个金盘移动 F(n)次 F(1)=1 F(n)=F(n-1)+1+F(n-1)=2*F(n-1)+1 F(n)+1=2*(F(n-1)+1) =22*(F(n-2)+1) =······ =2n-1*(F(1)+1)=2n F(n)=2n-1
河内塔问题程序 #include <iostream.h> #pragma hdrstop #include "strclass.h" // move n disks from startpeg to endpeg, // using middlepeg as the intermediate peg
void hanoi(int n, char A, char B, char C) { if (n == 1) cout << “move ”<<A << " → " < B << endl; else { hanoi(n-1,A, C, B); cout << “move ”<<A << " → " << B << endl; hanoi(n-1, C, B, A); } }
void main( ) { int n; // number of disks and the peg names cout << "Enter the number of disks: "; cin >> n; cout << "The solution for n = " << n << endl; hanoi(n, ‘A’, ‘B’, ‘C’); }
/* <Run 河内塔问题> Enter the number of disks: 3 The solution for n = 3 move A → B move A → C move B → C move A → B move C → A move C → B move B → C */
迷宫maze struct Intersection {int left; int forword; int right; }; 回溯 此路不通,返回 回溯 此路不通,返回 回溯 此路不通,返回 回溯 此路不通,返回 6 0 2 0 3 5 6 0 0 4 0 0 0 0 0 0 7 0 0 7
二、递归和回溯 • 迷宫算法 • 八皇后问题
// record that specifies the intersection you // arrive at when departing left, forward or right // from the current intersection struct Intersection { int left; int forward; int right; };
#include <iostream.h> #include <fstream.h> #include <stdlib.h> class Maze { int mazesize; int EXIT; Intersection *intsec; public: Maze(char *filename); int TraverseMaze(int intsecvalue); };
Maze::Maze(char *filename) { ifstream fin; int i; fin.open(filename, ios::in | ios::nocreate); if (!fin) { cerr << "The maze data file " << filename << " cannot be opened!" << endl; exit(1); } fin >> mazesize; intsec = new Intersection[mazesize+1]; for (i = 1; i <= mazesize; i++) fin >> intsec[i].left >> intsec[i].forward >> intsec[i].right; fin >> EXIT; fin.close( ); }
回溯法 一个变量控制递归 用另一个变量来控制回溯 出现特定情况时该变量取值0 回溯 1。全局变量 2。变量参数 引用变量,指针变量 3。函数返回值
int Maze::TraverseMaze(int intsecvalue) { if (intsecvalue > 0) {if (intsecvalue = = EXIT) { cout << intsecvalue << " "; return 1; } else if (TraverseMaze(intsec[intsecvalue].left)) { cout << intsecvalue << " "; return 1; } else if (TraverseMaze(intsec[intsecvalue].forward)) {cout << intsecvalue << " "; return 1; } else if (TraverseMaze(intsec[intsecvalue].right)) { cout << intsecvalue << " "; return 1; } } return 0; }
#include <iostream.h> #pragma hdrstop #include "maze.h" // include the maze class
void main (void) { char filename[32]; cout << "Enter the data file name: "; cin >> filename; Maze M(filename); if (M.TraverseMaze(1)) cout << "\nYou are free!" << endl; else cout << "No path out of the maze" << endl; }
//maze1.dat 6 0 2 0 3 5 6 0 0 4 0 0 0 0 0 0 7 0 0 7
//maze2.dat 11 0 2 0 3 0 5 0 0 4 0 0 0 6 7 0 0 0 0 8 0 0 9 0 0 0 11 10 0 0 0 0 0 0 12
//bigmaze.dat 18 0 2 0 3 8 0 7 4 0 0 6 5 0 0 0 0 0 0 0 0 0 9 0 0 0 0 10 14 0 11 12 13 0 0 0 0 0 0 0 0 15 16 0 0 0 17 0 0 0 18 19 0 0 0 19
/* <Run #1 > Enter the data file name: maze1.dat 7 6 2 1 You are free! <Run #2 > Enter the data file name: maze2.dat No path out of the maze <Run #3 > Enter the data file name: bigmaze.dat 19 17 16 14 10 9 8 2 1 You are free. */
八皇后问题 要求:一个棋盘上摆八个皇后,任意两个都不互相杀死 两皇后在同一行或同一列 或同一对角线上互相杀死
皇后的表示 • 用二维数组表示8*8棋盘 皇后用棋盘上的格点表示 • 用坐标表示皇后的位置 (a, 4) (1, 4) • 用一维数组表示皇后的位置 int q[9]; q[1]=4; 表示第一行第四列有一个皇后 q[4]=2; 表示第四行第二列有一个皇后
两个皇后冲突的特征 (a1, b1)与 (a2, b2)冲突 当且仅当 a1= b1或a2= b2或 | a1- b1|=| a2- b2| q[i]与q[j]冲突 当且仅当 i= j或 q[i]=q[j]或 | i - j |=| q[i] - q[j] |
三、广义表 LS=(a1, a2,······,an) 长度 n 每个 ai1≤i≤n 或是一个元素(原子), 或是一个子广义表。 a1是表头head, a2,······,an是表尾。 用小写字母表示原子,大写字母表示广义表。
广义表的例 A=( ) 长度为0的空表。 B=(e) 只有一个元素的表,长为1。 C=(a,(b,c,d)) 长度为2的广义表,第二个 元素是长度为3的子表。 D=(A,B,C) 长度为3的广义表,三个 元素都是长度为3的子表。 D=(( ),(e),(a,(b,c,d))) E=(a,E) 递归定义的表。 E=(a,(a,(a,······))).
广义表的存储 广义表的结点