下面 C++ 代码用于求斐波那契数列,该数列第 、 项为 ,以后各项均是前两项之和。下面有关说法错误的是( )。
01int fiboA(int N) 02{ 03 if (N == 1 || N == 2) 04 return 1; 05 return fiboA(N - 1) + fiboA(N - 2); 06} 07int fiboB(int N) 08{ 09 if (N == 1 || N == 2) 10 return 1; 11 int last2 = 1, last1 = 1; 12 int nowVal = 0; 13 for (int i = 2; i < N; i++) 14 { 15 nowVal = last1 + last2; 16 last2 = last1; 17 last1 = nowVal; 18 } 19 return nowVal; 20}
C:fiboA 递归中 fiboA(N-1) 与 fiboA(N-2) 的子问题大量重复计算,规模呈指数增长,效率低;fiboB 用 last1、last2 循环递推为 O(N),代码量少不代表执行效率高。
下面 C++ 代码以递归方式实现合并排序,并假设 merge(int T[], int R[], int s, int m, int t) 函数将有序(同样排序规则)的 T[s..m] 和 T[m+1..t] 归并到 R[s..t] 中。横线处应填上代码是( )。
01void mergeSort(int SList[], int TList[], int s, int t, int len) 02{ 03 if (s == t) { 04 TList[s] = SList[s]; 05 return; 06 } 07 int *T2 = new int[len]; // 保存中间结果 08 int m = (s + t) / 2; 09 ____________; 10 merge(T2, SList, s, m, t); 11 delete T2; 12 return ; 13}
C:m=(s+t)/2 把区间分为 [s..m] 和 [m+1..t],先递归把两段排好序写入 T2,再执行 merge(T2, SList, s, m, t) 归并回 SList,右段起点应为 m+1。
阅读下面的 C++ 代码,执行后其输出是( )。
01int stepCount = 0; 02int fracA(int N) 03{ 04 stepCount += 1; 05 cout << stepCount << "->"; 06 int rtn = 1; 07 for (int i = 1; i <= N; i++) 08 rtn *= i; 09 return rtn; 10} 11int fracB(int N) 12{ 13 stepCount += 1; 14 cout << stepCount << "->"; 15 if (N == 1) 16 return 1; 17 return N * fracB(N - 1); 18} 19int main() 20{ 21 cout << fracA(5); 22 cout << "<====>"; 23 cout << fracB(5); 24 return 0; 25}
D:stepCount 是全局变量,fracA(5) 先输出 1-> 并返回 5!=120;fracB 从 stepCount=2 开始计数,递归 5 层依次输出 2->、3->、4->、5->、6->,回溯相乘得 120。
下面的 C++ 用于对 lstA 排序,使得偶数在前奇数在后,横线处应填入( )。
01bool isEven(int N) 02{ 03 return N % 2 == 0; 04} 05 06void swap(int &a, int &b) 07{ 08 int t; 09 t=a,a=b,b=t; 10 return; 11} 12 13void sortA(int lstA[], int n) 14{ 15 int i, j, t; 16 for (i = n-1; i > 0; i--) 17 for(j = 0; j < i; j++) 18 if(____________) 19 swap(lstA[j], lstA[j+1]); 20 21 return; 22}
A:冒泡排序相邻比较时,若左侧 lstA[j] 是奇数而右侧 lstA[j+1] 是偶数,即 !isEven(lstA[j]) && isEven(lstA[j+1]),就交换,使偶数不断前移、奇数后移。
下面的 C++ 代码用于将字符串保存到带头节点的双向链表中,并对重复的串计数,然后将最新访问的串的节点放在链头便于查找。横线处应填入代码是( )。
01typedef struct Node{ 02 string str; 03 int ref; 04 struct Node *next, *prev; 05}Node; 06Node * Insert(Node *pHead, string s) 07{ 08 Node *p = pHead->next; 09 Node *q; 10 while(p) { 11 if(p->str == s) { 12 p->ref++; 13 p->next->prev = p->prev; 14 p->prev->next = p->next; 15 break; 16 } 17 p=p->next; 18 } 19 if(!p) { 20 p = new Node; 21 p->str = s; 22 p->ref=0; 23 p->next = p->prev = NULL; 24 } 25 ____________ 26 pHead->next = p, p->prev = pHead; 27 return pHead; 28}
B:把 p 插入链头前须判断 pHead->next 是否为空;非空时先令 p->next=pHead->next、pHead->next->prev=p,再执行 pHead->next=p、p->prev=pHead,空链表时跳过以免解引用空指针。
有关下面 C++ 代码说法正确的是( )。
01int rc; 02int foo(int x, int y) 03{ 04 int r; 05 if(y == 0) 06 r = x; 07 else { 08 r = foo(y, x % y); 09 rc++; 10 } 11 return r; 12}
A:foo 是欧几里得算法求最大公约数,每层递归调用 foo(y, x%y),y 严格变小最终为 0 终止;x<10 时递归步数很少,全局变量 rc 远小于 20,也不会无限递归,C、D 错在它求的是 gcd 而非质因子或最小公倍数。
下面的 C++ 代码实现对 list 的快速排序,有关说法,错误的是( )。
01vector<int> operator + (vector<int>lA, vector<int>lB) 02{ 03 vector<int>lst; 04 05 for (int i = 1; i < lA.size(); i++) 06 lst.push_back(lA[i]); 07 for (int i = 1; i < lB.size(); i++) 08 lst.push_back(lB[i]); 09 10 return lst; 11} 12vector<int> qSort(vector<int>lst) 13{ 14 if (lst.size() < 2) 15 return lst; 16 int pivot = lst[0]; 17 vector<int> less, greater; 18 for (int i = 1; i < lst.size(); i++) 19 if (lst[i] <= pivot) less.push_back(lst[i]); 20 else greater.push_back(lst[i]); 21 for (int i = 1; i < lst.size(); i++) 22 if (lst[i] <= pivot) less.push_back(lst[i]); 23 else greater.push_back(lst[i]); 24 25 return ____________; 26}
C:快排应返回 qSort(less)+(vector<int>)pivot+qSort(greater),pivot 夹在两组已排序元素之间,且需转成向量才能用重载的 + 拼接;A、B 把 pivot 放两端会打乱顺序,D 的 int 不能直接相加。
下面 C++ 代码中的 isPrimeA() 和 isPrimeB() 都用于判断参数 N 是否素数,有关其时间复杂度的正确说法是( )。
01bool isPrimeA(int N) 02{ 03 if (N < 2) 04 return false; 05 for (int i = 2; i <= N / 2; i++) 06 if (N % i == 0) 07 return false; 08 return true; 09} 10bool isPrimeB(int N) 11{ 12 if (N < 2) 13 return false; 14 for (int i = 2; i <= sqrt(N); i++) 15 if (N % i == 0) 16 return false; 17 return true; 18}
B:isPrimeA 试除到 N/2,最坏为 O(N/2) 即 O(N);isPrimeB 只试除到 sqrt(N),最坏 O(√N),绝大多数情况下循环次数更少、更优。
下面 C++ 代码用于有序 list 的二分查找,有关说法错误的是( )。
01int _binarySearch(vector<int>lst, int Low, int High, int Target) 02{ 03 if (Low > High) 04 return -1; 05 int Mid = (Low + High) / 2; 06 if (Target == lst[Mid]) 07 return Mid; 08 else if (Target < lst[Mid]) 09 return _binarySearch(lst, Low, Mid - 1, Target); 10 else 11 return _binarySearch(lst, Mid + 1, High, Target); 12} 13int bSearch(vector<int>lst, int Val) 14{ 15 return _binarySearch(lst, 0, lst.size(), Val); 16}
D:该代码每轮用 Mid=(Low+High)/2 比较后把搜索区间减半递归,属二分、分治与递归,但没有重叠子问题、没有状态转移,不是动态规划。
在二分查找算法中,如果有序列表中有 N 个元素,其时间复杂度是( )。
B:二分查找每轮取 Mid 比较后搜索区间缩小一半,最坏需 log2N 轮,所以含 N 个元素的有序列表查找时间复杂度为 O(log N)。
下面的 C++ 代码使用数组模拟整数加法,可以处理超出大整数范围的加法运算。横线处应填入代码是( )。
01vector<int> operator + (vector<int> a, vector<int> b) 02{ 03 vector<int> c; 04 int t = 0; 05 06 for(int i = 0; i < a.size() || i < b.size(); i ++ ) 07 { 08 if(i < a.size()) t = t + a[i]; 09 if(i < b.size()) t = t + b[i]; 10 ____________ 11 } 12 13 if(t) c.push_back(t); 14 15 return c; 16}
D:t 累计 a[i]、b[i] 与进位,先 c.push_back(t%10) 保存当前位数字,再 t=t/10 留下进位;循环结束后若 t 非零,再 push 最高位的进位。
有关下面 C++ 代码的说法正确的是( )。
01class Node 02{ 03public: 04 int Value; 05 Node* Prev; 06 Node* Next; 07 Node(int Val, Node* Prv = NULL, Node* Nxt = NULL); 08}; 09 10Node::Node(int Val, Node*Prv, Node* Nxt) 11{ 12 this->Value = Val; 13 this->Prev = Prv; 14 this->Next = Nxt; 15} 16 17int main() 18{ 19 Node firstNode = Node(10); 20 firstNode.Next = new Node(100, &firstNode); 21 firstNode.Next->Next = new Node(111, firstNode.Next); 22}
B:firstNode 的 Prev、Next 均为 NULL,第二个节点 Prev 指向 firstNode,第三个节点 Prev 指向第二个节点,三个节点通过 Next 依次相连、两端为 NULL,构成三节点双向链表而非循环链表。
通讯卫星在通信网络系统中主要起到( )的作用。
B:通讯卫星接收地面站发来的信号并放大后转发回地面,实现超视距远距离通信,起信号中继作用,而非信息过滤、数据加密或避免攻击。
小杨想编写一个判断任意输入的整数 N 是否为素数的程序,下面哪个方法不合适?( )
C:判断单个 N 是否素数可用枚举试除、埃氏筛或线性筛;二分答案需要答案区间单调且能 check,素数判定不具备这种结构,方法不合适。
下面的排序算法都要处理多趟数据,哪种排序算法不能保证在下一趟处理时从待处理数据中选出最大或最小的数据?( )
B:快速排序一趟 partition 只把基准 pivot 放到最终位置,其余元素仍无序,不能保证选出全局最大或最小;选择、堆、冒泡每趟都能确定一个极值。
归并排序的时间复杂度是 。
对。归并排序把区间不断二分,递归深度约 log2N 层,每层合并所有元素的总代价为 O(N),故总时间复杂度为 O(N log N)。
小杨在生日聚会时拿一块 H*W 的巧克力招待来的 K 个小朋友,保证每位小朋友至少能获得一块相同大小的巧克力。那么小杨想分出来最大边长的巧克力可以使用二分法。
错。二分法在考纲中分为二分查找与二分答案两类,本题求巧克力最大边长需对边长二分并验证能否切出至少 K 块,属于二分答案,笼统说可用二分法不准确。
以下 C++ 代码能以递归方式实现斐波那契数列,该数列第 、 项为 ,以后各项均是前两项之和。
01int Fibo(int N) 02{ 03 if (N == 1 || N == 2) 04 return 1; 05 else 06 { 07 int m = fiboA(N - 1); 08 int n = fiboB(N - 2); 09 return m + n; 10 } 11}
错。Fibo 的 else 分支调用的是 fiboA(N-1) 和 fiboB(N-2),这两个函数并未定义,代码无法编译;递归实现斐波那契应调用自身 Fibo(N-1)+Fibo(N-2)。
贪心算法可以达到局部最优,但可能不是全局最优解。
对。贪心算法每一步只取当前状态下的局部最优选择且不回头调整,因此可能无法得到全局最优解,使用前一般需证明其贪心策略的正确性。
小杨设计了一个拆数程序,它能够将任意的非质数自然数 N 转换成若干个质数的乘积,这个程序是可以设计出来的。
对。算术基本定理(素数分解定理)保证任意大于 1 的整数都能唯一分解为若干质数的乘积,用从 2 开始的试除法逐次除尽即可设计出该拆数程序。
插入排序有时比快速排序时间复杂度更低。
对。当数据初始基本有序时,插入排序只需少量比较,可接近 O(N);而快速排序最坏情况可达 O(N²),因此插入排序有时确实更低。
下面的 C++ 代码能实现十进制正整数 N 转换为八进制并输出。
01char s[10]; 02int main() 03{ 04 int N; 05 cin >> N; 06 string rst = ""; 07 while (N != 0) 08 { 09 s[0]=N % 8 + '0'; 10 rst += string(s); 11 N /= 8; 12 } 13 cout << rst << endl; 14 15 return 0; 16}
错。循环把最低位余数 N%8 先拼进 rst,先得到的低位却排在最前,输出为颠倒的八进制,如 N=10 得 21 而非 12;且八进制表示应以 0 开头。
对数组 int arr[] = {2, 6, 3, 5, 4, 8, 1, 0, 9, 10} 执行 sort(arr, arr+10),则执行后 arr 中的数据调整为 {0, 1, 2, 3, 4, 5, 6, 8, 9, 10}。
对。sort(arr, arr+10) 对区间 [arr, arr+10) 内全部 10 个元素按默认升序排序,原序列 {2,6,3,5,4,8,1,0,9,10} 排序后正是 {0,1,2,3,4,5,6,8,9,10}。
小杨想写一个程序来算出正整数 N 有多少个因数,经过思考他写出了一个重复没有超过 N/2 次的循环就能够算出来了。
对。因数成对出现,枚举 1 到 N/2 检查 N%i==0 计数,再加上 N 自身,即可求出全部因数个数,循环次数恰好不超过 N/2。
同样的整数序列分别保存在单链表和双向链中,这两种链表上的简单冒泡排序的复杂度相同。
对。单链表与双向链表上简单冒泡排序的比较、交换次数相同,复杂度均为 O(N²);单链表找前驱更麻烦些只影响常数倍,不改变复杂度。