数组

1

在包含 n 个整数的已排序数组中,确定某个整数是否出现超过 n/2 次所需的最小比较次数是( ):

正确答案:B

如果选择Θ(1),请考虑示例:

  • {1, 2, 2, 2, 4, 4}
  • {1, 1, 2, 2, 2, 2, 3, 3}

判断已排序(升序)数组中是否存在出现次数超过 n/2 次的整数的最佳方法是使用二分查找。具体步骤如下:

  1. 通过分治技术在 O(log(n)) 时间内找到目标元素的首次出现位置 i
  2. 通过分治技术在 O(log(n)) 时间内找到目标元素的末次出现位置 j
  3. 计算该元素的出现次数:count = j - i + 1

时间复杂度分析:

O(log n) [首次查找]
+ O(log n) [末次查找]
+ 1 [计数计算]
= O(log n)
2

一个算法对一组键值来自线性有序集合的数据项执行 $(logN)^{1/2}$ 次查找操作、$N$ 次插入操作、$(logN)^{1/2}$ 次删除操作和 $(logN)^{1/2}$ 次减键操作。对于删除操作,会提供指向必须删除记录的指针;对于减键操作,也会提供指向其键值被减少的记录的指针。如果目标是通过考虑所有操作实现最佳的总渐近时间复杂度,那么以下哪种数据结构最适合该算法使用?( )

正确答案:A
  • 无序数组:插入操作的时间复杂度为 O(1),因为可以在末尾直接插入。
  • 最小堆:插入操作需要 O(log n) 时间(参考二叉堆操作)。
  • 有序数组:插入操作可能需要移动所有元素,最坏情况下为 O(n)
  • 有序双向链表:需要 O(n) 时间来找到插入位置。

由于插入操作的数量在渐近意义上最高(N 次),因此优先选择插入效率最高的 无序数组。尽管其他操作如删除和减键可能需要额外开销,但题目明确提供了直接指针,使得这些操作可在 O(1) 时间内完成,从而整体时间复杂度由插入操作主导。

3

考虑一个包含正数和负数的数组。将相同符号的数字分组(即所有正数在一边,所有负数在另一边)的算法最坏情况下的时间复杂度是多少?( )

正确答案:A

解释:

  • 可以采用快速排序的分区思想进行分组
  • 通过单次遍历即可完成符号分组操作
  • 时间复杂度与数组长度呈线性关系
  • 最终时间复杂度为 O(N)
4

A[1...n] 是一个包含 n 个互不相同数字的数组。若 i < jA[i] > A[j],则称 (i, j)A 的一个逆序对。在任意 n 个元素的排列中,逆序对的期望数量是多少?( )

正确答案:B
  • 数组中共有 $ \frac{n(n-1)}{2} $ 个满足 $ i < j $ 的数对组合
  • 对于任意一对 $ (a_i, a_j) $,由于元素互异且随机排列,出现逆序对的概率为 $ \frac{1}{2} $
  • 根据期望的线性性质,总期望为: $$ \frac{1}{2} \times \frac{n(n-1)}{2} = \frac{n(n-1)}{4} $$
5

考虑一个二维数组 A[20][10]。假设每个内存单元存储 4 个字,数组 A 的基地址为 100,元素按行优先顺序存储且第一个元素是 A[0][0]A[11][5] 的地址是多少?( )

正确答案:A
  • 计算行偏移:

    $$ \text{行偏移} = 11 \times 10 \times 4 = 440 $$

    因此,A[11][0] 的地址为:

    $$ 100 + 440 = 540 $$

  • 计算列偏移: $$ \text{列偏移} = 5 \times 4 = 20 $$ 最终地址为: $$ 540 + 20 = 560 $$

6

数组 A 包含 n 个整数,位于 A[0], A[1]...A[n-1]。要求将数组元素循环左移 k 位,其中 1 <= k <= (n-1)。给出一个不完整的算法,在不使用额外数组的情况下以线性时间完成此操作。请通过填充空白处来完善该算法。假设所有变量均已正确声明。

