判断

  1. ( ✅ ) f(n) = O(g(n)),当且仅当 g(n) = Ω(f(n))。
  2. (  ❌   )若借助二分查找确定每个元素的插入位置,向量的插入排序只需 O(nlogn)时间。
  3. (  ❌   )无论有序向量或有序列表,最坏情况下均可在 O(logn)时间内完成一次查找。
  4. (  ✅   )对有序向量做 Fibonacci 查找,就最坏情况而言,成功查找所需的比较次数与失败查找相等。
  5. (  ❌   )只要是采用基于比较的排序算法,对任何输入序列都至少需要运行 Ω(nlogn) 时间。
  6. (  ✅   )对不含括号的中缀表达式求值时,操作符栈的容量可以固定为某一常数。
  7. (  ❌   )对于同一有序向量,每次折半查找绝不会慢于顺序查找。
  8. (  ✅   )RPN 中各操作数的相对次序,与原中缀表达式完全一致。

答案全部正确 6题所谓常数,其实就是 , , ^ 这三个不同优先级的运算符。

多项选择

  1. (  C, D   )若 f (n) = O (n^2) 且 g (n) = O (n),则以下结论正确的是: A. f(n) + g(n) = O(n^2) B. f(n) / g(n) = O(n) C. g(n) = O(f(n)) D. f(n) * g(n) = O(n^3)

❌ 正确答案是:A, D

  1. (  A, B   )算法 g (n)的复杂度为 Θ(n)。若算法 f (n)中有 5 条调用 g (n)的指令,则 f (n)的复杂度为: A. Θ(n)  B. O(n) C. Ω(n) D.不确定

❌ 正确答案:D。可以参考习题解析 1-7 ,构造恒假的分支语句屏蔽掉这五个 g

  1. (  C   )共有几种栈混洗方案,可使字符序列{‘x’, ‘o’, ‘o’, ‘o’, ‘x’}的输出保持原样? A. 12 B. 10 C. 6 D. 5

  1. (  C   )对长度为 Fib (12) ‐ 1 = 143 的有序向量做 Fibonacci 查找,比较操作的次数至多为: A. 12 B. 11 C. 10 D. 9

❌ 正确答案是:B 对该长度的向量进行 Fibonacci 查找,则第一步的划分后,得到的向量有三个区间,长度分别为:Fib (11)-1, 1 (即 mi), Fib (10)-1。 由于 Fibonacci 查找的特点——最坏情况时,失败查找和成功查找进行的比较次数相同,并且向右深入分支的代价是 2。因此考察原向量 (mi, hi)这一区间,即再次对 Fib (10)-1 长度的向量区间进行划分,得到 Fib (9)-1, 1 (new mi), Fib (8)-1 三个长度的新的向量划分。 以此类推,直到左子向量长度为 Fib (3)-1=1 时,其即是最多需要比较次数的元素。示意图如下:

  1. (  B, C   )对长度为 n = Fib (k) ‐ 1 的有序向量做 Fibonacci 查找。若各元素的数值等概率独立均匀分布,且平均成功查找长度为 L,则平均失败查找长度为: A. n(L‐1)/(n‐1) B. n(L+1)/(n+1) C. (n‐1)L/n D. (n+1)L/n

❌ B 习题解析 2-18:二分搜索的平均查找长度

复杂度分析

给出函数F(n)复杂度的紧界(假定int字长无限,移位属基本操作,且递归不会溢出)

void F(int n)           //O( loglogn ) ✅
{ for (int i=1,r=1; i<n;i<<r,r<<=1);}

