【计算机考研·数据结构】线性表的顺序表示·程序设计题

题目出处:王道考研2022年数据结构考研复习指导
本篇博客中所有题目代码均使用C++语言。


1.从顺序表中删除具有最小值的元素(假设唯一)并由函数返回被删函数的值。空出的位置由最后一个元素填补,若顺序表为空,则显示出错信息并退出运行。

遍历顺序表,记录最小值元素及其位置,遍历结束后用最后一个元素填补到记录的位置,顺序表长度减1。

bool del_min(SqList& L, ElemType& value) {
    if (L.length == 0) {
        // 显示出错信息
        return false;
    }
    value = L.data[0];
    int pos = 0;
    for (int i = 1; i < L.length; i++) {
        if (L.data[i] < value) {
            value = L.data[i];
            pos = i;
        }
    }
    L.data[pos] = L.data[L.length - 1];
    L.length--;
    return true;
}

2.设计一个高效算法,将顺序表 L L L 的所有元素逆置,要求算法的空间复杂度为 O ( 1 ) O(1) O(1)

遍历顺序表前半部分元素,将其与后半部分对应元素进行交换。

void Reverse(SqList& L) {
    ElemType t;
    for (int i = 0; i < L.length / 2; i++) {
        t = L.data[i];
        L.data[i] = L.data[L.length - i - 1];
        L.data[L.length - i - 1] = t;
    }
}

3.对长度为 n n n 的顺序表 L L L,编写一个时间复杂度为 O ( n ) O(n) O(n)、空间复杂度为 O ( 1 ) O(1) O(1) 的算法,该算法删除线性表中所有值为 x x x 的数据元素。

方法1:边遍历顺序表边记录顺序表中不等于 x x x 的元素个数cnt,将其向前移动cnt个位置,最后修改顺序表长度。

void del_x(SqList& L, Elemtype x) {
    int cnt = 0;
    for (int i = 0; i < L.length; i++) {
        if (L.data[i] != x) {
            L.data[cnt++] = L.data[i];
        }
    }
    L.length = cnt;
}

方法2:边遍历顺序表边记录顺序表中等于 x x x 的元素个数cnt,将不等于 x x x 的元素向前移动cnt个位置,最后修改顺序表长度。

void del_x(SqList& L, ElemType x) {
    int cnt = 0, i = 0;
    while (i < L.length) {
        if (L.data[i] == x) {
            cnt++;
        } else {
            L.data[i - cnt] = L.data[i];
        }
        i++;
    }
    L.length -= cnt;
}

4.从有序顺序表中删除其值在给定值 s s s t t t 之间(要求 s < t s < t s<t)的所有元素,若 s s s t t t 不合理或顺序表为空,则显示出错信息并退出运行。

寻找值大于或等于 s s s 的第一个元素,然后寻找值大于 t t t 的第一个元素,将这段元素删除。

bool del_s_t(SqList& L, ElemType s, ElemType t) {
    if (s >= t || L.length == 0) {
        // 显示出错信息
        return false;
    }
    int i, j;
    for (i = 0; i < L.length && L.data[i] < s; i++);
    if (i >= L.length) {
        // 显示出错信息
        return false;
    }
    for (j = i; j < L.length && L.data[j] <= t; j++);
    for (; j < L.length; i++, j++) {
        L.data[i] = L.data[j];
    }
    L.length = i;
    return true;
}

5.从顺序表中删除其值在给定值 s s s t t t 之间(包含 s s s t t t,要求 s < t s < t s<t)的所有元素,若 s s s t t t 不合理或顺序表为空,则显示出错信息并退出运行。

遍历顺序表,记录元素值在 s s s t t t 之间元素的个数cnt,对于当前元素,若其值不在 s s s t t t 之间,则前移cnt个位置,否则执行cnt++

bool del_s_t(SqList& L, ElemType s, ElemType t) {
    int cnt = 0;
    if (s >= t || L.length == 0) {
        // 显示出错信息
        return false;
    }
    for (int i = 0; i < L.length; i++) {
        if (L.data[i] >= s && L.data[i] <= t) {
            cnt++;
        } else {
            L.data[i - cnt] = L.data[i];
        }
    }
    L.length -= k;
    return true;
}

6.从有序顺序表中删除所有其值重复的元素,使表中所有元素的值均不同。

