SEx5-算法基础 参考代码及思路

函数填空题

5-1
本题要求实现折半查找的递归查找操作。

#include <stdio.h>
#include <stdlib.h>
typedef int KeyType;
typedef struct {
          KeyType   *data; /*表基址*/
          int     length;      /*表长*/
}SSTable;
void  CreatSSTable(SSTable *ST);/*有序表创建,由裁判实现,细节不表*/
int  BiSearch(SSTable ST,KeyType e,int low,int high);
int main()
{
   SSTable  ST;
   int n,result,i;
   KeyType e;
   CreatSSTable(&ST);
   scanf("%d",&n);
   for( i=0;i<n;i++)
   {
	scanf("%d",&e);
	result = BiSearch(ST,e,1,ST.length);
	if(result) printf("%d is found\n",e);
	else printf("%d is not found\n",e);
	}
	return 0;
}
int  BiSearch(SSTable ST,KeyType e,int low,int high)
{
   int mid;
   if(
low>high
(6)) return 0;
   mid=(low+high)/2;
   if(ST.data[mid]==e) return  mid;
   if(e<ST.data[mid]) return 
BiSearch(T, e, low, mid-1)
;
   else 
BiSearch(T, e, mid, high)
;
}

5-2
本题要求用冒泡排序将一组整数按增序排序。冒泡排序每次从头到尾扫描待排序列,检查相邻两数的顺序,如果顺序不对就交换。请补全下列冒泡排序的代码。

typedef struct node *nodeptr;
struct node{
   int value;
   nodeptr next;
   /* 一些其他的项,在此忽略 */
};

nodeptr BubbleSort (nodeptr h)
{/* h 是带空头结点的链表的头指针 */
   nodeptr p, q;
   int flag_swap;

   if (!h->next)  return h;
   do{
      flag_swap = 0;
      p = h;
      while (p->next->next){
         if ( 
p->next->value > p->next->next->value
(12) ){
            flag_swap++;
            q = p->next;
            
p->next = q->next
;
            
q->next = p->next->next
;
            
p->next->next=q
;
         }
         else p = p->next;
      }
   } while (flag_swap > 0);
   return h;
}

5-3
以下程序的功能是输入一个正整数n(1<n≤10),再输入n个整数,将它们存入数组a中,再输入1个数x,然后在数组中查找x,如果找到,输出相应的最小下标,否则,输出“Not Found”。请填空。

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int i, index, n, x, a[10];
    scanf("%d", &n);
    for (i = 0; i < n; i++)
        scanf("%d", 
&a[i]
(6));
    scanf("%d", &x);
    
index = -1
;
    for (i = 0; i < n; i++)
        if (a[i] == x) {
            index = i;
            
break
;
        }
    if (index != -1)
        printf("%d\n", index);
    else
        printf("Not Found\n");
    return 0;
}

5-4
有15个已经排好序的数存放在一个数组中,输入一个数,要求用折半查找法找出该数是数组中第几个元素的值。
如果该数不在数组中,则输出无此数。请填空。
变量说明:top,bott为查找区间两端点的下标;loca为查找成功与否的开关变量。

#include <stdio.h>

int main(void)
{
    int N, number, top, bott, min, loca;
    int a[15] = { -3, -1, 0, 1, 2, 4, 6, 7, 8, 9, 12, 19, 21, 23, 50};
    N = 15;
    scanf("%d", &number);
    loca = 0; top = 0; bott = N - 1;
    if ((number < a[0]) || (number > a[N - 1]))
        loca = -1;
    while ((loca == 0) && (top <= bott)) {
        min = 
(bott+top)/2
(6);
        if (number == a[min]) {
            loca = min;
            printf("The serial number is %d\n", loca + 1);
	    break;
        } else if (number < a[min])bott = min - 1;
        else 
top = min
;
    }
    if (
loca == 0
)
        printf("%d isn't in table\n", number);
    return 0;
}

5-6
本程序在数组中同时查找最大元素和最小元素的下标,分别存放在函数main()的max和min变量中。

#include <stdio.h>
void find(int *, int, int *, int *);
int main(void)
{
  int max, min, a[]={5,3,7,9,2,0,4,1,6,8};
  find(
a, 10, &max, &min
(3));
  printf("%d,%d\n", max, min);
  return 0;
}
void find(int *a, int n, int *max, int *min)
{
  int i;
  *max=*min=0;
  for (i = 1; i < n; i++)
  {
    if (a[i] > a [*max]) 
*max = i
 ;
    if (a[i] < a [*min]) 
*min = i
 ;
  }
}

