直接插入排序算法——Java实现
Posted 阳光流淌007
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直接插入排序算法——Java实现相关的知识,希望对你有一定的参考价值。
直接插入排序,是算法里老生常谈的经典,这里直接先上一段原始版的直接插入排序代码,然后顺着代码理思路,最后再来一段优化版的代码。
原始版的代码如下:
public static void main(String[] args){
int[] nums = buildArray();
System.out.print("亲,您输入的初始数组是 : ");
printArray(nums);
//默认构造从左→右依次递增的序列
for(int i=1; i<nums.length; i++){
int temp = nums[i]; //temp是本趟待插入的数,之前从0~i-1的数全是从左→右有序递增。
int j=i-1;
if(nums[i-1]<=temp){
}else{
while(nums[j]>temp&&j>0){
if(nums[j-1]<=temp){
break;
}
j--;
}
for(int t=i;t>j;t--){
nums[t] = nums[t-1];
}
nums[j] = temp;
}
System.out.print("第"+i+"次直接插入排序后的数组:");
printArray(nums);
}}
废话不多说,直接对着代码开始讲起:
//默认构造从左→右依次递增的序列for(int i=1; i<nums.length; i++){
int temp = nums[i]; //temp是本趟待插入的数,之前从0~i-1的数全是从左→右有序递增。
int j=i-1;
i从1开始,因为i=0是第一个元素,默认有序。此处定义temp=nums[i]表示当前元素,j=i-1指向左边一位的元素下标。从i = 1开始往右遍历剩余的元素,此处对于当前的temp只有两种操作:
1.当前元素值temp>=左侧有序数列最大值
2.当前元素值temp<左侧有序数列最大值
由于构造的是从左→右递增序列,左侧的有序数列最大值在有序数列的最右侧,即当前temp的左边第一个数nums[j]。
if(nums[i-1]<=temp){}else{。。。
if(nums[j]<=temp){则证明此趟temp不需要做任何改动}
else{判断temp在有序序列中的插入位置,插入位后面的~i位置的所有元素后移,temp插入到插入位j,完成本次操作,for循环继续遍历下一个nums[i]}
现在,问题来了,如何寻找temp的插入位置?
先举个小栗子:
1.待插入序列:【1,3,4】2
此时1,3,4为有序序列,2为待插入的数,很明显,2应该插入到3所在的位置(3、4右移一位)
2.待插入序列:【3,4,5】2
此时2应该插入到序列首位(3,4,5右移一位)
如何找到temp的插入位置呢?看上去很简单,一个while循环搞定:
while(nums[j]>temp&j>0){ j-- ;}
指示器j,初始j = i-1;当j所指的数>temp时,指示器左移一位j--,这样最后j指向待插入的位置。慢着...对么?乍一看没问题,实际上,有坑!!!
此处看下1.的情况:循环结束后j指向了1的位置,实际应该插入的位置为3;
再看看2.的情况:循环结束后j指向3的位置,为正确的实际插入位;
一个while循环,结束后出来的j指示器指示了不同的位置???肿么办?看来要改一下while循环的判断逻辑。
while(nums[j]>temp&&j>0){
if(nums[j-1]<=temp){
break;
}
j--;}
此处添加一个if判断,如果当前位置的元素nums[j]>temp,那么先判断下一位nums[j-1],如果nums[j-1]也>temp则再进行j--操作,这样while循环结束后,j即永远指向的是待插入位。(虽然这个while循环看起来不美观,但是能用,暂且先用着,毕竟是原始版的代码!囧)
找到插入位后,右边所有的元素右移:
for(int t=i;t>j;t--){
nums[t] = nums[t-1];}nums[j] = temp;
移动完成后将本轮待插入元素temp插入到最终位置,nums[j] = temp;完成本轮循环。
原始版的代码虽然能用,但是看上去就很不美观啊,有没有更优化的方案?让我们捋一捋思路:
原始版的做法是对于每一个temp=nums[i]都先来用while循环找到其插入位置j,然后有序代码部分右移一位,最后将temp插入到空出的位置上。能不能改进一点?譬如一边找插入位一边右移?这样插入位找到后,我直接插入temp就好?思路有了,下面来看代码。
优化版的代码如下:
public static void main(String[] args){
int[] nums = buildArray();
System.out.print("亲,您输入的初始数组是 : ");
printArray(nums);
//默认构造从左→右依次递增的序列
for(int i=1; i<nums.length; i++){
int j;
int temp = nums[i]; //temp是本趟待插入的数,之前从0~i-1的数全是从左→右有序递增。
for(j=i-1; j>=0&&nums[j]>temp; j--){
nums[j+1] = nums[j];
}
nums[j+1] = temp;
System.out.print("第"+i+"次直接插入排序后的数组:");
printArray(nums);
}}
代码量瞬间少了很多有木有!!!这里j还是从i-1开始,当num[j]>temp时(且要保证j>=0,要覆盖到j==0的情况),j指示的元素nums[j]右移一位,移动到num[j+1]的位置,j--,然后继续判断下一个位置的元素。要注意的是,此处for循环结束后,j的位置和原始版里的有区别,此处j的位置指向的是插入temp的前一位,故最后需要nums[j+1] = temp。
----------------分割线------------------
最后,附上可运行的完整版代码(可以输入任意数字构成乱序数组,打印出每趟直接插入排序的过程,最终将数组以左→右升序形式输出):
package Charpter5;import java.util.Scanner;public class Test0506 {
public static void main(String[] args){
int[] nums = buildArray();
System.out.print("亲,您输入的初始数组是 : ");
printArray(nums);
//默认构造从左→右依次递增的序列
for(int i=1; i<nums.length; i++){
int j;
int temp = nums[i]; //temp是本趟待插入的数,之前从0~i-1的数全是从左→右有序递增。
for(j=i-1; j>=0&&nums[j]>temp; j--){
nums[j+1] = nums[j];
}
nums[j+1] = temp;
System.out.print("第"+i+"次直接插入排序后的数组:");
printArray(nums);
}
}
public static int[] buildArray(){
Scanner scan = new Scanner(System.in);
System.out.println("请输入一组整数值,数字之间用','隔开:");
int[] a = null;
String strs = scan.nextLine();
try{//strNums存放的是从键盘输入的string类数值,再转化为int类的数组intNums中
String[] s = strs.split("\\,");
a = new int[s.length];
for(int i=0; i<a.length; i++){
a[i] = Integer.valueOf(s[i]);
}
}catch (Exception e){
System.out.println("您输入的数值格式有误!!!");
e.printStackTrace();
}
return a;
}
public static void printArray(int[] arr){
System.out.print('[');
for(int i=0;i<arr.length-1;i++){
System.out.print(arr[i]+", ");
}
System.out.println(arr[arr.length-1] + "]");
}}
运行演示:
以上是关于直接插入排序算法——Java实现的主要内容,如果未能解决你的问题,请参考以下文章
Java算法 直接插入排序 -- 直接插入排序算法的非递归和递归形式实现