void F(int n)           //O(  √n     ) ✅
{ for (int i=0, j=0; i<n;i+=j,j++;}

void F(int n)           //O(  log*n  ) ✅
{ for (int i=1; i<n;i=1<<i);}

void F(int n)           //O(  logn   ) ❌ O(n^0.694)
{ return (n<4)? n: F(n>>1) + F(n>>2); }

int F(int n)            //O(  n^2    ) ❌ O(2^n)
{ return (n == 0) ? 1 : G(2, F(n‐1)); }
int G(int n, int m)
{ return (m == 0) ? 0 : n + G(n,m‐1); }

int F(int n)            //O(  n^2    ) ✅
{ return G(G(n‐1)); }
int G(int n) { return (n==0) ? 0 : G(n‐1) + 2*n ‐ 1; }

void F(int n) {         //O(  nlogn  ) ✅
for (int i=1; i<n;i++)
for (int j=0; j<n;j+=i);
}

void F(int n) {     //expected‐O(  nlogn  ) ✅
	for (int i=n; 0<i; i--)
		if(0 == rand()%i)
			for(int j=0;j<n;j++);
}

简答题

  1. 考查如下问题:任给 12 个互异的整数,且其中 10 个已组织为一个有序序列,现需要插入剩余的两个以完成整体排序。若采用基于比较的算法(CBA),最坏情况下至少需做几次比较?为什么?

最坏需要做 11+12 次比较,这种情况是第 11、12个元素分别搜索整个序列后才找到自己的位置。

❌ 习题 2-39 前一整数可能的插入位置数 * 后一整数可能的插入位置数 = 11 * 12 = 132 这也是该判定树应含叶节点数目的下限。 于是对应地,判定树的高度应至少是: 这也是此类算法在最坏情况下需做比较操作次数的下限。

  1. 向量的插入排序由 n 次迭代完成,逐次插入各元素。为插入第 k 个元素,最坏情况需做 k 次移动,最好时则无需移动。从期望的角度来看,无需移动操作的迭代平均有多少次?为什么? 假定各元素是等概率独立均匀分布的。

无需移动就是插入在序列末尾,在插入元素的过程中,这一概率为 1,1/2,1/3,1/4…,因此整体期望为

  1. 现有长度为 15 的有序向量 A[0.. 14],各元素被成功查找的概率如下:
i01234567891011121314
1/1281/1281/321/81/81/321/161/161/1281/641/161/43/161/1281/64
若采用二分查找算法,试计算该结构的平均成功查找长度。

  1. 考查表达式求值算法。算法执行过程中的某时刻,若操作符栈中的括号多达 2010 个,则此时栈的规模(含栈底的’\0’)至多可能多大?试说明理由,并示意性地画出当时栈中的内容。

(2010+1)*4+1 = 8045 个字符,栈中内容是:\0+*^(+*^(+*^(...(+*^(+*^!,其中 可以用相同优先级的运算符 代替。

  1. 阅读以下程序,试给出其中 ListReport ()一句的输出结果(即当时序列 L 中各元素的数值)
#define LLIST_ELEM_TYPE_INT  //节点数据域为int型
LValueType  visit(LValueType e) {
	static int lemda = 1980;
	lemda += e*e;    
	return lemda;
}

int main(int argc, char* argv[]) {
	LList* L = ListInit(‐1);
	for (int i=0; i<5; i++)  
		ListInsertFirst(L, i);
	ListTraverse(L, visit);
	ListReport(L);   /*输出:                     */
	ListDestroy(L);
	return 0;
}

1996 2005 2009 2010 2010 ✅

算法题

  1. sortOddEven (L)
#define LLIST_TYPE_ARRAY  //基于向量实现序列
#define LLIST_ELEM_TYPE_INT  //节点数据域为 int 型
/************************************************************************
* 输入:基于向量实现的序列 L
* 功能:移动 L 中元素,使得所有奇数集中于前端,所有偶数都集中于后端
* 输出:无
* 实例:L = {2, 13, 7, 4, 6, 3, 7, 12, 9},则排序后
*      L = {9, 13, 7, 7, 3, 6, 4, 12, 2}
* 要求:O(n)时间,O(1)附加空间 ************************************************************************/

void  sortOddEven(LList* L) { } 

快排的快速划分思想

  1. shift (L, k)
#define LLIST_TYPE_ARRAY  //基于向量实现序列
#define LLIST_ELEM_TYPE_INT  //节点数据域为 int 型
/************************************************************************
* 输入:基于向量实现的序列 L
* 功能:将 L 中各元素循环左移 k 位
* 输出:无
* 实例:L = {1,..., k, k+1, ..., n},则左移后
*      L = {k+1, ..., n, 1, ..., k}
* 要求:O(n)时间(注意:最坏情况下 k=Ω(n)),O(1)附加空间 ************************************************************************/

void  shift(LList* L, int k) {
// Assert: L != NULL, 0 < k < Length(L)
}