Blog 8 | 算法·实验报告1(第二章)

 

算法·实验报告1

主题:分治策略——递归+二分法的应用

时间:2019/9/13

1.分治:递归+二分法思路总结

divide-and-conquer(P) {

  if (|P|<=n0)

    return adhoc(P);    //  |P|<=n0表示P的规模不超过阈值n0,可直接求解。

  divide P into smaller subinstances P1, P2, ..., Pk;  //  分解问题 

  for (i=1; i<=k; i++)
    yi=divide-and-conquer(Pi);    //递归解各子问题

  return merge(y1, ..., yk);            //算法Merge(y1, …, yk)表示将子问题的解合成P的解

}

int bsearch(int x, int a[ ], int left, int right){

   if (left >right)

    return -1;

  int middle = (left+right)/2;

  if (x == a[middle])

    return middle;

  if (x < a[middle])

    return bsearch(x, a, left, middle - 1);

  else

    return bsearch(x, a, middle + 1, right); 

}

 

分治法的时间复杂性为:

T(n):O(1)   n = 1

               kT(n/m) + f(n)   n > 1

T(n) = aT(n/b)+ O(nd)

其中设子问题规模为n/m,Divide和Merge的时间为f(n)。

 

2.PTA试题1——实践7-1 二分查找 (20 分)

(1)问题描述:典型的二分搜索法简单例子+比较次数

  题目:

  输入n值(1<=n<=1000)、n个非降序排列的整数以及要查找的数x,使用二分查找算法查找x,

       输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。

  输入格式:

  输入共三行: 第一行是n值; 第二行是n个整数; 第三行是x值。

  输出格式:

  输出x所在的下标(0~n-1)及比较次数。若x不存在,输出-1和比较次数。

  输入样例:

  4
  1 2 3 4
  1

  输出样例:

  0
  2
 

(2)算法描述/代码

  二分搜索+全局变量count统计次数

