设有 个已排好序的数据元素,采用折半查找时,最大比较次数为( )。
D:7。折半查找最大比较次数 ⌊log₂n⌋+1:100 满足 2⁶=64<100≤128=2⁷,最多比较 6+1=7 次;其余 6/8/10 均与公式不符。
在二分查找算法中,如果有序列表中有 N 个元素,其时间复杂度是( )。
B:二分查找每轮取 Mid 比较后搜索区间缩小一半,最坏需 log2N 轮,所以含 N 个元素的有序列表查找时间复杂度为 O(log N)。
若用二分法在 内猜数,最多需要猜( )次。
C:每次猜数把候选区间缩小一半,[1,100] 共 100 个数,2^6=64 不够覆盖、2^7=128 足够,故最多需要 7 次(ceil(log2(100))=7)。
二分查找适用于对无序数组和有序数组的查找。
错。二分查找每次按 arr[mid] 与 target 的大小关系决定进入左半还是右半区间,这要求数组必须有序;无序数组无法据此排除一半元素,只能退化为顺序查找。
查字典这个小学生必备技能,可以把字典视为一个已排序的数组。假设小杨要查找一个音首字母为 g 的单词,他首先翻到字典约一半的页数,发现该页的首字母是 m ,由于字母表中 g 位于 m 之前,所以排除字典后半部分,查找范围缩小到前半部分;不断重复上述步骤,直至找到首字母为 g 的页码。这种查字典的一系列操作可看作二分查找。
对。字典按字母序排列可看作有序数组,翻到中间页比较首字母,g 在 m 前则排除后半部分,每次比较排除约一半范围,正是二分查找的操作过程。
在 C++ 中,可以使用二分法查找链表中的元素。
错。二分查找需要按下标 O(1) 取中位元素再决定区间,链表只能从头指针顺序遍历,无法随机访问,即使链表有序也不能用二分查找。
下面 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 比较后把搜索区间减半递归,属二分、分治与递归,但没有重叠子问题、没有状态转移,不是动态规划。
小杨在生日聚会时拿一块 H*W 的巧克力招待来的 K 个小朋友,保证每位小朋友至少能获得一块相同大小的巧克力。那么小杨想分出来最大边长的巧克力可以使用二分法。
错。二分法在考纲中分为二分查找与二分答案两类,本题求巧克力最大边长需对边长二分并验证能否切出至少 K 块,属于二分答案,笼统说可用二分法不准确。
给定序列:。使用以下代码进行二分查找查找元素 82 时,需要循环多少次,即最后输出的 times 值为( )。
01int binarySearch(const std::vector<int>& arr, int target) { 02 int left = 0; 03 int right = arr.size() - 1; 04 int times = 0; 05 while (left <= right) { 06 times ++; 07 int mid = left + (right - left) / 2; 08 if (arr[mid] == target) { 09 cout << times << endl; 10 return mid; 11 } else if (arr[mid] < target) { 12 left = mid + 1; 13 } else { 14 right = mid - 1; 15 } 16 } 17 cout << times << endl; 18 return -1; 19}
D:13 个数下标 0~12,查 82 时 mid 为 6(39<82,left 变 7)、9(79<82,left 变 10)、11(90>82,right 变 10)、10(81<82,left 变 11),left=11>right=10 退出,共 4 次循环,times=4、返回 -1。
根据下述二分查找法,在排好序的数组 中查找数值 ,和 比较的数组元素分别是( )。
01int binary_search(vector<int>& nums, int target) { 02 int left = 0; 03 int right = nums.size() - 1; 04 while (left <= right) { 05 int mid = (left + right) / 2; 06 if (nums[mid] == target) { 07 return mid; 08 } else if (nums[mid] < target) { 09 left = mid + 1; 10 } else { 11 right = mid - 1; 12 } 13 } 14 return -1; // 如果找不到目标元素,返回-1 15}
C:39、79、90、81。数组共 13 个元素,二分查找中 mid 依次为下标 6、9、11、10,对应元素 39、79、90、81 逐个与 82 比较,最后左端越过右端返回 -1,未找到。
根据下述二分查找法,在排好序的数组 中查找数值 ,循环 while (left <= right) 执行的次数为( )。
01int binary_search(vector<int>& nums, int target) { 02 int left = 0; 03 int right = nums.size() - 1; 04 05 while (left <= right) { 06 int mid = left + (right - left) / 2; 07 08 if (nums[mid] == target) { 09 return mid; 10 } 11 else if (nums[mid] < target) { 12 left = mid + 1; 13 } 14 else { 15 right = mid - 1; 16 } 17 } 18 return -1; // 如果找不到目标元素,返回-1 19}
C:第 1 趟 left=0、right=9,mid=4 得 nums[4]=17<31,left 改 5;第 2 趟 mid=7 得 52>31,right 改 6;第 3 趟 mid=5 命中 31,while 共执行 3 次。
给定一个长度为 的有序数组 nums,其中所有元素都是唯一的。下面的函数返回数组中元素 target 的索引。关于上述函数,描述不正确的是( )。
01int binarySearch(vector<int> &nums, int target, int left, int right) { 02 if (left > right) { 03 return -1; 04 } 05 06 int middle = left + ((right - left) / 2); 07 if (nums[middle] == target) { 08 return middle; 09 } 10 else if (nums[middle] < target) { 11 return binarySearch(nums, target, middle + 1, right); 12 } 13 else 14 return binarySearch(nums, target, left, middle - 1); 15} 16 17int Find(vector<int> &nums, int target) { 18 int n = nums.size(); 19 return binarySearch(nums, target, 0, n - 1); 20}
C:递归终止有两处,除命中 target 外还有 left>right 返回 -1,数组不含 target 时区间收缩到 left>right 正常退出,不会死循环;A、B、D 均正确。
给定一个长度为 的有序数组 nums,其中可能包含重复元素。下面的函数返回数组中某个元素 target 的左边界,若数组中不包含该元素,则返回 。例如在数组 nums = [5,7,7,8,8,10] 中查找 target=8,函数返回 在数组中的左边界的索引为 。则横线上应填写的代码为( )。
01int getLeftBoundary(vector<int>& nums, int target) { 02 int left = 0; 03 int right = nums.size() - 1; 04 05 while (left < right) { 06 int middle = left + ((right - left) / 2); 07 if (target <= nums[middle]) 08 ____________ // 在此处填入代码 09 else 10 left = middle+1; 11 } 12 13 return nums[left]==target?left:-1; 14}
B:target<=nums[middle] 时左边界在 middle 或更左,应取 right=middle 保留该位置;while 条件 left<right 最终让 left 停在最左的 target,再校验 nums[left]==target。
对有序数组 {5,13,19,21,37,56,64,75,88,92,100} 进行二分查找,成功查找元素 的比较次数是 。
对。数组 11 个元素,首轮 mid=5 比较 56,56>19 转左半 [0,4];次轮 mid=2 比较 19 命中,共 2 次比较。
下面代码实现了二分查找算法,在数组 arr 找到目标元素 target 的位置,则横线上能填写的最佳代码是( )。
01int binarySearch(int arr[], int left, int right, int target) { 02 while (left <= right) { 03 ____________ // 在此处填入代码 04 if (arr[mid] == target) 05 return mid; 06 else if (arr[mid] < target) 07 left = mid + 1; 08 else 09 right = mid - 1; 10 } 11 return -1; 12}
A:mid 应为区间中点,left+(right-left)/2 是 (left+right)/2 的防溢出写法,避免 left+right 超过 int 上限,是二分查找的标准最佳写法;B、D 把 mid 设为端点无法对半分区间。
二分查找依赖数据的有序性,通过循环逐步缩减一半搜索区间来进行查找,且仅适用于数组或基于数组实现的数据结构。
对。二分查找每次比较 mid 处元素后把区间缩减一半,前提是数据有序,且需支持 O(1) 随机访问;链表只能顺序访问,无法直接二分。
下面代码尝试在有序数组中查找第一个大于等于 的元素位置。如果没有大于等于 的元素,返回 arr.size()。以下说法正确的是( )。
01int lower_bound(vector<int>& arr, int x) { 02 int l = 0, r = arr.size(); 03 while(l < r) { 04 int mid = l + (r - l) / 2; 05 if(arr[mid] >= x) r = mid; 06 else l = mid + 1; 07 } 08 return l; 09}
A:这是左闭右开写法,l<r、满足条件时 r=mid,正确返回第一个 arr[mid]≥x 的位置;若全部小于 x,l 会推进到 arr.size() 后退出循环,正合题意。
小杨要把一根长度为 的木头切成 段,使得每段长度小于等于 。已知每切一刀只能把一段木头分成两段,他用二分法找到满足条件的最小 ( 为正整数),则横线处应填写( )。
01// 判断:在不超过 K 次切割内,是否能让每段长度 <= x 02bool check(int L, int K, int x) { 03 int cuts = (L - 1) / x; 04 return cuts <= K; 05} 06 07// 二分查找最小可行的 x 08int binary_cut(int L, int K) { 09 int l = 1, r = L; 10 while (l < r) { 11 int mid = l + (r - l) / 2; 12 ____________ // 在此处填入代码 13 } 14 return l; 15} 16 17int main() { 18 int L = 10; // 木头长度 19 int K = 2; // 最多切 K 刀 20 21 cout << binary_cut(L, K) << endl; 22 return 0; 23}
A:check 为真说明 x 可行但可再小,r=mid 收缩;为假则 l=mid+1。代入 L=10、K=2:x=4 时 cuts=(10−1)/4=2 可行,x=3 时 cuts=3 不可行,输出 4。
二分查找仅适用于有序数据。若输入数据无序,当仅进行一次查找时,为了使用二分而排序通常不划算。
对。二分查找要求数据有序,无序时无法根据中点值与 x 的大小决定舍弃哪一侧;仅查一次时先排序要 O(n log n),超过直接线性扫描的 O(n),故不划算。
在升序数组中查找第一个大于等于 x 的位置,下面循环中横线应填( )。
01int lowerBound(const vector<int>& a, int x){ 02 int l=0, r=a.size(); 03 while(l<r){ 04 int mid = l + (r - l)/2; 05 if(a[mid] >= x) ____________; 06 else l = mid + 1; 07 } 08 return l; 09}
A:a[mid]>=x 时答案落在 [l,mid] 内,令 r=mid 保留 mid;否则 a[mid]<x 排除 mid 令 l=mid+1;左闭右开区间下循环结束 l 即第一个大于等于 x 的位置。
若数组 a 已按升序排列,则下面代码可以正确实现"在 a 中查找第一个大于等于 x 的元素的位置"。
01int lowerBound(vector<int>& a, int x){ 02 int l=0, r=a.size(); 03 while(l < r) { 04 int mid = (l + r) / 2; 05 if( a[mid] >= x) r = mid; 06 else l = mid + 1; 07 } 08 return l; 09}
对。l、r 取左闭右开区间 [l,r),a[mid]>=x 时答案含 mid 令 r=mid,否则 a[mid]<x 排除 mid 令 l=mid+1,循环结束 l 恰为第一个大于等于 x 的位置。
在一个有序数组中查找第一个大于或等于 x 的元素位置,横线处应填写( )。
01int lowerBound(vector<int>& a, int x) { 02 int l = 0, r = a.size(); 03 while (l < r) { 04 int mid = l + (r - l) / 2; 05 if (a[mid] >= x) ________________; // 在此处填入代码 06 else l = mid + 1; 07 } 08 return l; 09}
C:a[mid]>=x 时答案可能在 mid 或更左,令 r=mid 保留 mid 候选并缩小区间;r=mid+1 会跳过 mid,r=mid-1 可能漏掉答案,l=mid 在 l<r 下会死循环。
二分查找不仅可以应用于有序数组,也可以在不增加时间复杂度的情况下应用于有序的单链表,因为链表也支持 时间内的随机访问。
错。单链表没有 O(1) 随机访问能力,每次取中间结点都要从头遍历 O(n) 个结点,二分查找会退化;二分查找的前提是数组式下标随机访问,链表只能用快慢指针等顺序手段。
下面的 C++ 代码用于在升序数组 lst 中查找目标值 target 最后一次出现的位置。相关说法,正确的是( )。
01int binary_search_last_occurrence(const vector<int>& lst, int target) { 02 if (lst.empty()) return -1; 03 04 int low = 0, high = lst.size() - 1; 05 06 while (low < high) { 07 int mid = (low + high + 1) / 2; 08 if (lst[mid] <= target) { 09 low = mid; 10 } else { 11 high = mid - 1; 12 } 13 } 14 15 if (lst[low] == target) 16 return low; 17 else 18 return -1; 19}
A:mid=(low+high+1)/2 上取整,lst[mid]<=target 时令 low=mid,保证向最后一个 target 收敛,全相同数组也能退出并返回末尾。target 过小时返回 -1 而非 0;改 (low+high)/2 会因 low=mid 死循环。
有关下面 C++ 代码的说法,错误的是( )。
01double sqrt_binary(long long n, double epsilon = 1e-10) { 02 if (n < 0) { 03 throw invalid_argument("输入必须为非负整数"); 04 } 05 06 if (n == 0 || n == 1) return n; 07 08 // 阶段 1 09 long long low = 1, high = n; 10 long long k = 0; 11 12 while (low <= high) { 13 long long mid = (low + high) / 2; 14 long long mid_sq = mid * mid; 15 16 if (mid_sq == n) { 17 return mid; 18 } else if (mid_sq < n) { 19 k = mid; 20 low = mid + 1; 21 } else { 22 high = mid - 1; 23 } 24 } 25 26 long long next_k = k + 1; 27 if (next_k * next_k == n) { 28 return next_k; 29 } 30 31 // 阶段 2 32 double low_d = (double)k; 33 double high_d = (double)(k + 1); 34 double mid; 35 36 while (high_d - low_d >= epsilon) { 37 mid = (low_d + high_d) / 2; 38 double mid_sq = mid * mid; 39 40 if (mid_sq < n) { 41 low_d = mid; 42 } else { 43 high_d = mid; 44 } 45 } 46 47 double result = (low_d + high_d) / 2; 48 long long check_int = (long long)(result + 0.5); 49 if (check_int * check_int == n) { 50 return check_int; 51 } 52 53 return result; 54}
D:high_d-low_d 从 1 起每轮折半,约 34 轮即小于 1e-10 退出,double 精度足够,不会死循环。A、B、C 分别对应阶段 1 找整数平方根、阶段 2 二分逼近小数根、check_int 修正浮点误差,均正确。
给定一个 的矩阵 matrix,矩阵的每一行和每一列都按升序排列。函数 countLE 返回矩阵中第 小的元素,则两处横线上应分别填写( )。
01// 统计矩阵中 <= x 的元素个数:从左下角开始 02int countLE(const vector<vector<int>>& matrix, int x) { 03 int n = (int)matrix.size(); 04 int i = n - 1, j = 0, cnt = 0; 05 while (i >= 0 && j < n) { 06 if (matrix[i][j] <= x) { 07 cnt += i + 1; 08 ++j; 09 } 10 else { 11 --i; 12 } 13 } 14 return cnt; 15} 16 17int kthSmallest(vector<vector<int>>& matrix, int k) { 18 int n = (int)matrix.size(); 19 int lo = matrix[0][0]; 20 int hi = matrix[n - 1][n - 1]; 21 while (lo < hi) { 22 int mid = lo + (hi - lo) / 2; 23 if (countLE(matrix, mid) >= k) { 24 ____________ // 在此处填入代码 25 } else { 26 ____________ // 在此处填入代码 27 } 28 } 29 return lo; 30}
C:countLE(mid)>=k 说明第 k 小元素不超过 mid,收上界 hi=mid;否则在 mid 右侧,lo=mid+1。A 的 hi=mid-1 可能跳过答案,B 永不收敛。
在二叉排序树(Binary Search Tree,BST)中查找元素 ,从根节点开始:若根值为 ,则下一步应去搜索:
A:左子树。BST 中左子树所有结点的值都小于根结点,目标值 50 小于根值 60,故下一步应去左子树中继续查找。,继续在左子树找。
下面 search 函数的平均时间复杂度为( )。
01int search(int n, int * p, int target) { 02 int low = 0, high = n; 03 while (low < high) { 04 int middle = (low + high) / 2; 05 if (target == p[middle]) { 06 return middle; 07 } else if (target > p[middle]) { 08 low = middle + 1; 09 } else { 10 high = middle; 11 } 12 } 13 return -1; 14}
C:search 是二分查找,每轮把搜索区间减半,最多 log₂n 次迭代,平均与最坏时间复杂度均为 O(log n),故选 C,故选 C。
下面程序的运行结果为( )。
01++ 02#include <iostream> 03 04int query(int n, int *a, int x) { 05 int l = 0, r = n; 06 while (l < r) { 07 int mid = l + (r - l) / 2; 08 if (a[mid] >= x) r = mid; 09 else l = mid + 1; 10 } 11 12 if (l == n) return -1; 13 return l; 14} 15 16int main() { 17 int n = 10; 18 int x = 3; 19 int num[] = {1, 2, 2, 3, 3, 4, 5, 5, 6, 7}; 20 21 std::cout << query(n, num, x) << "\n"; 22 return 0; 23}
B:query 是二分查找第一个 ≥ x 的位置:a[mid]>=3 时 r=mid,否则 l=mid+1,num 中第一个 ≥3 的下标是 3(num[3]=3)。
在升序数组中用二分查找第一个大于等于 的位置。若当前中点 满足 ,下一步应( )。
B:二分找第一个 ≥ x 的位置,a[mid]<x 说明 mid 及其左边都小于 x,令左边界 l=mid+1 继续向右查。故选 B。
给定 double 类型的变量 ,且其值⼤于等于 ,我们可以通过⼆分法求出的 近似值
A:正确。√x 在 [0, x](x<1 时上界取 1)上单调,二分比较 mid² 与 x 的大小即可不断收窄区间,迭代 k 次误差降至 2⁻ᵏ,能逼近任意精度。
若能写出判定函数 check(x),表示“答案为 时是否可行”,即使 check(x) 不满足单调性,也一定可以使用二分答案求最优解。
B:错误。二分答案要求 check(x) 关于 x 单调(可行与不可行分界唯一);不单调时二分可能跳过最优解,无法保证求出最优。