zoukankan      html  css  js  c++  java
  • Java——希尔排序

    希尔排序

    • 希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
    • 前面学习插入排序的时候,我们会发现一个很不友好的事儿,如果已排序的分组元素为{2,5,7,9,10},未排序的分组元素为{1,8},那么下一个待插入元素为1,我们需要拿着1从后往前,依次和10,9,7,5,2进行交换位置,才能完成真正的插入,每次交换只能和相邻的元素交换位置。那如果我们要提高效率,直观的想法就是一次交换,能把1放到更前面的位置,比如一次交换就能把1插到2和5之间,这样一次交换1就向前走了5个位置,可以减少交换的次数,这样的需求如何实现呢?接下来我们来看看希尔排序的原理。

    需求:

    • 排序前:{9,1,2,5,7,4,8,6,3,5}
    • 排序后:{1,2,3,4,5,5,6,7,8,9}

    排序原理:

    1.选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;

    2.对分好组的每一组数据完成插入排序;

    3.减小增长量,最小减为1,重复第二步操作。

    增长量h的确定:增长量h的值每一固定的规则,我们这里采用以下规则:

    int h=1 
    while(h<5){ 
        h=2h+1;//3,7 
    }
    //循环结束后我们就可以确定h的最大值; 
    h的减小规则为: 
        h=h/2
    

    希尔排序API设计:

    类名 Shell
    构造方法 Shell():创建Shell对象
    成员方法 1.public static void sort(Comparable[] a):对数组内的元素进行排序
    2.private static boolean greater(Comparable v,Comparable w):判断v是否大于w
    3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

    希尔排序的代码实现:

    // 希尔排序
    public class Shell {
        /**
         * 对数组a中的元素进行希尔排序
         */
        public static void sort(Comparable[] a) {
    //        1.确定增量h
            int h = 1;
            while (h < a.length >> 1) {
                h = 2 * h + 1;
            }
    //        2.希尔排序
            while (h >= 1) {
    //          外层循环控制增量值有规律递减
    //          2.1 找到待插入的元素
                for (int i = h; i < a.length; i++) {
    //                2.2 把待插入的元素插入到有序数列中
                    for (int j = i; j >= h; j -= h) {
    //                    待插入的元素是a[j],比a[j-h]和a[j]
                        if (greater(a[j - h], a[j])) {
    //                        交换元素
                            exch(a, j - h, j);
                        } else {
    //                        待插入元素已经找到了合适的位置,结束循环;
                            break;
                        }
                    }
                }
    //            减小h的值 h=h/2;
                h >>= 1;
            }
        }
    
        /**
         * 比较v元素是否大于w元素
         */
        private static boolean greater(Comparable v, Comparable w) {
            return v.compareTo(w) > 0;
        }
    
        /**
         * 数组元素i和j交换位置
         */
        private static void exch(Comparable[] a, int i, int j) {
            Comparable temp;
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    
    // 测试代码
    public class ShellTest {
        public static void main(String[] args) {
            Integer[] a = {9, 1, 2, 5, 7, 4, 8, 6, 3, 5};
            Shell.sort(a);
            System.out.println(Arrays.toString(a));//{1,2,3,4,5,5,6,7,8,9}
        }
    }
    
    

    希尔排序的时间复杂度分析

    • 在希尔排序中,增长量h并没有固定的规则,有很多论文研究了各种不同的递增序列,但都无法证明某个序列是最好的,对于希尔排序的时间复杂度分析,已经超出了我们课程设计的范畴,所以在这里就不做分析了。
    • 我们可以使用事后分析法对希尔排序和插入排序做性能比较。
    • 在资料的测试数据文件夹下有一个reverse_shell_insertion.txt文件,里面存放的是从100000到1的逆向数据,我们可以根据这个批量数据完成测试。测试的思想:在执行排序前前记录一个时间,在排序完成后记录一个时间,两个时间的时间差就是排序的耗时。

    希尔排序和插入排序性能比较测试代码:

    public class SortCompare {
        //调用不同的测试方法,完成测试
        public static void main(String[] args) throws Exception {
            //1.创建一个ArrayList集合,保存读取出来的整数
            ArrayList<Integer> list = new ArrayList<>();
    
            //2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中;
            BufferedReader reader = new BufferedReader(new InputStreamReader(SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
            String line = null;
            while ((line = reader.readLine()) != null) {
                //line是字符串,把line转换成Integer,存储到集合中
                int i = Integer.parseInt(line);
                list.add(i);
            }
    
            reader.close();
    
    
            //3.把ArrayList集合转换成数组
            Integer[] a = new Integer[list.size()];
            list.toArray(a);
            //4.调用测试代码完成测试
    //        testInsertion(a);//34929毫秒
    //        testShell(a);//43毫秒
            testMerge(a);//97毫秒
            
    
        }
    
        //测试希尔排序
        private static void testShell(Integer[] a) {
            //1.获取执行之前的时间
            long start = System.currentTimeMillis();
            //2.执行算法代码
            Shell.sort(a);
            //3.获取执行之后的时间
            long end = System.currentTimeMillis();
            //4.算出程序执行的时间并输出
            System.out.println("希尔排序执行的时间为:" + (end - start) + "毫秒");
    
        }
    
        //测试插入排序
        private static void testInsertion(Integer[] a) {
            //1.获取执行之前的时间
            long start = System.currentTimeMillis();
            //2.执行算法代码
            Insertion.sort(a);
            //3.获取执行之后的时间
            long end = System.currentTimeMillis();
            //4.算出程序执行的时间并输出
            System.out.println("插入排序执行的时间为:" + (end - start) + "毫秒");
        }
    
        //测试归并排序
        private static void testMerge(Integer[] a) {
            //1.获取执行之前的时间
            long start = System.currentTimeMillis();
            //2.执行算法代码
            Merge.sort(a);
            //3.获取执行之后的时间
            long end = System.currentTimeMillis();
            //4.算出程序执行的时间并输出
            System.out.println("归并排序执行的时间为:" + (end - start) + "毫秒");
        }
    }
    
  • 相关阅读:
    mysql中drop、delete、truncate的区别简述
    hadoop之数据倾斜
    Mysql相关:navicat for mysql 加注释
    泛型
    工银亚洲见证开户详细过程和攻略
    classpath:和classpath*:的区别
    单索引与唯一索引
    MySQL中SQL语句之反引号,单引号
    《集体智慧编程》学习笔记 第三章
    《集体智慧编程》 读书笔记 第二章
  • 原文地址:https://www.cnblogs.com/momo-88/p/13433843.html
Copyright © 2011-2022 走看看