初始时将第一个元素视为非重复有序表,依次判断后面的元素是否与前面非重复有序表的最后一个元素相同,若相同则继续向后判断,若不同则插入前面的非重复有序表的最后,直至判断到表尾。

bool del_same(SqList& L) {
    if (L.length == 0) {
        return false;
    }
    int i, j;
    for (i = 0, j = 1; j < L.length; j++) {
        if (L.data[i] != L.data[j]) {
            L.data[++i] = L.data[j];
        }
    }
    L.length = i + 1;
    return true;
}

7.将两个有序顺序表合并为一个新的有序顺序表,并由函数返回结果顺序表。

按顺序不断取两表表头较小的元素存入新的顺序表,上述流程结束后将有剩余元素的那个表中的元素存入新的顺序表。

bool merge_list(SqList A, SqList B, SqList& C) {
    if (A.length + B.length > C.MaxSize) {
        return false;
    }
    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;
    return true;
}

8.已知在一维数组 A [ m + n ] A[m+n] A[m+n] 中依次存放两个线性表 ( a 1 , a 2 , a 3 , ⋯   , a m ) (a_1,a_2,a_3,\cdots,a_m) (a1,a2,a3,,am) ( b 1 , b 2 , b 3 , ⋯   , b n ) (b_1,b_2,b_3,\cdots,b_n) (b1,b2,b3,,bn)。试编写一个函数,将数组中两个顺序表的位置互换,即将 ( b 1 , b 2 , b 3 , ⋯   , b n ) (b_1,b_2,b_3,\cdots,b_n) (b1,b2,b3,,bn) 放在 ( a 1 , a 2 , a 3 , ⋯   , a m ) (a_1,a_2,a_3,\cdots,a_m) (a1,a2,a3,,am) 的前面。

先将数组 A [ m + n ] A[m+n] A[m+n] 中的全部元素逆置,再对前 n n n 个元素和后 m m m 个元素分别逆置。

void Reverse(ElemType arr[], int left, int right, int arrSize) {
    if (left >= right || right >= arrSize) {
        return;
    }
    int mid = left + (right - left) / 2;
    for (int i = 0; i <= mid - left; i++) {
        ElemType t = arr[left + i];
        arr[left + i] = arr[right - i];
        arr[right - i] = t;
    }
}

void exchange(ElemType A[], int m, int n, int ASize) {
    Reverse(A, 0, m + n - 1, ASize);
    Reverse(A, 0, n - 1, ASize);
    Reverse(A, n, m + n - 1, ASize);
}

9.线性表 ( a 1 , a 2 , a 3 , ⋯   , a n ) (a_1,a_2,a_3,\cdots,a_n) (a1,a2,a3,,an) 中的元素递增有序且按顺序存储于计算机内。要求设计一个算法,完成用最少时间在表中查找数值为 x x x 的元素,若找到,则将其与后继元素位置相交换,若找不到,则将其插入表中并使表中元素仍递增有序。

折半查找。

void search(ElemType A[], ElemType x, int n) {
    int left = 0, right = n - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (A[mid] = x) {
            break;
        } else if (A[mid] > x) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    if (A[mid] == x && mid != n - 1) {    // 查找成功且不为最后一个元素
        ElemType t = A[mid];
        A[mid] = A[mid + 1];
        A[mid + 1] = t;
    }
    if (left > right) {                   // 查找失败
        for (int i = n - 1; i > right; i--) {
            A[i + 1] = A[i];
        }
        A[i + 1] = x;
    }
}

10.【2010统考真题】设将 n ( n > 1 ) n(n>1) n(n>1) 个整数存放到一维数组 R R R 中。设计一个在时间和空间两方面都尽可能高效的算法。将 R R R 中保存的序列循环左移 p ( 0 < p < n ) p(0<p<n) p(0<p<n) 个位置,即将 R R R 中的数据由 ( X 0 , X 1 , ⋯   , X n − 1 ) (X_0,X_1,\cdots,X_{n-1}) (X0,X1,,Xn1) 变换为 ( X p , X p + 1 , ⋯   , X n − 1 , X 0 , X 1 , ⋯   , X p − 1 ) (X_p,X_{p+1},\cdots,X_{n-1},X_0,X_1,\cdots,X_{p-1}) (Xp,Xp+1,,Xn1,X0,X1,,Xp1)。要求:

1)给出算法的基本设计思想。

先将数组的前 p p p 个元素逆置,再将数组余下的 n − p n-p np 个元素逆置,最后再将整个数组逆置。

