部分题目
01-简介
Task 1
根据
power2(),设计一个算法,在 O(logn)时间内计算出fib(n)。其中,power2()方法是用来求幂函数,实现如下: cpp//power2 long long sqr ( long long a ) { return a * a; } //平方:若是连续执行,很快就会数值溢出! long long power2 ( int n ) { //幂函数2^n算法(优化递归版),n >= 0 if ( 0 == n ) return 1; //递归基;否则,视n的奇偶分别递归 return ( n & 1 ) ? sqr ( power2 ( n >> 1 ) ) << 1 : sqr ( power2 ( n >> 1 ) ); } //O(logn) = O(r),r为输入指数n的比特位数解:由于斐波那契数列可以使用矩阵乘法来求值:
则可以得到如下公式:
而矩阵乘法的时间复杂度为常数时间。则总的时间复杂度为
。
Task 2
对中华更相减损术算法的分析:
cpplong long gcdCN ( long long a, long long b ) { //assert: 0 < min(a, b) int r = 0; //a和b的2^r形式的公因子 while ( ! ( ( a & 1 ) || ( b & 1 ) ) ) { //若a和b都是偶数 a >>= 1; b >>= 1; r ++; //则同时除2(右移),并累加至r } //以下,a和b至多其一为偶 while ( 1 ) { while ( ! ( a & 1 ) ) a >>= 1; //若a偶(b奇),则剔除a的所有因子2 while ( ! ( b & 1 ) ) b >>= 1; //若b偶(a奇),则剔除b的所有因子2 ( a > b ) ? a = a - b : b = b - a; //简化为:gcd(max(a, b) - min(a, b), min(a, b)) if ( 0 == a ) return b << r; //简化至平凡情况:gcd(0, b) = b if ( 0 == b ) return a << r; //简化至平凡情况:gcd(a, 0) = a } }- 该算法的渐进时间复杂度
,与欧几里得算法相同。 - 考查该算法的每一步迭代,紧接于两个内部
while循环之后设置一个断点,观察此时的a和b。实际上,在a和b各自剔除了所有因子2之后,此时它们都将是奇数。接下来,无论二者大小如何,再经一次互减运算,它们必然将成为一奇一偶。比如,不失一般性,设,则得到: 偶数, 奇数;再经一步迭代并重新回到断点时,前者至多是: ,两个变量之和至多是 。可见,每经过一步迭代, 减少一半,故总体迭代步数不超过: 。 - 并且,相较于欧几里得算法而言,该算法涉及的运算更为简单,只有加减和移位操作,而没有欧几里得算法中的乘除操作。
- 该算法的渐进时间复杂度
Task 3
设计一个就地移位的算法,使得有
个元素的数组 arr[]进行平移操作,让第项 至数组的首位,让第 项至数组的末位。 cppint shift2 ( int* A, int n, int k ) { //倚劣倒置算法,将数组循环左秱k位,O(3n) k %= n; //确保k <= n reverse ( A, k ); //将匙间A[0, k)倒置:O(3k/2)次操作 reverse ( A + k, n - k ); //将匙间A[k, n)倒置:O(3(n - k)/2)次操作 reverse ( A, n ); //倒置整个数组A[0, n):O(3n/2)次操作 return 3 * n; //迒回累计操作次数,以便不其它算法比较:3/2 * (k + (n - k) + n) = 3n }//逻辑十分巧妙原理:若在原向量
中前 个元素组成的前缀为 ,剩余的(后缀)部分为 ,经整体左移之后的向量应为 ;这里约定,任意向量 整体倒置后的结果记作 。于是该算法的原理来自如下恒等式:
Task 4
对于某些算法的时间复杂度分析:
Subtask 1:
cppvoid F(int n) { for (int i = 0; i < n; i ++) for (int j = 1; j < n; j <<= 1); }复杂度:
Subtask 2:
cppvoid F(int n){ for(int i=1,r=1; i<n; i<<=r,r<<=1); }复杂度:
。所以,有 , . Subtask 3:
cppvoid F(int n){ for(int i=1;i<n;i=1<<i) }即每经一次迭代,
即增长至 。设经过 次迭代之后,因 而退出迭代。现颠倒原迭代的方向,其过程应等效于反复令 ,并经k次迭代之后有 。由此可知,若对 反复取对数直至其不大于1,则 等于其间所做对数运算的次数,记作 , 读作“log-星-n”。 所以时间复杂度为
. 和 均可视作常数。特别地,对于 ,有 。 Subtask 4:
cppint sum( int A[], int n ) { return n < 1 ? 0 : sum(A, n - 1) + A[n - 1]; }时间复杂度:
由于单个递归实例需要
时间完成,共有 个实例,所以整个算法的复杂度是 。当 sum(A,n)调用sum(A,n-1)时,sum(A,n)函数中的数据以“函数帧”的形式被压入一个栈中,并没有处于执行状态。
Task 5
下列函数渐进增长的大小排序:
因为,对数函数
02-向量
Task 1
12-图基础
Task 1
在一个含有
因为每个顶点都可以与其他
个顶点连接一条边,但这样会计算两次每条边(因为无向图的边是双向的),所以需要除以 。此时,每个点的度数相同,都是 。
Task 2
在人类的历史长河中,每个人都可能要与其他人握手。如果某人在他的一生中进行握手的次数为奇数,则称他为A类人,否则称为B类人。从古至今A类人的个数是偶数。如何用图的相关理论解释?
无向图的边数等于各顶点度数之和的一半。
Task 3
若i个元素为顶点的度的对角矩阵,则它们的关系是:
证明:(ChatGPT)
要证明对于简单无向图
,其邻接矩阵 、关联矩阵 和度矩阵 满足关系 ,我们可以按以下步骤进行。 步骤 1:定义和性质
- 邻接矩阵
:
- 如果
有 个顶点和 条边, 是一个 的矩阵。 表示顶点 和顶点 之间是否存在边。 是对称矩阵,且对角线上全为0。
- 关联矩阵
:
是一个 的矩阵,表示顶点和边的关系。 - 如果边
连接顶点 和 ,则 和 都为1(假设无向边)。
- 度矩阵
:
是一个 的对角矩阵。 表示顶点 的度数,即与顶点 相连的边的数量。 步骤 2:计算
的每一列代表一条边,两行之间的相应位置表示这两个顶点是否共享一条边。 的结果将是一个 的矩阵,其中每个元素 表示顶点 和顶点 共享的边的数目。 更具体地说:
- 对于
, 表示顶点 和顶点 之间是否有边,即 。 - 对于
, 表示顶点 的度数,即 。 步骤 3:证明关系
对于
: 这是因为
表示顶点 和顶点 之间是否有边。 对于
: 这是因为
是顶点 的度数,即与顶点 相连的边的数量,等于度矩阵 中对应的对角元素。 因此,我们得到:
其中
, 对于 。于是: 这完成了证明。