#include <iostream>
using namespace std;
int count=0;
int binarysort(int arr[],int x,int left,int right){
    //    第一次出错的原因 在该行写:count++ ①
if(left<=right){ count++;
     int i = left; int j = right; int mid = (left+right)/2; if( x == arr[mid] ) return mid; else{ if( x > arr[mid] ){ binarysort(arr,x,mid+1,right); } else{ binarysort(arr,x,left,mid-1); } } } else return -1; } int main(){ int n;//排序总数 int arr[1000];//存放数组 int x;//寻找的数x cin>>n; for(int i =0;i<n;i++){ cin>>arr[i]; } cin>>x; cout<<binarysort(arr,x,0,n-1)<<endl; cout<<count; }

 

(3)算法时间度/空间度分析

  T(n):O(1)   n = 1

                 2T(n/2) + f(n)   n > 1

  T(n) = 2T(n/2)+ O(1)

  ∵log2 2=1

  ∴T(n) = nlogn;

  S(n) = 1;(并没有额外开辟与n有关的空间)

(4)实践中暴露的问题

  第一次出错①:在left<=right之前对count++

  错误原因:当right<left后说明元素不在数组内,并没有进行比较,不应该放在外面。

 

 

3.PTA试题2——实践7-2 改写二分搜索算法 (20 分)

 

(1)问题描述:典型的二分搜索法简单例子+标记位置

 

  设a[0:n-1]是已排好序的数组,请改写二分搜索算法,使得当x不在数组中时,

  返回小于x的最大元素位置i和大于x的最小元素位置j。当搜索元素在数组中时,i和j相同,均为x在数组中的位置。

  输入格式:

  输入有两行:

  第一行是n值和x值; 第二行是n个不相同的整数组成的非降序序列,每个整数之间以空格分隔。

  输出格式:

  输出小于x的最大元素的最大下标i和大于x的最小元素的最小下标j。

  当搜索元素在数组中时,i和j相同。 提示:若x小于全部数值,则输出:-1 0 若x大于全部数值,则输出:n-1的值 n的值

  输入样例:

  在这里给出一组输入。例如:

  6 5
  2 4 6 8 10 12

  输出样例:

  在这里给出相应的输出。例如:

  1 2

(2)算法描述/代码

  二分搜索+标记下标

#include <iostream>
using namespace std;

void binarysort(int arr[],int x,int left,int right,int n){
    if(left<=right){    
    //    int i = left;
    //    int j = right;
        int mid = (left+right)/2;
        if( x == arr[mid] )
            cout<<mid<<" "<<mid<<endl;// i/jreturn mid;
        else{
            if( x > arr[mid] ){
                binarysort(arr,x,mid+1,right,n);
            }
                
            else{
                binarysort(arr,x,left,mid-1,n);
            }
                
        }    
    }
    else {//超出范围内  (left>right            
            if(left>0 && right<n){//找到目的
                cout<<right<<" "<<left<<endl;
            }
             
            else {
                if(right<0){//超出数组元素表示边界
                cout<<-1<<" "<<0<<endl;
            }            
                if(left>n){
                cout<<arr[n-2]<<" "<<arr[n-1]<<endl;
                }                
            }
    }
}
int main(){
    int n;//排序总数
    int arr[1000];//存放数组
    int x;//寻找的数x
    cin>>n;
    cin>>x;
    for(int i =0;i<n;i++){
        cin>>arr[i];
    }    
    binarysort(arr,x,0,n-1,n);

}

(3)算法时间度/空间度分析

  T(n):O(1)   n = 1

                 2T(n/2) + f(n)   n > 1

  T(n) = 2T(n/2)+ O(1)

  ∵log2 2=1

  ∴T(n) = nlogn;

  S(n) = 1;(并没有额外开辟与n有关的空间)

(4)实践中暴露的问题

   判断元素超出数组表示范围过于繁杂,不断二分之后才判断。因为数组有序,直接在一开始(if left<=right)之前与数组两端元素比较即可。

 

4.PTA试题3——实践7-3 两个有序序列的中位数 (20 分) (20 分)

(1)问题描述:典型的二分搜索法例子+难点:子问题中的左右范围

  已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。
  有序序列,的中位数指A(N1)/2​​的值,即第⌊个数(A0​​为第1个数)。

  输入格式:

  输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。

  输出格式:

  在一行中输出两个输入序列的并集序列的中位数。

  输入样例1:

  5
  1 3 5 7 9
  2 3 4 5 6

  输出样例1:

  4

  输入样例2:

  6
  -100 -10 1 1 1 1
  -50 0 2 3 4 5

  输出样例2:

  1

(2)算法描述/代码

  二分搜索,比较对应两段的中值,A>B,则说明中位数在A中值的左侧,在B的右侧。易错点:奇数偶数序列的左侧右侧坐标不同。

 

#include <iostream>
using namespace std;
int search(int arr1[],int arr2[],int L1,int R1,int L2,int R2){
    if(L1<R1 && L2<R2){
        int mid1 = (L1+R1)/2;
        int mid2 = (L2+R2)/2;
    
        if( arr1[mid1] == arr2[mid2] )//
             return arr1[mid1];    
        else if( arr1[mid1]>arr2[mid2] ){
            if( (R1-L1+1)%2==0 )
              return    search(arr1,arr2,L1,mid1,mid2+1,R2);
            else
              return    search(arr1,arr2,L1,mid1,mid2,R2);
                
        }
            
        else{
            if( (R1-L1+1)%2==0 )//偶数序列
        return  ①  search(arr1,arr2,mid1+1,R1,L2,mid2); 

       else return ② search(arr1,arr2,mid1,R1,L2,mid2);//奇数序列 }

    }   
    
  //
if(L1==R1 && L2==R2)(在纸上推演得出 最后范围只剩一个数)
      return arr1[R1]<arr2[R2]?arr1[R1]:arr2[R2];    
}


int main(){
    int N;
    cin>>N;
    int a[100001];
    int b[100001];
    for(int i =0;i<N;i++){
        cin>>a[i];
    }
    for(int i =0;i<N;i++){
        cin>>b[i];
    }
       cout<<search(a,b,0,N-1,0,N-1); 
    
}

(3)算法时间度/空间度分析

  T(n):O(1)   n = 1

                 2T(n/2) + f(n)   n > 1

  T(n) = 2T(n/2)+ O(1)

  ∵log2 2=1

  ∴T(n) = nlogn;

  S(n) = 1;(并没有额外开辟与n有关的空间)

(4)实践中暴露的问题

  错误的思路: 跳出if (L1<R1 && L2<R2) 循环后的函数无法没有返回值。

  错误代码演示:

            if( (R1-L1+1)%2==0 )
                search(arr1,arr2,L1,mid1,mid2+1,R2);
            else
                 search(arr1,arr2,L1,mid1,mid2,R2);

  错误结果分析: 因为①②的地方没有加return 导致当进入到子问题时没有返回值。

                                 在 return arr1[R1]<arr2[R2]?arr1[R1]:arr2[R2];  这个结果作为子问题的return在main函数中得不到return 因此出错。 

 (4)图示分析:(在解题中用于不懂得正确表示下标而耽误了很久,因此制作了图片分析)

 

 (某得会员)

posted @ 2019-09-23 00:32  丸zq  阅读(264)  评论(0编辑  收藏  举报