min = n;
i = 0;
while(___________){
    temp = A[i];
    j = i;
    while(________){
        A[j] = ________;
        j = (j + k) % n;
        If(j < min) then min = j;
    }
    A[(n + i - k) % n] = _________;
    i = ___________;
}
正确答案:D
在题目给出的五个空白中,最后两个空白必须是 tempi+1,因为第四、第五个空白的所有选项都包含这两个值。现在考虑第一个空白,当初始时 i=0min=n 时,若条件为 i>min 则会直接退出 while 循环,因此第一个空白应为 i < min,这意味着选项 (B)(D) 可能正确。假设选项 (B) 正确,则第二个 while 循环的条件为 j!=(n+i) mod n。由于 (n+i) mod n=i 且代码第 3 行将 i 赋值给 j,此时 j 始终等于 i,导致控制流永远不会进入内部循环,而实际上需要进入内部循环才能实现左移 k 位的操作。因此选项 (D) 正确。
7

以下哪项正确声明了一个数组?( )

正确答案:A

解析:
选项 A 正确地声明了一个整数数组 geeks,其大小为 20。

  • int 指定了数组元素的数据类型为整型。
  • geeks 是数组的名称。
  • [20] 表示数组可存储 20 个元素。

其他选项均不符合 C/C++ 的数组声明语法规范。

8

在 C++ 中,一个三维数组声明为 int A[x][y][z]。假设数组元素按行优先顺序存储且索引从 0 开始。那么,位置 A[p][q][r] 的地址可以计算如下(其中 w 是整数的字长):( )

正确答案:B

解析:

  1. 行优先存储原理
    三维数组 int A[x][y][z] 在内存中按行优先顺序存储,其访问顺序遵循:

    • 第一维(x)变化最慢
    • 第二维(y)次之
    • 第三维(z)变化最快
  2. 地址计算步骤

    • 定位到第 p 行
      需跨越前 p 行的所有元素: $$ \text{元素数量} = p \times (y \times z) $$
    • 定位到第 q 列
      需跨越当前行中前 q 列的所有元素: $$ \text{元素数量} = q \times z $$
    • 定位到第 r 个元素
      直接取当前列的第 r 个元素: $$ \text{元素数量} = r $$
  3. 总偏移量与地址公式

    • 总元素偏移量: $$ \text{Total Offset} = y \cdot z \cdot p + z \cdot q + r $$
    • 最终地址: $$ \text{Address} = &A[0][0][0] + w \cdot (\text{Total Offset}) $$ 即: $$ &A[0][0][0] + w \cdot (y \cdot z \cdot p + z \cdot q + r) $$
  4. 结论
    选项 B 的表达式完全匹配上述推导结果,因此 选项 B 正确

9

设 A 是一个 n×n 的方阵。考虑以下程序。预期输出是什么?

C = 100
for i = 1 to n do
    for j = 1 to n do
        {
            Temp = A[i][j] + C
            A[i][j] = A[j][i]
            A[j][i] = Temp - C
        }
for i = 1 to n do
    for j = 1 to n do
        Output(A[i][j])
正确答案:A
  • 关键分析:观察第一个循环中的三步操作:
    1. Temp = A[i][j] + C
    2. A[i][j] = A[j][i]
    3. A[j][i] = Temp - C
  • 执行效果:当 i ≠ j 时,该操作实现了 A[i][j]A[j][i] 的值交换(通过中间变量 Temp)。
  • 双重交换机制:由于循环遍历所有 (i, j) 组合,每个非对角线元素会被交换两次(一次作为 (i,j),另一次作为 (j,i)),从而恢复原始值。
  • 结论:矩阵整体未发生任何变化,最终输出仍为原矩阵 A。
10

程序 P 读取 500 个范围在 [0..100] 的整数,这些整数代表 500 名学生的成绩。然后程序输出每个高于 50 的成绩的出现频率。对于 P 来说,存储这些频率的最佳方式是什么?( )

正确答案:A

解析

  • 题目要求统计高于 50 的成绩的频率
  • 成绩范围是 [0..100],因此高于 50 的有效成绩范围是 51 到 100(共 50 个不同值)
  • 每个值对应的频率可以用一个长度为 50 的数组存储
  • 其他选项要么空间浪费(如 100/500/550),要么不符合实际需求
  • 因此,一个包含 50 个数字的数组是最优解
