跳转至

一、顺序表(15 题)

2026 校订版 · 专题阅读 · 保留代码与校订注 · 与版本锁一致

一、顺序表(15 题)

顺序表默认结构

/**/ #define MAXSIZE 50

typedef struct {
    int data[MAXSIZE];
    int length;
} SqList;
回源 · PDF 第 4 页 · b_4_0;PDF 第 4 页 · b_4_3

1. 递增有序顺序表插入元素 x,插入后仍递增有序

int findInsertPos(SqList L, int x) {
    for (int i = 0; i < L.length; i++) {
        if (x < L.data[i])
            return i;
    }
    return L.length;
}

bool insertOrdered(SqList *L, int x) {
    if (L->length >= MAXSIZE)
        return false;

    int pos = findInsertPos(*L, x);
    for (int i = L->length - 1; i >= pos; i--)
        L->data[i + 1] = L->data[i];

    L->data[pos] = x;
    L->length++;
    return true;
}

复杂度: 时间 O(n),额外空间 O(1)。

核心思路: 先找到第一个大于 x 的元素位置,再从表尾向后搬移元素,最后插入 x 并更新表长。

回源 · PDF 第 4 页 · b_4_3

2. 用表尾元素覆盖第一个最小元素,并返回最小值

若有多个最小值,选择第一次出现的位置;若表尾元素就是所选最小元素,则不改变顺序表。

bool coverFirstMinWithLast(SqList *L, int *minValue) {
    if (L == NULL || minValue == NULL || L->length <= 0)
        return false;

    int pos = 0;
    *minValue = L->data[0];

    for (int i = 1; i < L->length; i++) {
        if (L->data[i] < *minValue) {
            *minValue = L->data[i];
            pos = i;
        }
    }

    if (pos != L->length - 1)
        L->data[pos] = L->data[L->length - 1];

    return true;
}

复杂度: 时间 O(n),额外空间 O(1)。

校订注:题面只要求“覆盖”并强调表尾为最小值时状态不变,因此这里不缩短表长。若题目本意是“删除最小元素并以表尾填补”,则覆盖后还应执行 L->length--,且即使最小元素在表尾也应缩短表长。

回源 · PDF 第 4 页 · b_4_1;PDF 第 4 页 · b_4_6;PDF 第 4 页 · b_4_8

3. 数组循环左移 p 个位置(2010 年统考题)

将 (X0, X1, ..., Xn-1) 变为 (Xp, Xp+1, ..., Xn-1, X0, ..., Xp-1),其中 0 < p < n。

void reverseRange(int R[], int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) {
        int temp = R[i];
        R[i] = R[j];
        R[j] = temp;
    }
}

void rotateLeft(int R[], int n, int p) {
    reverseRange(R, 0, n - 1);
    reverseRange(R, 0, n - p - 1);
    reverseRange(R, n - p, n - 1);
}

复杂度: 时间 O(n),额外空间 O(1)。

核心思路: 先整体逆置,再分别逆置前 n-p 个元素和后 p 个元素。

回源 · PDF 第 4 页 · b_4_2;PDF 第 4 页 · b_4_7

4. 无序顺序表去重,仅保留第一次出现的元素

void uniqueUnordered(SqList *L) {
    for (int i = 0; i < L->length; i++) {
        int k = i + 1;
        for (int j = i + 1; j < L->length; j++) {
            if (L->data[j] != L->data[i])
                L->data[k++] = L->data[j];
        }
        L->length = k;
    }
}

复杂度: 时间 O(n²),额外空间 O(1)。

核心思路: 固定每个第一次出现的元素,将它后面与之不同的元素依次前移,从而覆盖其重复项。

回源 · PDF 第 5 页 · b_5_0;PDF 第 5 页 · b_5_1;PDF 第 5 页 · b_5_5

5. 有序顺序表去重,仅保留第一次出现的元素

void uniqueOrdered(SqList *L) {
    if (L->length <= 1)
        return;

    int k = 1;
    for (int j = 1; j < L->length; j++) {
        if (L->data[j] != L->data[k - 1])
            L->data[k++] = L->data[j];
    }
    L->length = k;
}

复杂度: 时间 O(n),额外空间 O(1)。

核心思路: k 始终指向下一个唯一元素应写入的位置;有序表中的重复值必然相邻。

回源 · PDF 第 5 页 · b_5_2;PDF 第 5 页 · b_5_3;PDF 第 5 页 · b_5_6

6. 在有序表中找出出现次数最多的元素及次数

若有多个众数,优先选择值最小的元素。