2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。

void Reverse(int R[], int left, int right) {
    for (int i = 0; i < (right - left + 1) / 2; i++) {
        int t = R[left + i];
        R[left + i] = R[right - i];
        R[right - i] = t;
    }
}

void solve(int R[], int n, int p) {
    Reverse(R, 0, p - 1);    // 逆置前p个元素
    Reverse(R, p, n - 1);    // 逆置余下的n-p个元素
    Reverse(R, 0, n - 1);    // 将整个数组逆置
}

3)说明你所设计算法的时间复杂度和空间复杂度。

solve函数中的三个Reverse函数的时间复杂度分别为 O ( p / 2 ) O(p/2) O(p/2) O ( ( n − p ) / 2 ) O((n-p)/2) O((np)/2) O ( n / 2 ) O(n/2) O(n/2),所以solve函数的时间复杂度为 O ( n ) O(n) O(n),其中 n n n 为数组 R R R 的长度, p p p 为循环左移的偏移量。

空间复杂度为 O ( 1 ) O(1) O(1),使用常数级空间。

11.【2011统考真题】一个长度为 L ( L ≥ 1 ) L(L \geq 1) L(L1) 的升序序列 S S S,处在第 ⌈ L / 2 ⌉ \lceil L/2 \rceil L/2 个位置的数称为 S S S 的中位数。例如,若序列 S 1 = ( 11 , 13 , 15 , 17 , 19 ) S_1=(11,13,15,17,19) S1=(11,13,15,17,19),则 S 1 S_1 S1 的中位数是 15 15 15,两个序列的中位数是含它们所有元素的升序序列的中位数。例如,若 S 2 = ( 2 , 4 , 6 , 8 , 20 ) S_2=(2,4,6,8,20) S2=(2,4,6,8,20),则 S 1 S_1 S1 S 2 S_2 S2 的中位数是 11 11 11。现在有两个等长升序序列 A A A B B B,试设计一个在时间和空间两方面都尽可能高效的算法,找出两个序列 A A A B B B 的中位数。要求:

1)给出算法的基本设计思想。

分别求序列 A , B A,B A,B 的中位数 a , b a,b a,b

①若 a = b a=b a=b,则 a a a(或 b b b)即为所求,算法结束。

②若 a < b a<b a<b,则舍弃序列 A A A 中较小的一半和序列 B B B 中较大的一半。

③若 a > b a>b a>b,则舍弃序列 A A A 中较大的一半和序列 B B B 中较小的一半。

在保留的两个升序序列中,重复上述过程,直到两个序列中均只含一个元素时为止,较小者即为所求。

2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。

int get_median(int A[], int B[], int n) {
    int s1 = 0, e1 = n - 1, s2 = 0, e2 = n - 1;    // 分别表示A和B的首末位元素
    while (s1 != e1 || s2 != e2) {
        int m1 = (s1 + e1) / 2;                    // 序列A的中位数
        int m2 = (s2 + e2) / 2;                    // 序列B的中位数
        if (A[m1] == B[m2]) {                      // 满足条件1
            return A[m1];
        } else if (A[m1] < B[m2]) {                // 满足条件2
            if ((s1 + e1) % 2 == 0) {              // 元素个数为奇数,舍弃操作保留中间点
                s1 = m1;
                e2 = m2;
            } else {                               // 元素个数为偶数
                s1 = m1 + 1;                       // 舍弃A中间点及之前部分
                e2 = m2;                           // 舍弃B中间点(不含)之后部分
            }
        } else {                                   // 满足条件3
            if ((s2 + e2) % 2 == 0) {              // 元素个数为奇数,舍弃操作保留中间点
                e1 = m1;
                s2 = m2;
            } else {                               // 元素个数为偶数
                e1 = m1;                           // 舍弃A中间点(不含)之后部分
                s2 = m2 + 1;                       // 舍弃B中间点及之前部分
            }
        }
    }
    return min(A[s1], B[s2]);                      // 返回剩余元素的较小者
}

3)说明你所设计算法的时间复杂度和空间复杂度。

时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 为序列 A A A(或序列 B B B)的长度。空间复杂度为 O ( 1 ) O(1) O(1),使用常数级空间。

