《算法分析与设计》练习6

本文详细探讨了数组合并、归并排序、寻找第k小元素、计算中位数、解决棋盘覆盖问题及大整数除法等算法。通过C语言实现,这些算法展示了如何高效地处理有序数组、排序、查找和数学运算,时间复杂度均为O(n)。通过对每个问题的分析和代码展示,读者可以深入理解这些经典算法的原理和应用。
摘要由CSDN通过智能技术生成

目录

A 数组合并

B 归并排序

C 第k小元素

D 找中位数

E 棋盘覆盖问题

F 大整数除法


A 数组合并

题目描述

编写一个程序,将两个有序数组合并成一个更大的有序数组,要求时间复杂度为O(n)。

输入

多组数据输入,每组输入包括两行,每行第一个数字为数组长度n,然后输入n个有序整数。

输出

输出合并后的数组(升序),每组输出用一个空行隔开。

样例输入 Copy

3 1 3 5

3 2 4 6

2 1 2

4 3 4 5 6

样例输出 Copy

1 2 3 4 5 6

1 2 3 4 5 6

分析:将两个有序数组 合并成 一个有序数组

我们可以专门开辟一个 数组 来存放这个 合并后的  数组

用一个for循环即可实现  数组合并。

主要思想就是,两个有序数组之间的比较,谁小谁就先放入,如果某一有序数组没值了,就把另一个有序数组剩下的值放入

具体看代码实现

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int main (){

int a[200];

int b[200];

int c[200];

int al;

int bl;

while(~scanf("%d",&al)){

    for(int i=0;i<al;i++)

        scanf("%d",&a[i]);

    scanf("%d",&bl);

    for(int i=0;i<bl;i++)

        scanf("%d",&b[i]);

    merge(a,b,c,al,bl);

    for(int i=0;i<al+bl;i++)

        printf("%d ",c[i]);

        

        printf("\n");

        printf("\n");

}



return 0;

}

void merge(int a[],int b[],int c[],int al,int bl){

int i=0;

int j=0;

int k=0;

while(i<al&&j<bl){

    if(a[i]<b[j])

        c[k++]=a[i++];

    else

        c[k++]=b[j++];



}

while(i<al&&j>=bl){

      c[k++]=a[i++];

}



while(j<bl&&i>=al){

      c[k++]=b[j++];

}

}

B 归并排序

题目描述

编写一个程序,使用分治策略实现二路归并排序(升序)。

输入

多组输入,每组第一个数字为数组长度,然后输入一个一维整型数组。

输出

输出排序之后(升序)的一维整型数组,每组输出占一行。

样例输入 Copy

6 1 8 6 5 3 4

5 12 42 2 5 8

样例输出 Copy

1 3 4 5 6 8

2 5 8 12 42

分析:要用到上一题的两两归并,归并排序实际上就是,先分治,再不断两两归并就得到了整个排序

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int main (){

int c[200];

int d[200];

int cl;



while(~scanf("%d",&cl)){

    for(int i=0;i<cl;i++)

        scanf("%d",&c[i]);



  sort(c,d,0,cl-1);

    for(int i=0;i<cl;i++)

        printf("%d ",d[i]);



        printf("\n");



}



return 0;

}

void merge(int c[],int d[],int s,int m,int t){

int i=s;

int j=m+1;

int k=s;



while(i<=m&&j<=t){

    if(c[i]<c[j])

        d[k++]=c[i++];

    else

        d[k++]=c[j++];



}

while(i<=m&&j>t){

      d[k++]=c[i++];

}

while(j<=t&&i>m){

      d[k++]=c[j++];

}

}



void sort(int c[],int d[],int s,int t){

if(s<t){

   int m=(s+t)/2;

   sort(c,d,s,m);

   sort(c,d,m+1,t);

   merge(c,d,s,m,t);

   copy(c,d,s,t);



}



}



void copy(int c[],int d[],int s,int t){

for(int i=s;i<=t;i++)

    c[i]=d[i];



}

C 第k小元素

题目描述

输入一个整数数组,请求出该数组的第k小元素。要求时间复杂度为O(n)。

输入

每组输入包括两行,第一行为一个整数数组,两个数字之间用空格隔开;第二行为k值。数组中元素个数小于10^9。

输出

输出第k小元素的值。

样例输入 Copy

