出2个算法设计题,每个20分。
1. 设a[0:n-1]是有n个元素的数组,k(0≤k≤n-1)是一个非负整数。试设计一个算法将子数组a[0:k-1]和a[k:n-1]换位。要求算法在最坏情况下耗时O(n),且只用到O(1)的辅助空间。
初步思考:最简单的方法就是循环(n-k-1)次,将a数组的末尾数字插入到a[0]之前。
具体做法:法一:(1) 首先开辟一个额外空间temp用于存放每一次a数组的末尾数据。
(2) temp <- a[n-1]
(3) 将a[0: n-2] 每个数据都依次向后移动一位赋值给a[1: n-1]。
(4) a[0] <- temp
(5) 循环执行(2) -(4) 步 (n-k+1)次。
代价分析: 时间代价—— O((n-1)*(n-k+1)) 即O(n^2)数量级;空间代价——O(1)
法二:如果a[0 : k] 与 a[k+1 : n-1] 正好长度相等,则可以直接一一对应交换即可。 当然,这
道题的难点就在于k并不一定是a数组的中间位置。即便如此,但是仍然可以交换:
如果a[0 : k].length< a[k+1 : n-1].length, 则可以将a[0 : k] 与 a[k+1 : n-1] 中最后一部分大小相同的数据交换:
|-------- a[k+1 : n-1] -----------|
a[0:k] a[k+1 : n-k-2] a[n-k-1 : n-1]
其中 a[0:k] 与 a[n-k-1 : n-1] 长度相同,因此完全可以一一对应交换成:
|------ a[0 : n-k-2] -------|秋天的祝福语
a[0:k] a[k+1 : n-k-2] a[n-k-1 : n-1]
交换完成以后,则a[n-k-1 : n-1] 已经交换到位,而a[0 : n-k-2 ]还需要进一步这样递归交换。
源代码如下:
C代码
1 格列喹酮片说明书#include<stdio.h>
2
3 //交换数组的两段大小相等的范围的对应数据
4 //a[low1] <->a[low2] a[low1+1]<->a[low2+1] ... a[high1] <-> a[high2]
5 void s a[],int low1,int high1,int low2,int high2){
6
7 int temp;
8 while(low1<=high1){
9 temp=a[low1];
10 a[low1]=a[low2];
11 a[low2]=temp;
12 low1++;
13 low2++;
14 }
15 }
16
17 //利用分治算法, 每次选择最小的数组进行换位
18 void patition(int a[], int low, int k, int high){
19
20 if(low<high){
21 if((k-low+1)==(high-k))
22 s);
23 el if((k-low+1)<(high-k)){
24 s);
25 patition(a,low,k,low+high-k-1);
26 }
27 el{
28 s);
29 patition(a,high+low-k,k,high);
30 }
31 }
32
33 }
34 //测试
35 int main(){
36 int a[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13};
37 patition(a,0,4,13);
38 for(int i=0;i<14;i++){
桂花茶功效39 printf("%d ",a[i]);
40 }
41 return 0;
42 }
[c] view plaincopy
43 真情真爱#include<stdio.h>
44
45 //交换数组的两段大小相等的范围的对应数据
46 //a[low1] <->a[low2] a[low1+1]<->a[low2+1] ... a[high1] <-> a[high2]
47 void s a[],int low1,int high1,int low2,int high2){
48
49 int temp;
50 舌尖上的中国观后感while(low1<=high1){
51 temp=a[low1];
52 a[low1]=a[low2];
53 a[low2]=temp;
54 low1++;
55 low2++;
56 }
57 }
58
59 //利用分治算法, 每次选择最小的数组进行换位
60 void patition(int a[], int low, int k, int high){
61
62 if(low<high){
63 if((k-low+1)==(high-k))
64 s);
65 el if((k-low+1)<(high-k)){
66 s);
67 patition(a,low,k,low+high-k-1);
68 }
cofco69 el{
70 s);
71 patition(a,high+low-k,k,high);
72 }
73 }
74
75 }
76 //测试
77 int main(){ 中月
78 int a[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13};
79 patition(a,0,4,13);
80 for(int i=0;i<14;i++){
81 printf("%d ",a[i]);
82 }
83 return 0;
84 }
这样的时间复杂度为O(n),而且交换数据的时候只需要O(1)的额外空间。
2. 设子数组a[0:k-1]和a[k:n-1]已经排好序k(0≤k≤n-1)。试设计一个合并这两个子数组为排好序的数组a[0:n-1]的算法。要求算法在最坏情况下耗时O(n),且只用到O(1)的辅助空间。
1)向右循环换位合并
向右循环换位合并算法首先用二分搜索算法在数组段a[k:n-1]中搜索a[0]的插入位置,即找到位置p使得a[p]<a[0]<=a[p+1]。数组段a[0:p]向右循环换位p-k+1个位置,使得a[k-1]移动到a[p]的位置。此时,原数组a[0]及其左边的所有元素均已经排好序。对剩余的数组元素重复上述过程,直到只剩下一个数组段,此时整个数组已经排好序。
代码如下所示。
1 #include <iostream>
2 using namespace std;
3
4 #define MAXNUM 100
5
6 //下面函数将数组段a[s:t活跃度]中元素循环右移位k个位置
7 void shiftright(int a[], int s, int t, int k)
8 {
9 int i = 0;
10 int j = 0;
11 for(i = 0; i < k; i++)
12 {
13 int tmp = a[t];
14 for(j = t; j > s; j--)
15 {
16 a[j] = a[j-1];
17 }
18 a[s] = tmp;
19 }
20 }
21
22 //下面函数用于在数组段中a[left:right]中搜索元素x的插入位置
23 int binarySearch(int a[], int x, int left, int right)
24 {
25 int mid;
26 while(left <= right)
27 {
28 mid = left + (right - left)/2;
29 if(x == a[mid])
30 {
31 return mid;
32 }
33 if(x > a[mid])
34 {
35 left = mid + 1;
36 }
37 el
38 {
39 right = mid - 1;
40 }
41 }
42 if(x > a[mid])
43 {
44 return mid;
45 }
46 el
47 {
48 return mid - 1;
49 }
50 }
51
52 //向右循环移位合并算法
53 //length:数组的长度
54 void mergefor(int a[], int k, int length)
55 {
56 //merge a[0:k-1] and a[k:n-1]
57 int i = 0;
58 int j = k;
59 while(i < j && j < length)
60 {
61 int p = binarySearch(a, a[i], j, length - 1);