【解法1】暴力解:枚举

强化讲义 · 原始转写 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照

## 【解法1】暴力解:枚举

(1) 二重循环i和j扫描数组,当i和j不相等且A[i]+A[j]== k时返回1,如果扫描完都没找到则返回0。(2)伪代码如下:

int ans(int A[], int n, int k)
{
    for (int i=0; i<n; i++)
    for (int j=0; j<n; j++)
    if (i!=j && A[i]+A[j]==k) return 1;
    return 0;
}
(3)时间复杂度:O(n²)空间复杂度:O(1) ## 【解法2】优化解:折半查找 (1) 循环i遍历数组,如果A[i]<=k则在整个数组中折半查找k-A[i],若折半查找能够找到k-A[i]则返回1。如果i遍历完整个链表都无法找到则返回0。(2)伪代码如下: <!-- suspect-code --> > [!warning] 这段代码需要核对(原文第 66 页) > OCR 体检未通过:疑似花括号被 OCR 读成单个字符。扫描件的两栏排版(代码 + 右侧注释)有时会被交错读取,产生看起来合理但语义错误的代码。背之前请对照原文。
//折半查找
int B_Search(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 &lt; A[mid]) R = mid - 1;
        else L = mid + 1;
    }
    return -1;
}
int ans(int A[]int n, int k)
{
    for (int i = 0; i &lt; n; i++)
    int j = B_Search(A, 0, n-1, k-A[i]);
    if (i != j) return 1; // 需要保证 i 和 j 不相等
}
return 0;
1
(3)时间复杂度:O(nlogn)空间复杂度:O(1) ## 【解法3】优化解:散列表 <!-- page 67 --> (1)设置 count 数组表示某个数出现的次数(数组下标 0\~k),循环遍历数组 A 并更新 count数组,遍历i访问 A[i],如果 A[i]<=k 则判断 count[k-A[i]] 已经>0 说明之前 k-A[i]已经出现过,说明A[i]和 k-A[i]都已经找到,输出1。如果最终都没有找到,则输出0。(2)伪代码如下:
int ans(int A[], int n, int k)
{
    int count[k+1] = {0};
    for (int i = 0; i &lt; n; i++)
    {
        if (A[i] &gt; k) break;
        if (count[k-A[i]] &gt; 0) return 1; // 找到A[i]和k-A[i]
        count[A[i]]++;
    } return 0;
}
(3) 时间复杂度:O(n)空间复杂度:O(k) ## 【解法4】优化解:多指针法 (1)设置变量i、j存储数组下标,初始i =0,j = n-1,此前往后循环遍历i,对于每个i从上次的 j 出发往左找直到 A[i] + A[j] <= k,对于这次i的取值只可能是当前的j 能满足 A[i] + A[j] 等于 k,若能够找到 A[i]+ Aj] 等于 k则返回 1,不断重复这个过程直到i和j相遇,如果一直找不到就返回0。(2)伪代码如下:
int ans(int A[], int n, int k)
{
    int j = n-1;
    for (int i = 0; i &lt; n; i++)
    {
        while (i&lt;j &amp;&amp; A[i]+A[j]&gt;k) j--;
        if (i &gt;= j) return 0;
        if (A[i] + A[j] == k) return 1;
    }
}
(3) 时间复杂度:O(n) 空间复杂度:O(1) ## 习题2-9 (13 分)已知升序数组(无重复元素)A、B 都含有n个整数,设计一个时间上尽可能高效的算法求新的数组C 使得C中只存在 A、B中都包含的元素,输出数组C。(1)给出算法的基本设计思想。(2)根据设计思想,采用C、C++或Java 语言描述算法,关键之处给出注释。(3)说明你所设计算法的时间复杂度和空间复杂度。 <!-- page 68 --> <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>O(n)</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>tO(n)</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>13</td></tr></table> 【解法1】暴力解:枚举法1)将数组A中的每个数和数组B中的每个数比较,如果相同就存入数组C,最后输出数组C。2)伪代码如下: <!-- verified-by-hand --> > [!success] 已结合原文第 68 页及 Markdown 上下文人工校对 > 两个指针移动分支被移出 while,导致越界;相等时 j++ 也脱离条件。
int ans(int A[], int B[], int n)
{
    int C[n], i = 0, j = 0, k = 0;
    while (i &lt; n &amp;&amp; j &lt; n)
    {
        if (A[i] == B[j]) { C[k++] = A[i]; i++; j++; }
        else if (A[i] &lt; B[j]) i++;
        else j++;
    }
    output(C, k);
    return k;
}
(3)时间复杂度:O(n²)空间复杂度:O(n) ## 【解法2】优化解:多指针法 指针。(2)伪代码如下: <!-- suspect-code --> > [!warning] 这段代码需要核对(原文第 68 页) > OCR 体检未通过:花括号不平衡。扫描件的两栏排版(代码 + 右侧注释)有时会被交错读取,产生看起来合理但语义错误的代码。背之前请对照原文。
int ans(int A[], int B[], int n)
{
    int C[n];
    int i = 0, j = 0, k = 0; //i、j作为遍历A、B的下标
    while (i&lt;n &amp;&amp; j&lt;n) //直到有一个数组遍历完
    {
        if (A[i] == B[j]) //数组A和B中都存在的元素存入C中
        C[k++] = A[i++];
        j++;
    }
    if (A[i] &lt; B[j]) i++; // A[i]更小,i指针后移
    if (A[i] &gt; B[j]) j++; // B[j]更小,j指针后移
}
输出数组C
}
(3)时间复杂度:O(n)空间复杂度:O(n) <!-- page 69 --> ## 习题2-10 (13分)将一个数组最开始的若于元素搬到数组的末尾,称之为数组的旋转。已知数组A为某个有序数组旋转后的结果(无重复值),设计一个时间上尽可能高效的算法求该旋转数组的最小元素。例:数组A = {3,4, 5,6,1,2} 为有序数组{1,2, 3,4, 5,6}的—个旋转数组,该数组的最小值为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(nlogn)</td><td rowspan=1 colspan=1>O(logn)</td><td rowspan=1 colspan=1>9</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>枚举 2</td><td rowspan=1 colspan=1>O(n)</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(logn)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1></td></tr></table> 【解法1】暴力解:快速排序1)对数组A快速排序然后输出最小值A[0]。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[j] &gt;= pivot) j--; // 从右找第—个 &lt;pivot 的数
        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)
{
    Qsort(A, 0, n-1);
    return A[0];
}
(3)时间复杂度:O(nlogn)空间复杂度:O(logn) <!-- page 70 --> (1)遍历一趟数组,依次判断每个元素是否比当前已存在的最小值A\_min更小,如果是则更新当前最小值。遍历完数组之后输出A\_min。(2)伪代码如下:
int ans(int A[], int n)
{
    int A_min = A[0];
    for (int i = 1; i &lt; n; i++)
    A_min = min(A[i], A_min);
    return A_min;
}
(3)时间复杂度:O(n) 空间复杂度:O(1) ## 【解法3】优化解:折半查找 (1) 对数组进行折半查找,如果L == R或者A[mid]比A[mid-1]小那就说明已经找到最小值所在,返回A[mid]。每次二分,判断mid 所在区间是否包含最小值。由于数组整体是旋转有序数组: X 若 A[mid] <= A[R],说明右半部分有序,最小值一定在左半部分(含 mid)。 若 A[mid]> A[R],说明最小值一定在右半部分(不含 mid)。(2)伪代码如下: <!-- suspect-code --> > [!warning] 这段代码需要核对(原文第 70 页) > OCR 体检未通过:花括号不平衡;中文出现在代码行但缺注释符。扫描件的两栏排版(代码 + 右侧注释)有时会被交错读取,产生看起来合理但语义错误的代码。背之前请对照原文。
int B Search(int A[], int L, int R)
{ int mid;
    while (L &lt; R)
    { 如果L&gt;R则退出循环
        mid = (L+R)/2; // mid取中间数,向下取整
        if (A[mid] &lt;= A[R]) R = mid; //取左一半
        else L = mid + 1; //取右一半
    }
    return L;
    int ans(int A[], int n)
    {
        int i = B_Search(A, 0, n-1);
        return A[i];
    }
(3)时间复杂度:O(logn)空间复杂度:O(1) 因为本题比较元素不是要查找到x的位置,而是根据某一种条件来缩小区间,所以用到的是折半查找的写法2。 ## 习题2-11 <!-- page 71 --> (15 分) 已知—个整数升序序列 A = {a0, a1, . . . , an−1},其中 0 ≤ ai < n (0 ≤ i < n)。连续子序列是从原序列中选取的一段连续的元素构成的序列(至少包含一个元素),如{a0,a1}、{a0}都是连续子序列。设计一个在时间上尽可能高效的算法,输出元素之和<=k的连续子序列个数。例:A = {1, 2, 3, 4, 5},n = 5,k = 6,则符合要求的连续子序列为 {1}、{2}、{3}、{4}、{5}、{1,2}、{2, 3}、{1,2, 3},共有8 个,返回 8。(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>O(n)</td><td rowspan=1 colspan=1>12</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> ## 【解法1】暴力解:枚举 (1)两层循环枚举,外层i枚举连续子序列的左边界,内层j枚举右边界,判断连续子序列A[i\~j] 是否满足元素之和 <= k(我们可以预先设置一个数组S,S[i+1] = A[0] + A[1] + ... +A[i],只需要判断 S[j+1] - S[i]是否 <=k 即可),如果是则计数器 count 加 1,最后输出count。(2)伪代码如下:
int ans(int A[], int n, int k)
{
    int count = 0;
    int S[n + 1] = {0};
    for (int i = 0; i &lt; n; i++) S[i + 1] = S[i] + A[i];
    for (int i = 0; i &lt; n; i++)
    for (int j = i; j &lt; n; j++)
    if (S[j + 1] - S[i] &lt;= k) count++;
    return count;
}
(3)时间复杂度:O(n²)空间复杂度:O(n) ## 【解法2】优化解:多指针法 (1) 用指针 i, j 维护—个窗口,保证其和 current sum <= k。j 作为右边界向右移动,同时累加窗口内的元素和。当窗口和超过k时,移动左边界来缩小窗口,直到和再次满足条件。每当j移动到新位置,都将以j为右边界、且和小于等于k的子序列个数加到累加和count里,这些子序列的起点就是从i到j,个数为j-i+1。最终得到的累加和 count 就是所有符合条件的 <!-- page 72 -->
int ans(int A[], int n, int k)
{
    int count = 0;
    int sum = 0;
    int i = 0; //窗口左边界
    for (int j = 0; j &lt; n; j++) // j 是窗□右边界
    {
        sum += A[j];
        { while (sum &gt; k)
            sum -= A[i];
            i++;
        }
        count += (j - i + 1);
    }
    return count;
}
(3) 时间复杂度:O(n) 空间复杂度:O(1) ## 习题2-12 (13 分) 已知两个非负整数序列 A = {a0, a1, . .., an−1}、B = {b0, b1, .. . , bn−1},求出有多少个数对{ai, bi}使得ai+bj <=5n,设计一个在时间上尽可能高效的算法,有多少个这样的数对。例:A= <2,10,8>,B = <8,12,1>,n = 3,<2,8>、<2,12>、<2,1>、<10,1>、<8,1> 共满足要求的数对,返回5。(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>秀</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(n)</td><td rowspan=1 colspan=1>13</td></tr></table> ## 【解法1】暴力解:枚举 (1) 两层循环i 和 j 遍历数组(保证 i < j),如果 A[i] + B[j] <= 5n 则计数器 t+1,遍历完后返回t。(2)伪代码如下:
int ans(int A[], int B[], int n)
{
    int t = 0;
    for (int i = 0; i &lt; n; i++)
    for (int j = 0; j &lt; n; j++)
    if (A[i] + B[j] &lt;= 5 * n) t++;
    return t;
}
<!-- page 73 --> (3)时间复杂度:O(n²)空间复杂度:O(1)