12.【2013统考真题】已知一个整数序列 A = ( a 0 , a 1 , ⋯   , a n − 1 ) A=(a_0,a_1,\cdots,a_{n-1}) A=(a0,a1,,an1),其中 0 ≤ a i < n ( 0 ≤ i < n ) 0 \leq a_i < n(0 \leq i < n) 0ai<n(0i<n)。若存在 a p 1 = a p 2 = ⋯ = a p m = x a_{p_1}=a_{p_2}=\cdots=a_{p_m}=x ap1=ap2==apm=x m > n / 2 ( 0 ≤ p k < n , 1 ≤ k ≤ m ) m>n/2(0 \leq p_k<n,1 \leq k \leq m) m>n/2(0pk<n,1km),则称 x x x A A A 的主元素。例如 A = ( 0 , 5 , 5 , 3 , 5 , 7 , 5 , 5 ) A=(0,5,5,3,5,7,5,5) A=(0,5,5,3,5,7,5,5),则 5 5 5 为主元素;又如 A = ( 0 , 5 , 5 , 3 , 5 , 1 , 5 , 7 ) A=(0,5,5,3,5,1,5,7) A=(0,5,5,3,5,1,5,7),则 A A A 中没有主元素。假设 A A A 中的 n n n 个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出 A A A 的主元素。若存在主元素,则输出该元素;否则输出 − 1 -1 1。要求:

1)给出算法的基本设计思想。

遍历序列 A A A 中的元素,将第一个整数保存到变量c中,记录出现次数cnt = 1。若遇到的下一个整数仍然等于c,则计数cnt 1 1 1,否则计数减 1 1 1。当计数cnt减为 0 0 0 时,再将下一个整数保存到变量c中,计数重新置为 1 1 1,从当前位置起重复上述流程,直至遍历结束,此时得到候选变量c

判断候选值c是否为真正的主元素,第二次扫描序列 A A A,统计序列中等于c的元素个数,根据题意进行判断。

2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。

int major_elem(int* A, int n) {
    int c = A[0], cnt = 1;               // c为候选元素,初始值为A[0];cnt为计数变量
    for (int i = 1; i < n; i++) {        // 第一次遍历
        if (A[i] == c) {                 // 遇到相同元素计数+1
            cnt++;
        } else {                         // 遇到不同元素
            if (cnt > 0) {               // 计数大于0,计数-1
                cnt--;
            } else {                     // 计数已减为0,令A[i]为新的候选元素,重置计数为1
                c = A[i];
                cnt = 1;
            }
        }
    }
    int count = 0;                       // 第二次遍历的计数变量
    if (cnt > 0) {
        for (int i = 0; i < n; i++) {    // 统计候选变量的出现次数
            if (A[i] == c) {
                count++;
            }
        }
    }
    return count > n / 2 ? c : -1;       // 根据题意返回结果
}

3)说明你所设计算法的时间复杂度和空间复杂度。

两次遍历的时间复杂度均为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n ) O(n) O(n),其中 n n n 为序列 A A A 的长度。空间复杂度为 O ( 1 ) O(1) O(1),使用常数级空间。

13.【2018统考真题】给定一个含 n ( n ≥ 1 ) n(n \geq 1) n(n1) 个整数的数组,请设计一个在时间上尽可能高效的算法,找出数组中未出现的最小正整数。例如,数组 { − 5 , 3 , 2 , 3 } \{-5,3,2,3\} {5,3,2,3} 中未出现的最小正整数是 1 1 1;数组 { 1 , 2 , 3 } \{1,2,3\} {1,2,3} 中未出现的最小正整数是 4 4 4。要求:

1)给出算法的基本设计思想。

因为数组arr中含有 n n n 个整数,因此返回的值一定是 [ 1 , n + 1 ] [1,n+1] [1,n+1] 范围内的正整数。可以申请一个辅助数组t,长度为 n n n。遍历arr,对于arr中小于或等于 0 0 0 或大于 n n n 的元素不采取任何操作,对其他元素,t[i]表示arr中值等于 i + 1 i+1 i+1 的元素出现的次数。遍历结束后,遍历辅助数组t,若能找到第一个满足t[i] == 0的下标i,则跳出循环;若不能找到,则 n + 1 n+1 n+1 为结果,此时满足i == n。两种情况均返回i + 1即可。

2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。

