题目描述
给定一个长度为 n的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k个数。
输入格式
第一行包含两个整数 n 和 k。
第二行包含 n个整数(所有整数均在 1∼1091∼109 范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第 k小数。
数据范围
1≤n≤100000,
1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3
关于右移一位等于除以二的操作:
右移一位是将二进制的数字进行右移,这是因为右移一位等价于将一个整数除以 2,而在计算机中,右移操作比除法运算更高效。
首先,我们需要知道计算机中的数是按补码存放的,正数的补码和源码一致,负数的补码为源码取反再加1,还有前面的符号位,例如(为了简单,用4个bit存放一个小整数):
5 // 源码:0101 补码:0101
-5 // 源码:1101 补码:1011 注:符号位不参与变换
计算机中右移一位和除以二的区别_为什么右移一位相当于除2_柚子树cc的博客-优快云博客
归并排序:先不断划分左右数组,直到分成只有一个元素的区间为止,之后再合并,并排序
在这段代码中,merge_sort(q, l, mid) 和 merge_sort(q, mid + 1, r) 是在计算中间索引 mid 之后立即进行的递归调用。
这样的递归调用将会不断拆分原始数组 q 的左右两个子数组,直到每个子数组都只有一个元素或没有元素(即满足 l>=r 的终止条件)为止。
if(l>=r)return;
int mid=l+r>>1;
merge_sort(q,l,mid),merge_sort(q,mid+1,r); //此时,这里已经得到了左右有序的数组
注意:递归是为了得到两个各自独立的有序数组,下面的while是为了把两个各自独立有序数组合并成一个有序数组
当一个数组有多个元素的时候它也是会执行下面的while那一段的,整个代码的逻辑是当只有一个元素的时候它返回到有两个元素的数组排序,然后这个递归才不断回溯,不断的由两个有序数组合并
示例代码
#include<iostream>
using namespace std;
const int N =1e6+10;
int n;
int q[N],tmp[N]; //tmp存放合并后的数组
void merge_sort(int q[],int l,int r)
{
if(l>=r)return;
int mid=l+r>>1; //>>1是右移一位操作,相当于将结果除以2
merge_sort(q,l,mid),merge_sort(q,mid+1,r); //递归排序,使左右两边都有序,这里两边都已经排好了
int k=0,i=l,j=mid+1; //i指向左半边的数组起点,j指向右半边的数组起点
while(i<=mid&&j<=r) //当左半边和右半边都没有循环完的时候
{
if(q[i]<=q[j]) //每次把小的放到当前的位置上
{
tmp[k++]=q[i++]; //这里是先让tmp[k]=q[i],再分别自增
}else
{
tmp[k++]=q[j++];
}
}
while(i<=mid) //当左半边或者右半边循环完了的时候,把剩下的数组直接放进合并数组中
{
tmp[k++]=q[i++];
}
while(j<=r)
{
tmp[k++]=q[j++];
}
for(i=l,j=0;i<=r;i++,j++)
{
q[i]=tmp[j]; //把临时的合并数组存回到原来的数组中
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&q[i]);
}
merge_sort(q,0,n-1);
for(int i=0;i<n;i++)
{
printf("%d ",q[i]);
}
return 0;
}