基础算法|6 折半插入排序 - HDU 1412
Posted ACM算法日常
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础算法|6 折半插入排序 - HDU 1412相关的知识,希望对你有一定的参考价值。
我们之前已经了解了5种基础算法,是否自己找了一些题练练手呢~话不多说,让我们进入第6中基础算法的学习吧。本篇我们将学习又一种排序算法——折半插入排序算法,跟上篇我们所学习的快速排序有点像,都是建立在我们之前学习的算法的基础上改进而来的。从这个算法的名字中大概就能知道它是建立在哪个算法的基础之上的,没错,就是折半(二分)查找和直接插入排序。
折半插入排序
我们知道,直接插入排序我们是通过顺序查找(即逐个元素逐一比较)然后找到待插入元素的位置,而我们之前学习了一种效率较高的查找算法——二分(折半)查找,而且我们需要进行查找的表又是有序的,这样就刚好符合了二分查找的使用条件,这样就可以提高我们排序的效率。
折半插入排序的算法思想
通过将直接插入排序的顺序查找更换为效率更高的二分查找,以提高排序算法的效率。
折半插入排序的实现过程
为了适应插入排序,我们需要对之前的二分查找做一些改进。插入排序每次的有序序列的长度并不为原序列的长度,而是从长度为1(只有a[0]一个元素)到原序列的长度n。所有我们需要给二分查找传入两个参数——start和end,用来表示需要查找的范围。其次就是二分查找算法的返回值。若找到了与同相等的值,返回的middle值就是我们要插入的位置,而如果原数列中没有找到相等的值,它会返回-1,那此时我们应该如何判断插入的位置呢?让我们来一起探讨一下吧。
假设我们需要对数列[5,3,8,6,4]进行排序,起初elements(有序序列中元素的个数)为1。
tip:有序序列中的元素用红色标明
现在我们需要将3插入到有序序列中,根据二分查找算法,它会判断待插入值3与中间值middle(此时为5)的大小,发现比5小,所有它将pow变成middle-1,所以此时pow变为-1。发现pow<low,结束循环,返回-1,我们通过示意图可以知道,我们要查找的插入位置就是low此时在的位置(即第一个大于待插入值元素的位置)。这是有序序列中存在比待插入值大的元素,若不存在这样的元素,我们知道应该将其插入到有序序列的末尾,此时这个位置也正好是low所在的位置(有兴趣大家可以自己画个图分析下)。通过以上的分析,我们知道,当二分查找返回-1时,我们应该将其改为返回low。
代码实现
public static void binaryInsertSort(int[] a){
int elements=1; //记录有序序列中的元素个数,开始只有a[0]
for(int i = 1;i<a.length;i++){ //从待排序数列的第二个元素开始
int res = binarySearch(a, 0, elements-1, a[i]); //将二分查找返回的结果存储在res变量中
int temp = a[i]; //将a[i]的值临时存储在temp中
for(int j =elements-1;j>=res;j--){ //从有序数列的最后位置到res,逐个向后移动一个位置
a[j+1] = a[j];
}
a[res] = temp; //将a[i]的值插入到对应位置
elements++; //加入一个元素之后,elements自增
}
}
/**
*
* 改进版的二分查找,可以控制在一定范围内查找value
* 若未找到,将返回-1改成返回low的位置即可
*
*/
public static int binarySearch (int[] a ,int start,int end,int value) {
int low=start,middle; //定义区间下界并初始化为0,声明中间位置变量middle
int pow = end; //定义区间上界并初始化为数组的长度
while(pow >= low){ //开始循坏,当pow<low,说明没有需要查找的值
middle = (pow+low)/2; //为middle开始赋予初值
if(value == a[middle]){ //先判断middle所在处的值是不是需要查找的值
return middle;
}
else if(a[middle] > value) { //若middle处的值大于需要查找的值,则将搜寻范围缩小到前半部分
pow = middle -1;
}
else{ //若middle处的值小于需要查找的值,则将搜寻范围缩小到后半部分
low = middle +1;
}
}
return low; //若循坏结束,即pow<low,说明需要查找的值不存在,则返回low的位置
}
public static void main(String[] args) {
int[] a = new int[]{5,7,9,10,25,18,19,13};
binaryInsertSort(a);
for(int i :a){
System.out.print(i+" ");
}
}
讲到这,又到了练手的时间了,上题~
HDU 1412 {A}+{B}
Problem Description
给你两个集合,要求{A} + {B}.
注:同一个集合中不会有两个相同的元素.
Input
每组输入数据分为三行,第一行有两个数字n,m(0<n,m<=10000),分别表示集合A和集合B的元素个数.后两行分别表示集合A和集合B.每个元素为不超出int范围的整数,每个元素之间有一个空格隔开.
Output
针对每组数据输出一行数据,表示合并后的集合,要求从小到大输出,每个元素之间有一个空格隔开.
Sample Input
1 2 1 2 3 1 2 1 1 2
Sample Output
1 2 3 1 2
分析:我们可以先建立一个集合类Set,它可以自动帮我们消重,然后再将Set集合转化为int类型的数组,再对此数组进行我们今天所学的折半插入顺序即可。
代码实现:
public static void main(String[] args) {
int n,m; //A,B集合的元素个数
Scanner input = new Scanner(System.in);
System.out.println("请输入集合A,B的元素个数:");
do{
n = input.nextInt();
m = input.nextInt();
if(n>10000 || m>10000){ //元素个数应小于10000
System.out.println("元素个数应小于10000,请重新输入:");
}
else{
break;
}
}while(true);
Set<Integer> set = new HashSet<Integer>(); //定义集合Set类,若set中有重复的元素,自动消重
System.out.println("请输入A的元素:");
for(int i = 0;i<n;i++){
set.add(input.nextInt()); //直接将A中的元素全部添加到set中
}
System.out.println("请输入B的元素:");
for(int i =0;i<m;i++){
set.add(input.nextInt()); //将B中的元素全部添加到set中,自动消重
}
Object[] resultTemp = set.toArray(); //将set转化为Object类型的数组
int[] result = new int[resultTemp.length]; //定义结果集
for(int i =0;i<resultTemp.length;i++){ //将Object类型的数组转化为int类型
result[i] = (int)resultTemp[i];
}
binaryInsertSort(result); //进行折半插入排序
for(int i : result){ //输出结果
System.out.print(i+" ");
}
}
让我们测试一下吧
总述
我们本次有学习了一种基础排序算法,可以和我们之前学习的其他算法进行比较学习,这样学习效果更佳呢ヾ(◍°∇°◍)ノ゙
温馨提示
以上是关于基础算法|6 折半插入排序 - HDU 1412的主要内容,如果未能解决你的问题,请参考以下文章