zoukankan      html  css  js  c++  java
  • 算法导论2-9章补充几道题

    本篇博文意在对前几章中遗漏的,本人觉得有意思的习题当独拿出来练练手。

    1、习题2-4,求逆序对,时间复杂度要求Θ(nlgn)

    定义:对于一个有n个不同的数组A, 当i<j时,存在A[i]>A[j],则称对偶(i, j)为A的一个逆序对。

    譬如:<2,3,8,6,1>有5个逆序对。

    解题思路:归并排序的思想:逆序对的数量=左区间的逆序对+右区间的逆序对+合并的逆序对

    代码如下:

     1 #include <iostream>
     2 #include <vector>
     3 using namespace std;
     4 
     5 int MergeInversion(int arr[], int nLeft, int nRight);
     6 int Merge(int arr[], int p, int q, int r);
     7 
     8 //用归并排序找逆序对
     9 int MergeInversion(int arr[], int nLeft, int nRight)
    10 {
    11     int nInversion = 0;
    12     if (nLeft < nRight) {
    13         int nMid = (nLeft+nRight) >> 1;
    14         nInversion += MergeInversion(arr, nLeft, nMid);
    15         nInversion += MergeInversion(arr, nMid+1, nRight);
    16         nInversion += Merge(arr, nLeft, nMid, nRight);
    17     }
    18     return nInversion;
    19 }
    20 
    21 int Merge(int arr[], int p, int q, int r)
    22 {
    23     int n1 = q - p + 1;
    24     int n2 = r - q;
    25     int *left = new int[n1];
    26     int *right = new int[n2];
    27 
    28     int i,j;
    29     for (i = 0; i < n1; i ++)
    30         left[i] = arr[p+i];
    31     for (j = 0; j < n2; j ++)
    32         right[j] = arr[q+1+j];
    33 
    34     i = 0;
    35     j = 0;
    36     int k = p;
    37     int nInverCount = 0; //逆序对的数目
    38 
    39     while (i < n1 && j < n2) {
    40         if (left[i] <= right[j]) 
    41             arr[k++] = left[i++];
    42         else {//假如左边子序列的数大于右子序列,则逆序对数为n1 - i;
    43             arr[k++] = right[j++];
    44             nInverCount += (n1 - i);
    45         }
    46     }
    47     while (i < n1)
    48         arr[k++] = left[i++];
    49     while (j < n2)
    50         arr[k++] = right[j++];
    51     
    52     delete left;
    53     left = NULL;
    54     delete right;
    55     right = NULL;
    56     
    57     return nInverCount;
    58 }
    59 
    60 int main()
    61 {
    62     int arr[] = {2,3,8,6,1};
    63     int len = sizeof(arr)/sizeof(arr[0]);
    64 
    65     int *arrT = new int[len];
    66     
    67 
    68     cout << MergeInversion1(arr, 0, 4, arrT) << endl;
    69     for (int i = 0; i < 5; i++)
    70         cout << arrT[i] << " ";
    71     return 0;
    72 }

    2、习题9.3-7,设计一个O(n)时间的算法,对于一个给定的包含n个互异元素的集合S和一个正整数 k<=n,该算法能够确定S中最接近中位数的k个元素。

    譬如数组:<5,7,10,3,6,2,8,9,1,11,12>,中位数为6,如果k=3,最接近6的3个元素是<5,7,4>或<5,7,8>;如果k=4,则最接近6的4个元素是<5,7,4,8>。

    解题思路:

    1)通过Select()得到A的中位数 --->O(n)

    2)计算A中每个数到中位数的差值作为数组D ,并增加D的一个副本D‘ --->O(n)

    3)通过Select()得到D’中第k大的数 ---> O(n)

    4)依次遍历D中的数,判断比k小的k个数,即为所求 ---> <O(n)

    代码如下:

     1 #include <iostream>
     2 #include <cassert>
     3 #include <cmath>
     4 using namespace std;
     5 
     6 int* K_Select(int arr[], int nLen, int k);
     7 int Select(int arr[], int nLeft, int nRight, int nMin);
     8 int Partition(int arr[], int p, int r);
     9 void Swap(int &n, int &m);
    10 
    11 int* K_Select(int arr[], int nLen, int k)
    12 {
    13     assert(k <= nLen);
    14     
    15     int *pDis = new int[nLen-1]; //the distance of every bit and median
    16     int *pDis_cpy = new int[nLen-1];
    17     int *pKArr = new int[k]; //return k_array
    18 
    19     int nMedian = Select(arr, 0, nLen-1, nLen/2); //get the median of arr
    20     int iMedian;
    21     int nDLen = 0;
    22     for (int i = 0; i < nLen; i ++) {
    23         if (arr[i] != nMedian)
    24             pDis[nDLen++] = abs(arr[i]-nMedian); //get the distance using abs()
    25         else
    26             iMedian = i; //get the index of median
    27     }
    28     memcpy(pDis_cpy, pDis, sizeof(int)*(nLen-1)); //copy the distance    
    29     int nKMedian = Select(pDis_cpy, 0, nDLen-1, k); //get the k-th minimum distance between median and every bit
    30     
    31     delete pDis_cpy;
    32     pDis_cpy = NULL;
    33 
    34     int ik = 0;
    35     for (int i = 0; ik < k && i < nDLen; i ++) {
    36         if (pDis[i] <= nKMedian) {
    37             if (i < iMedian) //judge the index of array and the index of median
    38                 pKArr[ik++] = nMedian - pDis[i];
    39             else
    40                 pKArr[ik++] = nMedian + pDis[i];
    41         }
    42     }
    43 
    44     delete pDis;
    45     pDis = NULL;
    46 
    47     return pKArr;
    48 }
    49 
    50 int Select(int arr[], int nLeft, int nRight, int nMin)
    51 {
    52     assert(nLeft <= nRight);
    53     assert(nMin <= nRight-nLeft+1);
    54 
    55     if (nLeft == nRight)
    56         return arr[nLeft];
    57     int nMid = Partition(arr, nLeft, nRight);
    58     int k = nMid - nLeft + 1;
    59     if (k == nMin)
    60         return arr[nMid];
    61     else if (k > nMin)
    62         return Select(arr, nLeft, nMid-1,nMin);
    63     else
    64         return Select(arr, nMid+1, nRight, nMin-k);
    65 }
    66 
    67 int Partition(int arr[], int p, int r)
    68 {
    69     assert(p <= r);
    70 
    71     int nTemp = arr[r];
    72     int i = p - 1, j = p;
    73     while(j < r) {
    74         if (arr[j] <= nTemp) {
    75             Swap(arr[i+1], arr[j]);
    76             i ++;
    77         }
    78         j ++;
    79     }
    80     Swap(arr[i+1], arr[r]);
    81     return i+1;
    82 }
    83 
    84 void Swap(int &n, int &m)
    85 {
    86     int t = n;
    87     n = m;
    88     m = t;
    89 }
    90 
    91 int main()
    92 {
    93     int arr[] = {5,7,10,3,6,2,8,9,4,1,11,12};
    94     int *karr = K_Select(arr, 12, 4);
    95     for (int i=0; i < 4; i ++)
    96         cout << karr[i] << " ";
    97     return 0;
    98 }

    3、习题9.3-8,求两个有序数组的中位数?

    题目:设X[1...n]和Y[1...n]为两个数组,每个都包含n个有序的元素。请设计一个O(lgn)时间的算法来找出数组X 和Y中所有2n个元素的中位数。

    譬如:X:<1,2,3,4,7,9>; Y:<2,5,6,7,10,11>; 中位数为数组Y中的6.

    解题思路:二分查找思想--->O(lgn)

    1)如果两个数组均只有一个元素,则返回数组中的较小值。

    2)两个数组有2n个元素,则中位数位于n index,小于中位数的元素个数为n-1;

    3)如果当前找到中位数在数组X中的标号为 K,则在数组Y的标号为:n-K-2 < index <= n-K-1;

    4)如果当前在数组X中找到的中位数 X[K]<= Y[n-K-2],说明该数小了,应该在X的右区间[mid+1, right]继续找,反之,如果X[K] > Y[n-K-1],说明该数大了,应该在X的左区间[left, mid-1]继续找。

    5)如果重复该过程,没有在X中找到该中位数,则采用同样的方法在Y中找。

    代码如下:

     1 #include <iostream>
     2 #include <cassert>
     3 using namespace std;
     4 
     5 int SelectMedian(int *arrA, int *arrB, int left, int right)
     6 {
     7     assert(NULL != arrA);
     8     assert(NULL != arrB);
     9     
    10     int nLen = right-left+1;
    11 
    12     while (left <= right) {
    13         int mid = (left+right)/2;
    14 
    15         if (mid == nLen-1 && arrA[mid] <= arrB[0]) //only one element
    16             return arrA[mid];
    17         else if (mid < nLen-1) {
    18             if(arrA[mid] <= arrB[nLen-mid] && arrA[mid] > arrB[nLen-1-mid]) //K = nLen-1-K note:from 0 index
    19                 return arrA[mid];
    20             else if (arrA[mid] <= arrB[nLen-1-mid]) //if the element is small, find it in right region.
    21                 left = mid + 1;
    22             else         //if the element is big, find it in left region.
    23                 right = mid - 1;
    24         }
    25     }
    26     return -1;
    27 }
    28 
    29 //find the median of two array: arrA and arrB
    30 //return the median of two array
    31 void TwoArrMedian(int *arrA, int *arrB, int nLen)
    32 {
    33     assert(NULL != arrA);
    34     assert(NULL != arrB);
    35     
    36     int nMedian;
    37     if (SelectMedian(arrA, arrB, 0, nLen-1) == -1) //not find
    38         nMedian = SelectMedian(arrB, arrA, 0, nLen-1);
    39     
    40     cout << nMedian << endl;
    41 }
    42 
    43 int main()
    44 {
    45     int a[] = {1,2,3,4,8,9,13};
    46     int b[] = {2,5,6,7,10,11,12};
    47 
    48     TwoArrMedian(a, b, 7);
    49     return 0;
    50 }

    我的公众号 「Linux云计算网络」(id: cloud_dev),号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。

  • 相关阅读:
    查看whl包名是否满足系统的条件的命令,以此解决whl包出现“is not a supported wheel on this platform”错误提示的问题
    C++-文件输入输出流
    C++-PTA-6-7-1 地下迷宫探索
    C++-PTA-时钟模拟
    数据结构-深入虎穴-树的应用
    数据结构-二叉树-(先序|后序)+中序求(后序|先序)笔记
    C++-课后习题-日期类DATE时间类Time
    C++-课后习题-学生类
    数据结构-串数组广义表笔记
    数据结构-PTA-银行业务队列简单模拟
  • 原文地址:https://www.cnblogs.com/bakari/p/4856141.html
Copyright © 2011-2022 走看看