【解法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 &lt; n; i++)
    count[A[i] + n]++;
    int sum = 0; KX //累计到目前为止已经是第几小的数了
    for (int i = 0; i &lt; 2*n+1; i++)
    {
        sum = sum + count[i];
        if (sum &gt; 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 &gt;= R) return; //区间≤1,返回
    int i = L, j = R;
    int pivot = A[L]; //选首元素为基准
    不断交换左边比pivot 大的数和右边比 pivot 小的数
    while (i &lt; j)
    {
        while (i &lt; j &amp;&amp; A[j] &gt;= pivot) j--; // 从右找第—个 ≤pivot 的数
        if (i &lt; j) swap(A[i], A[j]);
    }
    //将基准值A[L] 与A[i]交换
    swap(A[L], A[i]);
    if (L &lt;= n/2 &amp;&amp; i-1 &gt;= 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 &gt;= R) return; //区间≤1,返回
    int i = L, j = R;
    int pivot = A[L]; // 选首元素为基准
    // 不断交换左边比 pivot 大的数和右边比 pivot 小的数
    while (i &lt; j)
    {
        while (i &lt; j &amp;&amp; A[i] &lt;= pivot) i++; // 从左找第个 &gt;pivot 的数
        if (i &lt; 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&lt;0; i&lt;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&gt;=R) return; // 区间≤1,退出
    int pivot, i=L, j=R; // i,j 为左右指针
    pivot=k;
    while (i&lt;j)
    {
        while (i&lt;j &amp;&amp; A[j]&lt;=pivot) j--; // 右边&lt;=则不处理
        while (i&lt;j &amp;&amp; A[i]&gt;=pivot) i++; //左边&gt;=则不处理
        if (i&lt;j) swap(A[i], A[j]); /7A[j]&gt;k,A[i]&lt;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 &lt; 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 &lt; 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 &lt;= R) // 区间非空则继续
        {
            mid = (R + L) / 2; //防溢出
            if (A[mid] == x)
            return mid; //找到了,立即返回
            else
            if (x &gt; 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 &lt; n; i++)
    for (int j = 0; j &lt; 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&lt;n &amp;&amp; j&lt;n)
    {
        D_min = min(D_min, abs(a[i] - b[j]));
        if (a[i]&lt;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 -->