【解法1】暴力解:枚举法
强化讲义 · 原始转写 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照
## 【解法1】暴力解:枚举法
(1) 对每个元素 A[i],计算比它小的元素个数 count,如果恰好为 n/2,则为中位数。
(2) 伪代码如下:
int ans(int A[], int n)
{
for (int i = 0; i < n; i++)
{
int count = 0;
for (int j = 0; j < n; j++)
if (A[j] < A[i]) count++;
if (count == n / 2) return A[i];
}
} return -1;
(3)时间复杂度:O(n²)空间复杂度:O(1)
## 【解法2】暴力解:快速排序
(1)将数组 A快速排序,输出排序后数组 A最中间的数 A[n/2]即是中位数。(2) 伪代码如下:
//快速排序函数
void Qsort(int A[], int L, int R)
{
if (L >= R) return; //区间≤1,返回
int i = L, j = R;
//选首元素为基准
// 不断交换左边比 pivot 大的数和右边比 pivot 小的数
while (i < j)
{
while (i < j && A[j] >= pivot) j--; // 从右找第—个 <pivot 的数
while (i < j && A[i] <= pivot) i++; // 从左找第—个 >pivot 的数
//将基准值A[L] 与A[i] 交换
swap(A[L], A[i]);
//递归处理左右两个子区间
Qsort(A, L, i - 1);
Qsort(A, i + 1, R);
}
int ans(int A[],int n)
{
Qsort(A, 0, n-1);
return A[n/2];
}
}
<!-- page 60 -->
<!-- code-audit: false_positive -->
> [!info] 已人工审读:这是复杂度说明正文,不是代码。
(3)时间复杂度:O(nlogn)空间复杂度:O(logn)
## 【解法3】优化解:散列表
(1) 设置散列表 count[2n+1],散列函数 f(x) = x - n,其中 count[i] 表示 i-n 这个数出现的次数,循环遍历数组 A 并更新 count 数组。之后i从 0 遍历到 2n,每次将 count[i]累加,当累加变量 >= n / 2 时输出 i - n 并退出程序。(2) 伪代码如下:
int ans(int A[], int n)
{
int count[2 * n + 1] ={0};// 散列表记录x-n出现的次数
for (int i = 0; i < n; i++)
count[A[i] + n]++;
int sum = 0; KX //累计到目前为止已经是第几小的数了
for (int i = 0; i < 2*n+1; i++)
{
sum = sum + count[i];
if (sum > n/2)
return i-n;
}
}
(3)时间复杂度:O(n) 空间复杂度:O(n)
## 【解法4】优化解:多指针法
(1) 对无序数组使用多指针法,类似快速排序,但是在递归调用区间[L, i-1]和[i+1,R]时,因为中位数只可能出现在下标包含n/2的区间中,所以增加一个判断,只调用其中一个区间。
(2) 伪代码如下:
void Qsort_Select(int A[], int L, int R)
{
if (L >= R) return; //区间≤1,返回
int i = L, j = R;
int pivot = A[L]; //选首元素为基准
不断交换左边比pivot 大的数和右边比 pivot 小的数
while (i < j)
{
while (i < j && A[j] >= pivot) j--; // 从右找第—个 ≤pivot 的数
if (i < j) swap(A[i], A[j]);
}
//将基准值A[L] 与A[i]交换
swap(A[L], A[i]);
if (L <= n/2 && i-1 >= n/2)
Quick_Select(A, L, i-1, n); // 左区间包括中位数
else
Quick_Select(A, i+1, R, n); //右区间包括中位数
}
int ans(int A[], int n)
{
Quick_Select(A, 0, n-1, n);
return A[n/2];
}
<!-- page 61 -->
(3)时间复杂度:O(n)空间复杂度:O(logn)
## 习题2-5
(15 分)已知数组A含有n个整数,现在要将小于k的元素放在数组右侧,大于等于k的元素放在左侧,设计一个时间上尽可能高效的算法实现这个功能。例:n = 7,A = {4,5, 9, 3, 7,10, 1},k = 6,则处理之后的数组可以为{10,7, 9, 3, 5,4,1},其中 10、7、9 比 6 大,在数组左侧;而3、5、4、1小于等于6,在数组右侧。(1)给出算法的基本设计思想。(2)根据设计思想,采用C、C++或Java 语言描述算法,关键之处给出注释。(3)说明你所设计算法的时间复杂度和空间复杂度。
<table><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>做题思路</td><td rowspan=1 colspan=1>时间复杂度</td><td rowspan=1 colspan=1>空间复杂度</td><td rowspan=1 colspan=1>得分</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>快速排序</td><td rowspan=1 colspan=1>O(nlogn)</td><td rowspan=1 colspan=1>O(logn)</td><td rowspan=1 colspan=1>13</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>多指针法</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>15</td></tr></table>
## 【解法1】暴力解:快速排序
(1)将数组A快速排序得到升序数组,然后将数组A翻转并输出即可得到降序数组,满足小于k的元素在右侧,大于k的元素在左侧。(2)伪代码如下:
//快速排序函数
void Qsort(int A[], int L, int R)
{
if (L >= R) return; //区间≤1,返回
int i = L, j = R;
int pivot = A[L]; // 选首元素为基准
// 不断交换左边比 pivot 大的数和右边比 pivot 小的数
while (i < j)
{
while (i < j && A[i] <= pivot) i++; // 从左找第个 >pivot 的数
if (i < j) swap(A[i], A[j]);
}
//将基准值A[L] 与A[i] 交换
swap(A[L], A[i]);
//递归处理左右两个子区间
Qsort(A, L, i - 1);
Qsort(A, i + 1, R);
}
int ans(int A[], int n, int k)
{
Qsort(A, 0, n-1);
for (int i<0; i<n/2; i++)
swap(A[i], A[n-i-1]);
输出数组 A
}
<!-- page 62 -->
(3)时间复杂度:O(nlogn)空间复杂度:O(logn)
## 【解法2】优化解:多指针法
(1)该方法类似快速排序,只需要把左侧小于k的元素和右侧大于k的元素不断交换即可。左右分别设置指针i、j,j 从右往左移动直到 A[i]>k,i 从左往右移动直到 A[i]] < k,交换 A[i]和 A[i]。不断重复该过程直到i和j相遇。(2)伪代码如下: EX
void Multiointers(int A[], int L, int R, int k)
{
if (L>=R) return; // 区间≤1,退出
int pivot, i=L, j=R; // i,j 为左右指针
pivot=k;
while (i<j)
{
while (i<j && A[j]<=pivot) j--; // 右边<=则不处理
while (i<j && A[i]>=pivot) i++; //左边>=则不处理
if (i<j) swap(A[i], A[j]); /7A[j]>k,A[i]<k
}
}
int ans(int A[], int n, int k)
{
Multiointers(A, 0, n-1, k);
按顺序输出A数组
}
(3) 时间复杂度:O(n)空间复杂度:O(1)
## 习题2-6
(13 分)有一个数组 A由 n 个互不相同的正整数组成(n 为奇数),该序列中的数先减后增,中位数是最小值,输入一个整数 key。请设计一个时间和空间上尽可能高效的算法,判断key是否在该数组中,如果是则返回数组下标(下标从0开始),否则返回-1。例如对应 A= {10,7, 5, 2, 6, 13,15},key = 5 时返回下标 2,若 key = 8 则返回 -1。(1) 给出算法的基本设计思想。(2)根据设计思想,采用C 或C++语言描述算法,关键之处给出注释。(3)说明你所设计算法的时间复杂度和空间复杂度。
<!-- page 63 -->
<table><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>做题思路</td><td rowspan=1 colspan=1>时间复杂度</td><td rowspan=1 colspan=1>空间复杂度</td><td rowspan=1 colspan=1>得分</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>枚举</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>10</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>折半查找</td><td rowspan=1 colspan=1>NO(logn)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>13</td></tr></table>
## 【解法1】暴力解:枚举,扫描一遍数组
(1) 循环i扫描一次数组,如果A[i]== key则返回i,如果没找到则返回-1。(2) 伪代码如下:
int ans(int A[], int n, int key)
{
for (int i = 0; i < n; i++)
if (A[i] == key) return i;
return -1;
}
(3)时间复杂度:O(n)空间复杂度:O(1)
## 【解法2】优化解:折半查找两次
(1)A[0...n/2]递减,前半段折半查找一次;A[n/2...n-1]递增,后半段再折半查找一次。(注意:因为一个是降序一个是升序,这两次写法会有不同,也可以合成一种写法)(2)伪代码如下: 7
<!-- suspect-code -->
> [!warning] 这段代码需要核对(原文第 63 页)
> OCR 体检未通过:小括号不平衡。扫描件的两栏排版(代码 + 右侧注释)有时会被交错读取,产生看起来合理但语义错误的代码。背之前请对照原文。
//在升序数组中查找
int B_Search_up(int A[],int L, int R, int x)
{
int mid;
// 区间非空则继续
{
mid = (R + L) / 2; //防溢出
return mid; //找到了,立即返回
else
if (x < A[mid]) R = mid - 1;
else L = mid + 1; // x 在右半边,mid 被排除
}
return -1; //循环结束,说明没找到
//在降序数组中查找
int B_Search_down(int A[], int L, int R, int x))
{
int mid;
while (L <= R) // 区间非空则继续
{
mid = (R + L) / 2; //防溢出
if (A[mid] == x)
return mid; //找到了,立即返回
else
if (x > A[mid]) R = mid - 1;
else L = mid + 1; // x 在右半边, mid 被排除
}
return -1; // 循环结束,说明没找到
}
int ans(int A[], int n, int key)
{
int i;
i=B_Search_down(A,0,n/2-1,key); // 折半查找降序序列
if (i!=-1)
return i; //折半查找降序成功
i=B_Search_up(A, n/2, n-1, key); //折半查找升序序列
return i;
}
}
<!-- page 64 -->
(3)时间复杂度:O(logn)空间复杂度:O(1)
## 习题2-7
(13 分)定义距离 d = x - yl,给定 2 个长度为 n 的升序数组 a、b,x 是数组 a 中的某个元素,y是数组b中的某个元素。请设计一个尽可能高效的算法,计算并输出所有可能的最小距离 d。例如 数组 a = {-16, -8, 5, 8, 13},b ={-2,0,2,6,10},则最小距离为 1,相应的 x=5,y=6,d=|5- 6|=1。(1)给出算法的基本设计思想。(2)根据设计思想,采用C 或C++语言描述算法,关键之处给出注释。(3)说明你所设计算法的时间复杂度和空间复杂度。
<table><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>做题思路</td><td rowspan=1 colspan=1>时间复杂度</td><td rowspan=1 colspan=1>空间复杂度</td><td rowspan=1 colspan=1>预计得分</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>枚举</td><td rowspan=1 colspan=1>O(n2)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>10</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>多指针法</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>O(1)</td><td rowspan=1 colspan=1>13</td></tr></table>
## 【解法1】暴力解:枚举
(1)使用双指针i,j分别指向a,b,同步遍历。每次移动指向较小元素的指针,才有可能获得更小的距离。(2)伪代码如下:
int ans(int a[], int b[], int n)
{
int D_min = INT_MAX; // D_min初值为最大
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
D_min = min(D_min, abs(a[i] - b[j]));
return D_min;
}
<!-- page 65 -->
(3)时间复杂度:O(n²)空间复杂度:O(1)
## 【解法2】优化解:多指针法
(1) 使用双指针i,j 分别指向a, b,同步遍历。每次移动指向较小元素的指针,才有可能获得更小的距离。(2)伪代码如下: A
int ans(int a[], int b[],int n)
{
int D_min = INT_MAX; // D_min初值为最大
int i = 0, j = 0;
while (i<n && j<n)
{
D_min = min(D_min, abs(a[i] - b[j]));
if (a[i]<b[j]) i++;
else j++;
}
return D_min;
}
(3)时间复杂度:O(n)空间复杂度:O(1)
## 习题2-8
(15 分)已知升序数组A含有n个非负整数,没有重复元素,设计一个时间上尽可能高效的算法求是否存在两个数和为 k(k>0),如果存在则返回1,不存在则返回0。例:A={1,2,3,7,8,11,15},k=18,则7+11=18,返回1。(1)给出算法的基本设计思想。(2) 根据设计思想,采用C、C++或Java 语言描述算法,关键之处给出注释。(3)说明你所设计算法的时间复杂度和空间复杂度。
<table><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>做题思路</td><td rowspan=1 colspan=1>时间复杂度</td><td rowspan=1 colspan=1>空间复杂度</td><td rowspan=1 colspan=1>预计得分</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>枚举</td><td rowspan=1 colspan=1>O(n2)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>11</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>折半查找</td><td rowspan=1 colspan=1>O(nlogn)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>13</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>散列表</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>O(k)</td><td rowspan=1 colspan=1>15</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>多指针法</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>O(1)</td><td rowspan=1 colspan=1>15</td></tr></table>
注意 A[i] + A[j] == k 时还需要保证i 和 j 不相等,这个条件一定要注意。
<!-- page 66 -->