2 5 6 1 8 7 9

2

样例输出 Copy

2

分析:此题乍一看,想直接排序,再找。

但是好像也可以用分区来写。我们先引用 快排里的patition方法进行分区

 if   分区一次之后,k值比前面的值个数小,那么要找的数肯定就在前面的区域

如果大,肯定就在后面的区域里。当然,如果相等,那么肯定就是这个数咯。

每次分区找数时,根据比较k的大小,要进行k的更新

if  k小于前面值的个数,那么依然可以继续递归前面区域的第k小值。

else 就要递归后面区域的第 k-j 小值。

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int main (){



int n;

int a[200];

int k;

char c;

while(~scanf("%d",&a[0])){

        int n=0;

        while(scanf("%d",&a[++n])){

            if(c=getchar()=='\n') break;

        }



    scanf("%d",&k);

    int z=solve(a,0,n,k);

    printf("%d",z);

    printf("\n");

}



return 0;

}



int patition(int a[],int p,int q){

    int j,i=p;

    int x=a[p];

    for(j=p+1;j<=q;j++)

    {

        if(a[j]<x){

            i++;

            swap(a,i,j);

        }

    }



swap(a,i,p);

return i;



}



int solve(int a[],int p,int q,int k){

    if(p==q) return a[p];

    if(p<q)

    {

       int i=patition(a,p,q);

       int j=i-p+1;

       if(j==k) return a[i];

        if(j>k) return solve(a,p,i,k);

        if(j<k) return solve(a,i+1,q,k-j);

    }



}



void swap(int a[],int i,int j){

int temp=a[i];

a[i]=a[j];

a[j]=temp;

}

D 找中位数

题目描述

请设计一个算法,不排序,快速计算出一个无序数列的中位数。 时间复杂度要求为O(n)。

如果有奇数个元素,中位数则是数组排序后最中间的那个数字。

如果是偶数个元素,中位数则是数组排序后最中间两个元素的平均值。

输入

有多组输入,每组输入的第一行为n(1<=n<=1e5),表示该数列的元素个数。

第二行为n个整数组成的无序数列

输出

每组样例输出一行,表示该无序数列的中位数。

若为偶数,请保留三位小数

若为奇数,直接输出

样例输入 Copy

5

5 3 2 1 4

样例输出 Copy

3

分析:这题在做过找第k小元素之后就变得很简单。

        首先中位数,我们可以直接得出下标,将其作为要找的第k小元素即可

当然啦,考虑复杂点的,就是要求两个中位数,再求出其两数的中位数。

为了省精力,我就直接在上面改了,嘿嘿嘿

代码实现:c语言

#include <stdio.h>

#include <stdlib.h>



int main (){



int n;

int a[200000];





while(~scanf("%d",&n)){

    for(int i=0;i<n;i++)

    scanf("%d",&a[i]);



    if(n%2==1)

    {

       int k=n/2+1;

int z=solve(a,0,n-1,k);

    printf("%d",z);

    printf("\n");

    }

    else{

        int k1=n/2;

        int k2=n/2+1;

        double z1=solve(a,0,n-1,k1);

        double z2=solve(a,0,n-1,k2);

        double z=(z1+z2)/2;

         printf("%.3lf",z);

         printf("\n");

    }





}





return 0;

}







int patition(int a[],int p,int q){

    int j,i=p;

    int x=a[p];

    for(j=p+1;j<=q;j++)

    {

        if(a[j]<x){

            i++;

            swap(a,i,j);

        }

    }



swap(a,i,p);

return i;



}



int solve(int a[],int p,int q,int k){

    if(p==q) return a[p];

    if(p<q)

    {

       int i=patition(a,p,q);

       int j=i-p+1;

       if(j==k) return a[i];

        if(j>k) return solve(a,p,i,k);

        if(j<k) return solve(a,i+1,q,k-j);

    }







}





void swap(int a[],int i,int j){

int temp=a[i];

a[i]=a[j];

a[j]=temp;

}

E 棋盘覆盖问题

题目描述

在一个n×n (n = 2k)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。

在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

输入

多组测试用例,每组测试用例包括两部分,

第一部分为方格的宽度n,

第二部分则为方格,特殊方格为-1,其他方格为0。

输出

输出覆盖后的方案

样例输入 Copy

4

-1 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

