一:绪论
表示时间复杂度的阶有:
O(1) :常量时间阶
O (n):线性时间阶
O(㏒n) :对数时间阶
O(n㏒n) :线性对数时间阶
O (nk): k≥2 ,k次方时间阶
以下六种计算算法时间的多项式是最常用的。其关系为:
O(1)<O(㏒n)<O(n)<O(n㏒n)<O(n2)<O(n3)
指数时间的关系为:
O(2n)<O(n!)<O(nn)
算法的空间复杂度定义为:S(n) = O(g(n))
表示随着问题规模 n 的增大,算法运行所需存储量S(n)的增长率与 g(n) 的增长率相同,称S(n)(渐近)空间复杂度。
二:线性表
顺序线性表:
设在线性表L中的第i个元素之前插入结点的概率为Pi,不失一般性,设各个位置插入是等概率,则Pi=1/(n+1),而插入时移动结点的次数为n-i+1。
总的平均移动次数: Einsert=∑pi*(n-i+1) (1≦i≦n)
∴ Einsert=n/2 。
链式线性表:
单链表:
例2.1假设利用两个线性表LA和LB分别表示两个集合A和B,现要求一个新的集合A=A∪B。
算法思想:1、扩大La,将存在于Lb中而不存在于La中的数据元素插入到La中去。2、需要从Lb中依次取得每个数据元素,并依值在La中进行查访,若不存在,则插 之。
例2.3 已知线性表LA和LB中的数据元素是按值非递减有序排列,现要求将LA和LB归并为一个新的线性表LC,且LC中的数据元素也是按值非递减有序排列。
1.初始化 LC 为空表;
2.分别从 LA和LB中取得当前元素 ai 和 bj;
3.若 ai≤bj,则将 ai 插入到 LC 中,否则将bj 插入到 LC 中;
4.重复 2 和 3 两步,直至 LA 或 LB 中元素被取完为止;
5.将 LA 表或 LB 表中剩余元素复制插入到LC 表中。
(双向)循环链表:
约瑟夫问题:
n 个人围成一个圆圈,首先第1个人从1开始一个人一个人顺时针报数, 报到第m个人,令其出列。然后再从下一个人开始,从1顺时针报数,报到第m个人,再令其 出列,…,如此下去, 直到圆圈中只剩一个人为止。此人即为优胜者。
例如 n = 8 m = 3
三:栈和队列
括号匹配的检验:(栈)
假设在表达式中允许包含两种括号:圆括号和方括号,其嵌套的顺序随意,即:
([]())或[([ ][ ])]等为正确的格式,
[( ])或([( ))或 (()])均为不正确的格式。
则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。
算法设计:
1) 凡出现左括弧,则进栈;
2) 凡出现右括弧,首先检查栈是否空
若栈空,则表明该“右括弧”多余,
否则和栈顶元素比较,
若相匹配,则“左括弧出栈” ,
否则表明不匹配。
3)表达式检验结束时,
若栈空,则表明表达式中匹配正确,
否则表明“左括弧”有余。
表达式求值:(栈)
迷宫求解:(栈)
求迷宫路径算法的基本思想是:从入口出发,按某一方向向未走过的前方探索
若当前位置“可通”,则纳入路径,继续前进
若当前位置“不可通”,则后退,换方向继续探索;
若四周“均无通路”,则将当前位置从路径中删除出去。
算法:
设定当前位置的初值为入口位置;
do{
若当前位置可通,
则{将当前位置插入栈顶;
若该位置是出口位置,则算法结束;
否则切换当前位置的东邻方块为
新的当前位置;
}
否则 {
}
}while (栈不空);
若栈不空且栈顶位置尚有其他方向未被探索,
则设定新的当前位置为: 沿顺时针方向旋转
找到的栈顶位置的下一相邻块;
若栈不空但栈顶位置的四周均不可通,
则{删去栈顶位置;// 从路径中删去该通道块
若栈不空,则重新测试新的栈顶位置,
直至找到一个可通的相邻块或出栈至栈空;
}
若栈空,则表明迷宫没有通路。
栈的另外一个重要的应用:递归调用
用分治法求解递归问题:
分治法:对于一个较为复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解
三个条件:
1、能将一个问题转变成一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些处理对象是变化有规律的
2、可以通过上述转化而使问题简化
3、必须有一个明确的递归出口,或称递归的边界
分治法求解递归问题算法的一般形式:
void p (参数表) {
if (递归结束条件)可直接求解步骤;-----基本项
else p(较小的参数);------归纳项
}
long Fact ( long n ) {
if ( n == 0) return 1; //基本项
else return n * Fact (n-1); //归纳项}