数据结构强化
1.快速排序算法
1.1 2011统考真题42
42.一个长度为L(L大于等于1)升序序列S,处在第[L/2]个位置的数称为S的中位数。例如,若序列S1=(11,13,15,17,19),则S1的中位数是15,两个序列的中位数是含它们所有元素的升序序列的中位数。例如,若S2=(2,4,6,8,20)则S1和S2的中位数是11。现在有两个等长升序序列A和B,试设计一个在时间和空间两方面都尽可能高效的算法,找出两个序列A和B的中位数。要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用 C、C++或 Java 语言描述算法,关键之处给出注释。
(3)说明你所设计算法的时间复杂度和空间复杂度。
运行代码
#include<stdio.h>
int A[5] = {11, 13, 15, 17, 19};
int B[5] = {2, 4, 6, 8, 20};
// 寻找枢轴
int partition(int A[],int L,int R){
int mid = A[L];
while (L<R){
while (A[R]>=mid && L<R) R--;
A[L] = A[R];
while (A[L]<=mid && L<R) L++;
A[R] = A[L];
}
A[L] = mid;
return L;
}
// 循环快排
void QSort(int A[],int L,int R){
if(L>=R) return;
int M = partition(A,L,R);
QSort(A,L,M-1); // 左半部分快排
QSort(A,M+1,R); // 右半部分快排
}
// 打印数组信息
void Print(int A[],int length){
for (int i = 0; i < length; ++i) {
printf(" %d",A[i]);
}
}
// 寻找中位数
int func(int A[],int M,int B[],int N){
// 两数组合并
int C[M+N];
for (int i = 0; i < M; ++i) {
C[i]= A[i];
}
for (int i = 0; i < N; ++i) {
C[i+M] = B[i];
}
// 对合并后数组快速排序
QSort(C,0,M+N-1);
printf("序列C:");
Print(C,10);
// 找到中间元素
return C[(N+M-1)/2];
}
int main() {
printf("序列A:");
Print(A,5);
printf("\n");
printf("序列B:");
Print(B,5);
printf("\n");
printf("\n两个序列A和B的中位数是:%d",func(A,5,B,5));
}
输出结果
序列A: 11 13 15 17 19
序列B: 2 4 6 8 20
序列C: 2 4 6 8 11 13 15 17 19 20
两个序列A和B的中位数是:11
1.2 2013统考真题41
41.已知一个整数序列A=(a0,a1,……,an-1),其中ai大于等于0,小于n(i大于等于0小于n)。若存在apl=ap2=……=apm=x且m>n/2(Pk大于等于0小于m,k大于等于1小于等于m),则称x为A的主元素。例如A=(0,5,5,3,5,7,5,5),则5为主元素;又如A=(0,5,5,3,5,15,7),则A中没有主元素。假设A中的n个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出A的主元素。若存在主元素,则输出该元素;否则输出-1。要求:
(1)给出算法的基本设计思想。
(2) 根据设计思想,采用 C、C++或 Java 语言描述算法,关键之处给出注释
(3)说明你所设计算法的时间复杂度和空间复杂度。
运行代码
#include<stdio.h>
int A[8] = {0, 5, 5, 3, 5, 7, 5, 5};
int B[8] = {0, 5, 5, 3, 5, 1, 5, 7};
int partition(int A[], int L, int R) {
int mid = A[L];
while (L < R) {
while (A[R] >= mid && L < R) R--;
A[L] = A[R];
while (A[L] <= mid && L < R) L++;
A[R] = A[L];
}
A[L] = mid;
return L;
}
void QSort(int A[], int L, int R) {
if (L >= R) return;
int M = partition(A, L, R);
QSort(A, L, M - 1); // 左半部分快排
QSort(A, M + 1, R); // 右半部分快排
}
void Print(int A[], int length) {
for (int i = 0; i < length; ++i) {
printf(" %d", A[i]);
}
}
int func(int A[], int n) {
QSort(A, 0, n - 1);
int x = A[n / 2];
int count = 0;
// 左半部分主元素个数累加
for (int i = n / 2 - 1; i >= 0; --i)
if (A[i] == x)
count++;
// 右半部分主元素个数累计
for (int i = n / 2; i <= n - 1; ++i)
if (A[i] == x)
count++;
// 比较主元素与数组长度一半
if (count > n / 2) return x;
else return -1;
}
int main() {
printf("数组A:");
Print(A, 8);
printf("\n");
printf("数组B:");
Print(B, 8);
printf("\n");
printf("数组A的主元素为:%d", func(A, 8));
printf("\n");
printf("数组B的主元素为:%d", func(B, 8));
}
输出结果
数组A: 0 5 5 3 5 7 5 5
数组B: 0 5 5 3 5 1 5 7
数组A的主元素为:5
数组B的主元素为:-1
1.3 2018统考真题41
41.给定一个含n(n大于等于1)个整数的数组,请设计一个在时间上尽可能高效的算法,找出数组中未出现的最小正整数。例如,数组{-5,3,2,3}中未出现的最小正整数是1; 数组{1,2,3}中未出现的最小正整数是4。要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释(3)说明你所设计算法的时间复杂度和空间复杂度。
运行代码
#include<stdio.h>
int A[5] = {-5,1,2,3,4};
int B[5] = {-5,4,7,3,2};
int C[5] = {-5,-4,-7,-3,-2};
int partition(int A[], int L, int R) {
int mid = A[L];
while (L < R) {
while (A[R] >= mid && L < R) R--;
A[L] = A[R];
while (A[L] <= mid && L < R) L++;
A[R] = A[L];
}
A[L] = mid;
return L;
}
void QSort(int A[], int L, int R) {
if (L >= R) return;
int M = partition(A, L, R);
QSort(A, L, M - 1); // 左半部分快排
QSort(A, M + 1, R); // 右半部分快排
}
void Print(int A[], int length) {
for (int i = 0; i < length; ++i) {
printf(" %d", A[i]);
}
}
int func(int A[], int n) {
QSort(A, 0, n - 1);
int m = -1;
for (int i = 0; i < n; i++) {
if(A[i]>0){
m = i;
break;
}
}
// 数组值全为负值
if(m == -1) return 1;
// 数组中不含有值为1的数
if(A[m] != 1) return 1;
// 检查数组相邻差值
for (m= m+1; m < n; m++)
// 数组相邻差值大于1
if(A[m]-A[m-1]>1)
// 较小元素加一
return A[m-1]+1;
// 数组相邻差值均等于1 最大元素加1
return A[n-1]+1;
}
int main() {
printf("数组A:");
Print(A, 5);
printf("\n");
printf("数组B:");
Print(B, 5);
printf("\n");
printf("数组C:");
Print(C, 5);
printf("\n");
printf("数组A中未出现的最小正整数是:%d", func(A, 5));
printf("\n");
printf("数组B中未出现的最小正整数是:%d", func(B, 5));
printf("\n");
printf("数组C中未出现的最小正整数是:%d", func(C, 5));
printf("\n");
}
输出结果
数组A: -5 1 2 3 4
数组B: -5 4 7 3 2
数组C: -5 -4 -7 -3 -2
数组A中未出现的最小正整数是:5
数组B中未出现的最小正整数是:1
数组C中未出现的最小正整数是:1
1.4 2016统考真题43
43.已知由n(n>=2)个正整数构成的集合A={ak|0<=k<n},将其划分为两个不相交的子集A1和A2,元素个数分别是n1和n2,A1和A2中元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足 |n1-n2|最小且|S1-S2|最大。要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
(3)说明你所设计算法的平均时间复杂度和空间复杂度。
运行代码
#include<stdio.h>
int A[5] = {1, 13, 5, 21, 19};
// 寻找枢轴
int partition(int A[],int L,int R){
int mid = A[L];
while (L<R){
while (A[R]>=mid && L<R) R--;
A[L] = A[R];
while (A[L]<=mid && L<R) L++;
A[R] = A[L];
}
A[L] = mid;
return L;
}
// 循环快排
void QSort(int A[],int L,int R){
if(L>=R) return;
int M = partition(A,L,R);
QSort(A,L,M-1); // 左半部分快排
QSort(A,M+1,R); // 右半部分快排
}
// 打印数组信息
void Print(int A[],int length){
for (int i = 0; i < length; ++i) {
printf(" %d",A[i]);
}
}
// 寻找中位数
int func(int A[],int n){
QSort(A,0,n-1);
}
int main() {
printf("未排序:");
Print(A,5);
printf("\n");
printf("排序后:");
func(A,5);
Print(A,5);
// 集合A1为A[0]~A[n/2-1]
// 集合A2为A[n/2]~A[n-1]
// min |n1-n2| max |s1-s2|
}
输出结果
未排序: 1 13 5 21 19
排序后: 1 5 13 19 21
2.快速排序算法划分思想
2.1 例题1
试编写一个算法,使之能够在数组L[1...n]中找出第k小的元素 (即从小到大排序后处于第k个位置的元素)。
运行代码
#include<stdio.h>
int A[5] = {1, 13, 5, 21, 19};
// 寻找枢轴
int partition(int A[],int L,int R){
int mid = A[L];
while (L<R){
while (A[R]>=mid && L<R) R--;
A[L] = A[R];
while (A[L]<=mid && L<R) L++;
A[R] = A[L];
}
A[L] = mid;
return L;
}
// 打印数组信息
void Print(int A[],int length){
for (int i = 0; i < length; ++i) {
printf(" %d",A[i]);
}
}
int func(int L[],int n,int k){
int left = 1,right = n,M=0;
while (1){
M = partition(L,left,right);
if(M==k) break;
else if(M>k) right=M-1;
else if(M<k) left = M+1;
}
return L[k];
}
int main() {
printf("数组为:");
Print(A,5);
printf("\n");
printf("第3小的元素为%d",func(A,5,3));
}
输出结果
数组为: 1 13 5 21 19
第3小的元素为13