int find_miss(int* arr, int n) {
    int* t = (int*) malloc(sizeof(int) * n);    // 申请辅助数组t空间
    memset(t, 0, sizeof(int) * n);              // 初始化辅助数组为全0
    int i;
    for (i = 0; i < n; i++) {                   // 遍历数组arr,计数
        if (arr[i] > 0 && arr[i] <= n) {
            t[arr[i] - 1] = 1;
        }
    }
    for (i = 0; i < n; i++) {                   // 寻找第一个满足条件的下标
        if (t[i] == 0) {                        // 找到则退出循环
            break;
        }
    }
    return i + 1;
}

3)说明你所设计算法的时间复杂度和空间复杂度。

遍历arr和遍历t的时间复杂度均为 O ( n ) O(n) O(n),所以总的时间复杂度为 O ( n ) O(n) O(n)。使用了辅助数组t,空间复杂度为 O ( n ) O(n) O(n)。其中 n n n 为数组长度。

14.【2020统考真题】定义三元组 ( a , b , c ) (a,b,c) (a,b,c) a , b , c a,b,c a,b,c 均为正数)的距离 D = ∣ a − b ∣ + ∣ b − c ∣ + ∣ c − a ∣ D=|a-b|+|b-c|+|c-a| D=ab+bc+ca。给定 3 3 3 个非空整数集合 S 1 S_1 S1 S 2 S_2 S2 S 3 S_3 S3,按升序分别存储在 3 3 3 个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组 ( a , b , c ) ( a ∈ S 1 , b ∈ S 2 , c ∈ S 3 ) (a,b,c)(a \in S_1,b \in S_2,c \in S_3) (a,b,c)(aS1,bS2,cS3) 中的最小距离。例如 S 1 = { − 1 , 0 , 9 } S_1=\{-1,0,9\} S1={1,0,9} S 2 = { − 25 , − 10 , 10 , 11 } S_2=\{-25,-10,10,11\} S2={25,10,10,11} S 3 = { 2 , 9 , 17 , 30 , 41 } S_3=\{2,9,17,30,41\} S3={2,9,17,30,41},则最小距离为 2 2 2,相应的三元组为 ( 9 , 10 , 9 ) (9,10,9) (9,10,9)。要求:

1)给出算法的基本设计思想。

根据 D = ∣ a − b ∣ + ∣ b − c ∣ + ∣ c − a ∣ D=|a-b|+|b-c|+|c-a| D=ab+bc+ca 可以分析出:

①当 a = b = c a=b=c a=b=c 时, D D D 可以取到最小值 0 0 0

②其他情况下,假设 a ≤ b ≤ c a \leq b \leq c abc L 1 = ∣ a − b ∣ L_1=|a-b| L1=ab L 2 = ∣ b − c ∣ L_2=|b-c| L2=bc L 3 = ∣ c − a ∣ L_3=|c-a| L3=ca,则可以得到 D = L 1 + L 2 + L 3 = 2 L 3 D=L_1+L_2+L_3=2L_3 D=L1+L2+L3=2L3。所以,要使 D D D 最小,就需要最小化 L 3 L_3 L3

使用变量ijk作为三个指针,分别指向三个序列的起始元素,遍历序列,计算S1[i]S2[j]S3[k]的距离,并更新结果变量ans。将S1[i]S2[j]S3[k]的最小值的下标加 1 1 1。遍历结束后结果变量ans即为所求。

2)根据设计思想,采用C或C++或Java语言描述算法,关键之处给出注释。

int find_min_D(int* S1, int* S2, int* S3, int S1Size, int S2Size, int S3Size) {
    int i = 0, j = 0, k = 0;                       // 指针变量,分别指向三个序列
    int ans = INT_MAX;                             // 初始化结果变量为INT_MAX
    while (i < S1Size && j < S2Size && k < S3Size && ans > 0) {
        int d = abs(S1[i] - S2[j]) + abs(S2[j] - S3[k]) + abs(S3[k] - S1[i]);
        ans = min(d, ans);                         // 更新ans为较小值
        if (S1[i] <= S2[j] && S1[i] <= S3[k]) {    // 将最小元素的下标+1
            i++;
        } else if (S2[j] <= S1[i] && S2[j] <= S3[k]) {
            j++;
        } else {
            k++;
        }
    }
    return ans;
}

3)说明你所设计算法的时间复杂度和空间复杂度。

时间复杂度为 O ( n ) O(n) O(n),其中 n n n 3 3 3 个数组的长度之和。空间复杂度为 O ( 1 ) O(1) O(1),使用常数级空间。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值