bool modeOfOrderedList(SqList L, int *mode, int *frequency) {
    if (L.length <= 0 || mode == NULL || frequency == NULL)
        return false;

    int maxValue = L.data[0];
    int maxCount = 1;
    int count = 1;

    for (int i = 1; i < L.length; i++) {
        if (L.data[i] == L.data[i - 1]) {
            count++;
        } else {
            if (count > maxCount) {
                maxCount = count;
                maxValue = L.data[i - 1];
            }
            count = 1;
        }
    }

    if (count > maxCount) {
        maxCount = count;
        maxValue = L.data[L.length - 1];
    }

    *mode = maxValue;
    *frequency = maxCount;
    return true;
}

复杂度: 时间 O(n),额外空间 O(1)。

校订注:原稿未初始化 max_val,当所有元素只出现一次时会产生未定义结果。本版将第一个元素设为初始众数;只在次数严格更大时更新,因此并列时自然保留较小值。

回源 · PDF 第 5 页 · b_5_10;PDF 第 5 页 · b_5_4;PDF 第 5 页 · b_5_9

7. 找出数组中的主元素(2013 年统考题)

数组元素满足 0 <= A[i] < n。若某元素出现次数超过 n/2,则为主元素;不存在时返回 -1。

int majorityElement(int A[], int n) {
    int *count = (int *)calloc(n, sizeof(int));
    if (count == NULL)
        return -1;

    for (int i = 0; i < n; i++)
        count[A[i]]++;

    int result = -1;
    for (int value = 0; value < n; value++) {
        if (count[value] > n / 2) {
            result = value;
            break;
        }
    }

    free(count);
    return result;
}

复杂度: 时间 O(n),额外空间 O(n)。

核心思路: 利用元素取值范围建立计数数组,再查找计数超过 n/2 的值。

回源 · PDF 第 6 页 · b_6_1;PDF 第 6 页 · b_6_0;PDF 第 6 页 · b_6_5

8. 找出数组中未出现的最小正整数(2018 年统考题)

int smallestMissingPositive(int A[], int n) {
    bool *seen = (bool *)calloc(n + 1, sizeof(bool));
    if (seen == NULL)
        return -1;

    for (int i = 0; i < n; i++) {
        if (A[i] > 0 && A[i] <= n)
            seen[A[i]] = true;
    }

    int answer = n + 1;
    for (int value = 1; value <= n; value++) {
        if (!seen[value]) {
            answer = value;
            break;
        }
    }

    free(seen);
    return answer;
}

复杂度: 时间 O(n),额外空间 O(n)。

核心思路: 答案只可能位于 [1, n+1]。标记 1..n 是否出现,再返回第一个未被标记的数。

回源 · PDF 第 6 页 · b_6_2;PDF 第 6 页 · b_6_7

9. 两个递增有序表合并为一个递增有序表

void mergeAscending(SqList A, SqList B, SqList *C) {
    int i = 0, j = 0, k = 0;

    while (i < A.length && j < B.length) {
        if (A.data[i] <= B.data[j])
            C->data[k++] = A.data[i++];
        else
            C->data[k++] = B.data[j++];
    }
    while (i < A.length)
        C->data[k++] = A.data[i++];
    while (j < B.length)
        C->data[k++] = B.data[j++];

    C->length = k;
}

复杂度: 时间 O(m+n);除结果表外,额外空间 O(1)。

回源 · PDF 第 2 页 · b_2_12;PDF 第 6 页 · b_6_3

10. 两个递增有序表合并为一个递减有序表

void mergeDescending(SqList A, SqList B, SqList *C) {
    int i = A.length - 1;
    int j = B.length - 1;
    int k = 0;

    while (i >= 0 && j >= 0) {
        if (A.data[i] >= B.data[j])
            C->data[k++] = A.data[i--];
        else
            C->data[k++] = B.data[j--];
    }
    while (i >= 0)
        C->data[k++] = A.data[i--];
    while (j >= 0)
        C->data[k++] = B.data[j--];

    C->length = k;
}

复杂度: 时间 O(m+n);除结果表外,额外空间 O(1)。

回源 · PDF 第 2 页 · b_2_13;PDF 第 6 页 · b_6_3

11. 输出三个递增无重复序列的公共元素

三个序列 A、B、C 长度均为 n。

int max3(int a, int b, int c) {
    int maxValue = a;
    if (b > maxValue) maxValue = b;
    if (c > maxValue) maxValue = c;
    return maxValue;
}

void printCommonElements(int A[], int B[], int C[], int n) {
    int i = 0, j = 0, k = 0;

    while (i < n && j < n && k < n) {
        if (A[i] == B[j] && A[i] == C[k]) {
            printf("%d ", A[i]);
            i++;
            j++;
            k++;
        } else {
            int maxValue = max3(A[i], B[j], C[k]);
            if (A[i] < maxValue) i++;
            if (B[j] < maxValue) j++;
            if (C[k] < maxValue) k++;
        }
    }
}

复杂度: 时间 O(n),额外空间 O(1)。