样例输出 Copy

-1 2 4 4

2 2 1 4

3 1 1 5

3 3 5 5

分析:棋盘覆盖问题也是一个分区的问题,主要是把盘不断的分成四个小正方形

然后逐个分析,这里要注意那个if ..else 语句都是并列的。只要一个符合,那么其他三个都不符合,

也就是要覆盖。

假设,等等我们就看样例吧,我们首先总是以左上角为标准坐标,然后把这个盘分成四个相当的小正方形。

然后这么一个特殊方格在第一个方框区域内(先左后右,先上后下),也就是-1,于是我们把剩下三个方框区域靠近

盘最中心的地方涂了颜色。刚好构成一个L。接着,我们继续递归,在第一个正方形方框中,我们继续以左上角为标准坐标

同时以-1为特殊方格,其他三个正方形中,分别以其左上角为标准坐标,并且以最靠近中心的涂了颜色的格子为特殊方格。

......

哈哈哈哈,具体请看代码,看我直接手写。

代码分析:c语言

#include <stdio.h>

#include <stdlib.h>



int board[200][200];

int title=0;

int main (){

int n;

while(~scanf("%d",&n)){

    int th;

    int tl;

    for(int i=0;i<n;i++)

        for(int j=0;j<n;j++)

       {

          scanf("%d",&board[i][j]);

          if(board[i][j]==-1){

          th=i;

          tl=j;

          }



       }

    boardcheck(0,0,th,tl,n);

    for(int i=0;i<n;i++)

        for(int j=0;j<n;j++)

        {

              printf("%d ",board[i][j]);

             if((j+1)%n==0)

                printf("\n");

        }

        title=0;

}



return 0;

}



void boardcheck(int bh,int bl,int th,int tl ,int size){

    if(size==1) return;

int t=++title;

int s=size/2;

if(th<s+bh&&tl<s+bl)

{

    boardcheck(bh,bl,th,tl,s);

}

else{

    board[s+bh-1][s+bl-1]=t;

    boardcheck(bh,bl,s+bh-1,s+bl-1,s);

}



if(th>=s+bh&&tl<bl+s){

    boardcheck(bh+s,bl,th,tl,s);

}

else{

    board[bh+s][bl+s-1]=t;

    boardcheck(bh+s,bl,bh+s,bl+s-1,s);

}

if(th<s+bh&&tl>=bl+s){

    boardcheck(bh,bl+s,th,tl,s);

}

else{

    board[bh+s-1][bl+s]=t;

    boardcheck(bh,bl+s,bh+s-1,bl+s,s);

}

if(th>=s+bh&&tl>=s+bl){

    boardcheck(s+bh,s+bl,th,tl,s);



}

else{

    board[s+bh][s+bl]=t;

    boardcheck(s+bh,s+bl,s+bh,s+bl,s);

}

}

F 大整数除法

题目描述

使用分治算法实现两个大整数相乘。

输入

两个十进制大整数,满足每一个整数长度为2^n且两个大整数的长度相等。(多组数据)

输出

两个大整数的乘积。

样例输入 Copy

1234 5678

样例输出 Copy

7006652

分析:大整数除法原理就是把两乘数分治,分解为加法进行运算

具体请看代码:

直接手写

代码分析:c语言

#include <stdio.h>

#include <stdlib.h>



int main (){

int x;

int y;

while(~scanf("%d %d",&x,&y)){

        int n=0;

        int i=1;

while(i<=x){

    i*=10;

    n++;

}

    int z=dacheng(x,y,n);

  printf("%d",z);

  printf("\n");



}



return 0;

}





int dacheng(int x,int y,int n){



   int sign2=sign(x)*sign(y);

    x=abs(x);

    y=abs(y);

   if(x==0||y==0) return 0;

   if(n==1) return sign2*x*y;



   int a=x/pow(10,n/2);

   int b=x-a*pow(10,n/2);

   int c=y/pow(10,n/2);

   int d=y-c*pow(10,n/2);

   int ac=dacheng(a,c,n/2);

   int bd=dacheng(b,d,n/2);

   int abcd=dacheng(a-b,d-c,n/2)+ac+bd;



   return sign2*(ac*pow(10,n)+abcd*pow(10,n/2)+bd);

}



int sign(int x){

if(x<0) return -1;

return 1;



}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值