直接插入排序算法——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算法 直接插入排序 -- 直接插入排序算法的非递归和递归形式实现

Java常用的八种排序算法与代码实现

Java常用的八种排序算法与代码实现

Java实现七大排序算法

插入排序(直接插入排序折半插入排序希尔排序的算法思想及代码实现)

8 种排序算法与 Java 代码实现!