11

以下程序中,调用函数 fun 的正确方式是什么?( )

#include <bits/stdc++.h>
#include <iostream>
using namespace std;

void fun(char* arr) {
    int i;
    unsigned int n = strlen(arr);
    for (i = 0; i < n; i++)
        cout << " " << arr[i];
}

// 主程序
int main() {
    char arr[] = {'g', 'e', 'e', 'k', 's', 'q', 'u', 'i', 'z'};
    // 如何在此处调用上述函数以打印字符元素?
    return 0;
}
正确答案:C
  • 数组退化规则:在 C/C++ 中,数组作为函数参数传递时会自动退化为指向其首元素的指针
  • 参数匹配char arr[] 在函数参数中等价于 char* 类型
  • 正确调用:直接使用 arr 即可(等价于 &arr[0]),与函数参数类型 char* 完全匹配
  • 错误选项分析
    • fun(&arr) 实际传递的是 char(*)[9] 类型(指向数组的指针)
    • _arr 不是合法标识符
    • None 选项不符合实际需求
12

设 A 是一个 n x n 的矩阵。考虑以下程序。预期输出是什么?

void fun(int A[][N]) {
    for (int i = 0; i < N; i++)
        for (int j = i + 1; j < N; j++)
            swap(A[i][j], A[j][i]);
}
正确答案:C
  • 解析
    在函数 fun 中,通过双重循环遍历矩阵的上三角区域(即 $i < j$ 的位置)。
    • 对于每个元素 $A[i][j]$,将其与 $A[j][i]$ 交换。
    • 这一操作实际上将矩阵的行和列互换位置,最终得到的是矩阵 $A$ 的转置矩阵
      因此,正确答案是 (C)
13

以下哪项是数组的局限性?( )

正确答案:D

假设我们声明一个数组:

arr[5] = {1, 2, 3};

此时数组的大小被声明为 5,但实际仅初始化了 3 个元素。这种预分配空间与实际使用量的差异会直接导致内存资源的浪费。因此选项(D)描述的情况是数组的典型局限性。

14

考虑以下程序,该程序基本上在做什么?

#include<bits/stdc++.h>
using namespace std;

void print(char a[], int n, int ind) {
    for(int i = ind; i < n + ind; i++)
        cout << a[(i % n)] << " ";
}

int main() {
    char a[] = {'A','B','C','D','E','F'};
    int n = sizeof(a)/sizeof(a[0]);
    print(a, n, 3);
    return 0;
}
正确答案:B

在上述程序中,我们只是将数组元素循环右移 3 位。循环从 i=3 运行到 9,在循环内部:

  • a[3%6] = a[3] = 'D'
  • a[4%6] = a[4] = 'E'
  • a[5%6] = a[5] = 'F'
  • a[6%6] = a[0] = 'A'
  • a[7%6] = a[1] = 'B'
  • a[8%6] = a[2] = 'C'

因此选项 (B) 正确。

15

请填写空白以完成程序,实现将数组旋转 d 个元素。

/*Function to left rotate arr[] of size n by d*/
void Rotate(int arr[], int d, int n) {
    int p = 1;
    while(_______) {
        int last = arr[0];
        for(int i = 0; _______i++) {
            arr[i] = arr[i+1];
        }
        __________
        p++;
    }
}
正确答案:A

解析:

  1. 第一个空白处(while 循环条件)
    p <= d
    因为初始值 p = 1,需要循环执行 d 次(每次左移一个元素),所以条件应为 p <= d

  2. 第二个空白处(for 循环条件)
    i < n - 1
    循环需将前 n-1 个元素依次前移,因此终止条件为 i < n - 1

  3. 第三个空白处(更新最后一个元素)
    arr[n - 1] = last;
    将原首元素 last 赋值给数组末尾,完成单次左移操作。

综上,选项 (A) 正确。

16

考虑以下程序。预期输出是什么?

void fun(int arr[], int start, int end) {
    while (start < end) {
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
}
正确答案:C
该函数通过交换首尾元素逐步向中间移动,最终实现数组反转