为了使大家更好的去理解,去验证,我们这边举一个例题来说明
任务描述
本关任务:对于给定的 n 个元素的数组
a[0:n-1]
,要求从中找出第 k 小的元素。
编程要求
请在右侧编辑器
Begin-End
处补充代码,完成本关任务,注意需要学生自己获取输入数据再进行操作。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:
测试输入:
10 5 //表示给定10(n)个元素的数组,从中找出第5(k)小的元素
-34 //此行及以下为具体的每个数据
95
-50
67
73
81
-38
10
-11
70
预期输出:第5小的元素是10
分析与思路:
求第几位最小,那么必然需要先排序
分治算法两种排序方法:
- 快速排序
- 归并排序
这里我们使用归并排序详解!
这里图片用的是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;
}