5-7
输入10个成绩,查找最高分并输出。

#include<stdio.h>
int *GetMax(int score[ ], int n);
int main(void)
{
  int i, score[10], *p;
  for(i = 0; i < 10; i++)
      scanf("%d", &score[i]);
  p= 
GetMax(score, 10)
(2)   ;
  printf("%d\n", *p);
  return 0;
}
int *GetMax(int score[ ], int n)
{
      int i, temp, pos = 0;
      temp = score[0] ;
      for(i = 0 ; i < 10 ; i++)
        if(score[i] > temp)
        {  temp = score[i];
           pos = i ; 
        }
      return  
&score[pos]
   ;
}

函数编程题

6-1 归并排序

本题要求实现二路归并排序中的归并操作,待排序列的长度1<=n<=1000。

归并排序思路请参考归并排序详解

本题参考代码:

void Merge(SqList L,int low,int m,int high) {
  int i=low, j=m+1;
  int buf1[m-low+1];//开辟额外空间,分别存储两部分数据
  int buf2[high-m];
  while(i<=m) {
    buf1[i-low] = L.elem[i];
    i++;
  }
  while(j<=high) {
    buf2[j-m-1] = L.elem[j];
    j++;
  }
  i=0,j=0;
  int index = low;
  //进行数据合并,在两个数组中寻找较小的元素,存入L.elem中
  while(i<m-low+1&&j<high-m) {
    L.elem[index++] = buf1[i]<buf2[j]?buf1[i++]:buf2[j++];
  }
  //若仍有数据未合并,则分别处理剩余未合并的两个数组
  while(i<m-low+1) {
     L.elem[index++] = buf1[i++];
  }
  while(j<high-m) {
     L.elem[index++] = buf2[j++];
  }
}

7-1 两个有序序列的中位数

已知有两个非降序序列S1, S2, 求S1与S2归并成一个序列的低位中位数。有序序列A0,A1,⋯,AN−1的中位数指A(N−1)/2的值,即第⌊(N+1)/2⌋个数(A0为第1个数)。

限制:
时间限制: 700 ms
内存限制: 24 MB

思路一:将A和B合并成新的数组
本思路利用最笨的方法去实现,利用排序将两个数组合并成一个数组,合并成有序数组后即可求任意k值,其时间复杂度为 O(m+n), 空间复杂图为O(m+n)。
本题N最大值为2500000,则输入总空间为250000024(byte)/1024/1024=19.07MB,如果再申请额外空间(即额外的O(m+n)空间),会产生内存不足。

思路二:采用归并方法
为了降低空间,本思路采用归并排序的归并算法,利用两个分别指向A和B数组头的指针去遍历数组,然后统计元素个数,直到找到中位数。本算法是对思路一的改进,用一个临时变量保存k值,而不需要合并数组单独存储,节省了存储空间。
其 时间复杂度为O(m+n), 空间复杂度为O(1).

参考代码:

#include <stdio.h>
#include <stdlib.h>
#define MAX_LEN 2500001
int a[MAX_LEN], b[MAX_LEN];
int main() {
  int n,m;
  int i,j;
  scanf("%d",&n);
  for(i=0;i<n;i++) scanf("%d",&a[i]);
  scanf("%d",&m);
  for(i=0;i<m;i++) scanf("%d",&b[i]);

  i=0,j=0;
  int mid = (n+m-1)/2, cur_index = 0;
  int result = 0;
  while(cur_index<=mid&&i<n&&j<m) {
    result = a[i]<b[j]?a[i++]:b[j++];
    cur_index++;
  }
  while(cur_index<=mid&&i<n) {
    result = a[i];
    i++;cur_index++;
  }
  while(cur_index<=mid&&j<m) {
    result = b[j];
    j++;cur_index++;
  }
  printf("%d",result);
}

思路三:采用二分方法
当数据量增大时,线性增长也可能不满足时间限制。由于已知两个序列是非递减的,所以就可以将原问题转变成一个寻找第k小数的问题,于是中位数实际上是第(m+n)/2小的数。因此可以将原题目求中位数转换为求第 k = (m+n)/2的数。

