【算法复习四】计算复杂性与算法分析---算法分析

一,生成函数与递推

递推关系举例

1Hanoi问题:这是个组合数学中的著名问题。N个圆盘依其半径大小,从下而上套在A柱上,如下图示。每次只允许取一个移到柱BC上,而且不允许大盘放在小盘上方。若要求把柱A上的n个盘移到C柱上请设计一种方法来,并估计要移动几个盘次。现在只有A、B、C三根柱子可用。

| | |

| | |

A B C


第一步把A中N-1个移动到B(借助C)

第二步把A中最下一个移动到C

第三步把B中移动到C(借助A)


算法复杂度:

h(n)表示n个盘子所需要转移次数。

h(n)=2h(n-1)+1

h(1)=1


递归算法:

#include using namespace std; int num=0; void Move(int n,char x,char y) { num++; cout<<"把"<

2】排错问题.n个有序的元素应有个不同的排列,如若一个排列使得所有的元素都不在原来的位置上,则称这个排列为错排;有的叫重排。

以1,2,3,4四个数的错排为例,分析其结构,找出规律性的东西来。

1)1 2的错排是唯一的,即2 1

2)1 2 3的错排有3 1 2,2 3 1。这二者可以看作是1 2错排,3分别与1,2换位而得的。

2 1 323换位3 1 2

2 1 3 13换位2 3 1

3)1,2,3,4的错排

4 3 2 1,4 1 2 3,4 3 1 2,

3 4 1 2,3 4 2 1,2 4 1 3,

2 1 4 3,3 1 4 2,2 3 4 1。

第一列是4分别与1,2,3互换位置,其余两个元素错排,由此生成的。

第二列是4分别与3,1,2(123的一个错排)的每一个数互换而得到的

分析:

设n个数1,2,…,n错排的数目为Dn,任取其中一数 i,数i分别与其他的n-1个数之一互换,其余n-2个数进行错排,共得 (n-1)Dn-2个错排。另一部分位数i以外的n-1个数进行错排,然后 i 与其中每个数互换得(n-1)Dn-1个错排。

Dn=(n-1)(Dn-1 + Dn-2) D1=0 D2=1

Dn - nDn-1=(-1) `n


二,递归算法的结构

递归结构定义

在进行递归算法的设计时,通常先写出问题的递归定义,递归定义由基本项归纳项两部分组成。

基本项,也就是递归出口。它描述了一个或几个递归过程的终止状态。所谓终止状态指的是不需要继续递归而直接求解的状态。

归纳项,也称为递归过程。它描述了如何实现从当前状态到终止状态的变化。

#include using namespace std; int multiplication(int n) { if(n==1) return 1; return n*multiplication(n-1); } int main() { cout<<"10的阶乘为:"<

基于递归的插入排序算法

#include using namespace std; void in_rec (int A[],int n)// 基于递归的插入排序算法 { int a,k; if(n<1) return ; in_rec (A,n-1);//归纳项,否则,对前面的n-1个元素排序 a = A[n]; // 把第n元素插入合适位置 k = n -1; while ((k>=0)&&(A[k]>a)) { A[k+1] = A[k]; k = k - 1; } A[k+1] = a; } int main() { int a[5]={3,2,1,4,5}; in_rec (a,5); for(int i=0;i<5;++i) cout<

三,组合算法分析

1)全排列算法

模型对应

序数法

字典序法
中介数法

换位法

例题:1 2 3 4 5 6 7 8 9 字典序全排列,求8 3 9 6 4 7 5 2 1的下一个排列

分析:一个全排列可看做一个字符串,字符串可有前缀、后缀。所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

例如,839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的是987654321,从右向左扫描若都是增的,就到987654321,也就没有下一个了。否则找出第一次出现下降的位置

解答: 1、搜索末端的最长递降序列。
2、记紧挨着该序列左边的数为 a。
3、在该序列中从右到左寻找首个大于 a 的数记为 b。
4、交换 a、b,反转原序列被 b 换入后的新序列。

(输出全排列)算法:

#include int n = 0; void swap(int *a, int *b) { int m; m = *a; *a = *b; *b = m; } void perm(int list[], int k, int m) { int i; if(k > m) { for(i = 0; i <= m; i++) printf("%d ", list[i]); printf("\n"); n++; } else { for(i = k; i <= m; i++) { swap(&list[k], &list[i]); perm(list, k + 1, m); swap(&list[k], &list[i]); } } } int main() { int list[] = {1, 2, 3, 4, 5}; perm(list, 0, 4); printf("total:%d\n", n); return 0; }
(输出下一个)算法:#include using namespace std; void swap(int* x,int* y) { int temp; temp=*x; *x=*y; *y=temp; } void reverse(int *first,int *last) { --last; for(;first

2)中介数

在[1,n]的全排列中,nn-1 321是最后一个排列,其中介数(n-1,n-2,...,3,2,1)。而其序号为 1*1!+2*2!+……(n-1)!

计算给定排列的序号

8 3 9 6 4 7 5 2 1的序号即先于此排列的排列的个数。将先于此排列的排列按前缀分类

将8!,7!,…,1!前面的系数抽出,放在一起得到7 2 6 4 2 3 2 1。

7 2 6 4 2 3 2 1是计算排列8 3 9 6 4 7 5 2 1的序号的中间环节,我们称之为中介数

这是一个很有用的概念。

12】由中介数推出排列的算法,例如由中介数7 2 6 4 2 3 2 1(8个数)推算出全排列:8 3 9 6 4 7 5 2 1

方法一: 推导

p1 p2 p3 p4 p5 p6 p7 p8 p9

8 2 1

8 3 2 1

8 3 4 2 1

8 3 9 4 2 1

…… …… ……

其他详细方法参见博文 http://blog.csdn.net/tianshuai11/article/details/7520370


你可能感兴趣的:(【算法复习四】计算复杂性与算法分析---算法分析)