算法分析-归并排序(合并排序)求解(附代码详解)---例题第3关:找出数组中第 k 个小的元素

为了使大家更好的去理解,去验证,我们这边举一个例题来说明

任务描述

本关任务:对于给定的 n 个元素的数组a[0:n-1],要求从中找出第 k 小的元素。

编程要求

请在右侧编辑器Begin-End处补充代码,完成本关任务,注意需要学生自己获取输入数据再进行操作。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:

 
  1. 10 5 //表示给定10(n)个元素的数组,从中找出第5(k)小的元素
  2. -34 //此行及以下为具体的每个数据
  3. 95
  4. -50
  5. 67
  6. 73
  7. 81
  8. -38
  9. 10
  10. -11
  11. 70

预期输出:第5小的元素是10


分析与思路:

        求第几位最小,那么必然需要先排序

        分治算法两种排序方法:

  1.        快速排序
  2.        归并排序

这里我们使用归并排序详解!

这里图片用的是b站up主做的图

归并排序分为两大步骤:

  • 划分
  • 合并

        首先一个整体数组,进行排序,先一份为二,然后再拆解,直到只剩下一个(在代码中我们这里会用到递归进行拆分判断)

        第二步进行合并,我们需要设置一个空间数组,用来存放合并后的数据(这里看图,看最小一部分,绿色那块就是我们的空间数组),同时设置一个p作为我们的空间数组的下标

        具体,在设置一个left,right。那么看图中。假设进行到如下图示,那么

        left-----》》2的位置(第一行的2)

        right----》》7的位置                        (right可以通过mid+1得到,仔细看right的位置)

        p------》》2的位置(第二行的2)

        那么就是left与right进行比较,如果left小于right,则放入空间数组且p自增一(要移动放入位置)

        反之则反之。

#include <stdio.h>
#include <stdlib.h>

// 合并
void merge(int arr[], int tempArr[], int left, int mid, int right)
{
    
    int l_pos = left;            // 标记左半区第一个未排序的元素
    
    int r_pos = mid + 1;   // 标记右半区第一个未排序的元素(这里就用到了我们前面的mid,来定位)
    
    int pos = left;              // 空间数组元素的下标

    // 合并                        //(right是最右边的元素)
    while (l_pos <= mid && r_pos <= right)    //下标移动完了就结束
    {
        if (arr[l_pos] < arr[r_pos])          // 左半区第一个剩余元素更小
            tempArr[pos++] = arr[l_pos++];    //将元素放到空间数组,pos和l_pos都自增一移动位置
        else                                  // 右半区第一个剩余元素更小
            tempArr[pos++] = arr[r_pos++];    //同上
    }
    //上述可能会留下,没有分配的元素,因为上面判断条件只要有一个移动完了,就结束循环
    //那么也说明剩下的就是排序好的(因为最底层是一个元素,有序,每进行一次调用,都会排序好)
    //直接合并剩余部分就可以了
    // 合并左半区剩余的元素
    while (l_pos <= mid)
        tempArr[pos++] = arr[l_pos++];

    // 合并右半区剩余的元素
    while (r_pos <= right)            //判断语句,有剩余则执行
        tempArr[pos++] = arr[r_pos++];

    while (left <= right)             // 把临时数组中合并后的元素复制回原来的数组  
    {
        arr[left] = tempArr[left];
        left++;
    }
}

// 归并排序--拆分
void msort(int arr[], int tempArr[], int left, int right)
{
    // 如果只有一个元素,那么不需要继续划分
    if (left < right)            //所以这也是一个递归的终止判断,左小于右,则表示有两个或以上数据
    {
    
        int mid = (left + right) / 2;            // 找中间点
        
        msort(arr, tempArr, left, mid);          // 递归划分左半区
        
        msort(arr, tempArr, mid + 1, right);     // 递归划分右半区
        
        merge(arr, tempArr, left, mid, right);   // 合并已经排序的部分
    }
}

// 归并排序入口
void merge_sort(int arr[], int n)        #传入数组和n
{
    // 分配一个辅助数组
    int tempArr[n];                      #设置一个tempArr作为空间数组,存放数据
    // 调用实际的归并排序
    msort(arr, tempArr, 0, n - 1);       #调用归并排序,数组,空间数组,起始位置
}
// 主函数
int main()
{
    int n,k,i;                #设置一个n数量,k第几个,i下标
    scanf("%d %d",&n,&k);    
    int arr[n];
    for (i=0;i<n;i++){        #循环写入数组
        scanf("%d",&arr[i]);
    }
    merge_sort(arr,n);        #调用我们的归并排序函数
    printf("第%d小的元素是%d",k,arr[k-1]);    #打印第几小的元素,下标k-1,因为从零开始
    return 0;
}

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值