首先假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:>、<和=。

如果A[k/2-1]<B[k/2-1],这表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中。换句话说,A[k/2-1]不可能大于两数组合并之后的第k小值,所以我们可以将其抛弃。上述描述证明:假设A[k/2-1]大于合并之后的第k小值,我假定其为第(k+1)小值。由于A[k/2-1]小于B[k/2-1],所以B[k/2-1]至少是第(k+2)小值。但实际上,在A中至多存在k/2-1个元素小于A[k/2-1],B中也至多存在k/2-1个元素小于A[k/2-1],所以小于A[k/2-1]的元素个数至多有k/2+ k/2-2,小于k,这与A[k/2-1]是第(k+1)的数矛盾。

当A[k/2-1]>B[k/2-1]时存在类似的结论。

当A[k/2-1]=B[k/2-1]时,则第k小的数就是这个相等的数据。由于在A和B中分别有k/2-1个元素小于m,所以m即是第k小的数。

通过上面的分析,本思想可以采用递归的方式实现寻找第k小的数。
确定的返回条件:

  • 如果A或者B为空,则直接返回B[k-1]或者A[k-1];
  • 如果k为1,我们只需要返回A[0]和B[0]中的较小值;
  • 如果A[k/2-1]=B[k/2-1],返回其中一个

本思想效率较高。每次都有一半的元素被删除,即计算中值时每次将范围缩小一半,故而时间复杂度为O(lg(m+n)),由于不需要开辟额外空间,本算法空间复杂度为O(1)。

参考代码:

#include <stdio.h>
#include <stdlib.h>
#define MAX_LEN 2500001
int a[MAX_LEN], b[MAX_LEN];
int findKthMinValue(int* a, int n, int* b, int m, int k) {
    //使得第一个数组的长度永远比第二个小
    if(n>m) return findKthMinValue(b, m, a, n, k);
    if(n == 0) return b[k-1];
    if(k==1) return a[0]>b[0]?b[0]:a[0];
    //将k分为两个部分
	int pa = k / 2>n?n:k/2, pb = k - pa;
    if (a[pa - 1] < b[pb - 1])
		return findKthMinValue(a + pa, n - pa, b, m, k - pa);
	else if (a[pa - 1] > b[pb - 1])
		return findKthMinValue(a, n, b + pb, m - pb, k - pb);
	else
		return a[pa - 1];
}
int main() {
  int n,m;
  int i,j;
  scanf("%d",&n);
  for(i=0;i<n;i++) scanf("%d",&a[i]);
  scanf("%d",&m);
  for(i=0;i<m;i++) scanf("%d",&b[i]);

  printf("%d",result);
  if((n+m)&1) {
    printf("%d", findKthMinValue(a,n,b,m,(n+m)/2+1));
  }else {
    printf("%d", findKthMinValue(a,n,b,m,(n+m)/2));
  }
}

7-2 字符串的冒泡排序 (20 分)

我们已经知道了将N个整数按从小到大排序的冒泡排序法。本题要求将此方法用于字符串序列,并对任意给定的K(<N),输出扫描完第K遍后的中间结果序列。

参考代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LEN 11  //字符串最大长度

//冒泡排序(升序)
void bubbleSort(char (*pt)[MAX_LEN],int n, int k)  //形参pt为指向二维字符数组的指针
{
    int i,j;  //i表示趟数,j表示第i趟两两比较的次数
    char tmp[MAX_LEN];  //临时字符数组
    for(i=0;i<n-1;i++) {

        for(j=0;j<n-1-i;j++)
            if(strcmp(pt[j],pt[j+1]) > 0)  //字符串进行两两大小比较,如果>0,则执行if语句体
            {   //两个字符串进行交换操作
                strcpy(tmp,pt[j]);
                strcpy(pt[j],pt[j+1]);
                strcpy(pt[j+1],tmp);
            }
        if(i==k-1) return;
    }
}
int main()
{
    int i,n,k;

    scanf("%d%d",&n,&k);
    char str[n][MAX_LEN];  //定义二维字符数组

    for(i=0;i<n;i++)
        scanf("%s",str[i]);

    bubbleSort(str, n, k);  //调用排冒泡序函数
    for(i=0;i<n;i++)
        printf("%s\n",str[i]);
    return 0;
}

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值