核心思路: 三指针同步前进;三值相等时输出,否则让所有小于当前最大值的指针前移。

校订注:原稿的 fmax 使用严格比较,在最大值并列时可能返回较小值,本版已修正。

回源 · PDF 第 7 页 · b_7_2;PDF 第 7 页 · b_7_0;PDF 第 7 页 · b_7_1;PDF 第 7 页 · b_7_6

12. 三个升序集合的最小三元组距离(2020 年统考题)

三元组距离为 D = |a-b| + |b-c| + |c-a|。

int minTripleDistance(int S1[], int m,
                      int S2[], int n,
                      int S3[], int p) {
    int i = 0, j = 0, k = 0;
    int minDistance = INT_MAX;

    while (i < m && j < n && k < p) {
        int distance = abs(S1[i] - S2[j])
                     + abs(S2[j] - S3[k])
                     + abs(S3[k] - S1[i]);

        if (distance < minDistance)
            minDistance = distance;

        if (S1[i] <= S2[j] && S1[i] <= S3[k])
            i++;
        else if (S2[j] <= S1[i] && S2[j] <= S3[k])
            j++;
        else
            k++;
    }

    return minDistance;
}

复杂度: 时间 O(m+n+p),额外空间 O(1)。

核心思路: 每次计算当前三元组的距离,然后移动三者中最小值所在的指针;只有提高最小值才可能缩小极差和距离。

暴力法需枚举所有三元组,时间复杂度为 O(mnp)。原稿暴力代码末尾误写为 return min_val;,正确变量应为 min_dis。

回源 · PDF 第 7 页 · b_7_3;PDF 第 7 页 · b_7_7

13. 两个等长升序序列的中位数(2011 年统考题)

这里的中位数指合并后 2n 个元素中的第 n 小元素,即较小的那个中间数。

int medianOfTwoEqualSortedArrays(int A[], int B[], int n) {
    int s1 = 0, d1 = n - 1;
    int s2 = 0, d2 = n - 1;

    while (s1 != d1) {
        int m1 = (s1 + d1) / 2;
        int m2 = (s2 + d2) / 2;

        if (A[m1] == B[m2])
            return A[m1];

        if (A[m1] < B[m2]) {
            if ((s1 + d1) % 2 == 0) {
                s1 = m1;
                d2 = m2;
            } else {
                s1 = m1 + 1;
                d2 = m2;
            }
        } else {
            if ((s2 + d2) % 2 == 0) {
                d1 = m1;
                s2 = m2;
            } else {
                d1 = m1;
                s2 = m2 + 1;
            }
        }
    }

    return A[s1] < B[s2] ? A[s1] : B[s2];
}

复杂度: 时间 O(log n),额外空间 O(1)。

校订注:原稿采用完整归并,复杂度为时间 O(n)、空间 O(n),不满足题目“时间和空间都尽可能高效”的要求。本版换为等长区间折半法。

回源 · PDF 第 8 页 · b_8_2;PDF 第 8 页 · b_8_9

14. 将负数移到数组前端,非负数移到后端

void partitionBySign(int A[], int n) {
    int left = 0;
    int right = n - 1;

    while (left < right) {
        while (left < right && A[left] < 0)
            left++;
        while (left < right && A[right] >= 0)
            right--;

        if (left < right) {
            int temp = A[left];
            A[left] = A[right];
            A[right] = temp;
        }
    }
}

复杂度: 时间 O(n),额外空间 O(1)。

核心思路: 左指针寻找非负数,右指针寻找负数,二者交换后继续向中间收缩。此算法不保证原相对次序稳定。

回源 · PDF 第 8 页 · b_8_4;PDF 第 8 页 · b_8_3;PDF 第 8 页 · b_8_7

15. 计算 res[i] = max(A[i] * A[j])(2025 年统考题)

其中 0 <= i <= j <= n-1。

void calculateMaxProducts(int A[], int res[], int n) {
    int suffixMax = A[n - 1];
    int suffixMin = A[n - 1];
    res[n - 1] = A[n - 1] * A[n - 1];

    for (int i = n - 2; i >= 0; i--) {
        if (A[i] > suffixMax) suffixMax = A[i];
        if (A[i] < suffixMin) suffixMin = A[i];

        if (A[i] >= 0)
            res[i] = A[i] * suffixMax;
        else
            res[i] = A[i] * suffixMin;
    }
}

复杂度: 时间 O(n),除输出数组外额外空间 O(1)。

核心思路: 从右向左维护后缀最大值和最小值。A[i] 非负时与后缀最大值相乘最优;为负时与后缀最小值相乘最优。更新后缀极值时包含 A[i],因此也覆盖 j=i 的情况。

回源 · PDF 第 9 页 · b_9_0;PDF 第 9 页 · b_9_2;PDF 第 9 页 · b_9_12