排序算法之希尔排序

希尔排序

算法思路:我觉得就是升级版的插入排序,因为如果用插入排序遇到了反序的序列,那么工程量将会特别大,虽然这只是一种假设,但是我们可以看作是一种趋势,如果越偏向反序那么插入排序的工程量会越来越大。

插入排序的跨度为1,每次挨着比较会很麻烦,我们将1变为incrent这个不变量,跨度变大,然后对所有序列排序一次,然后吗慢慢将跨度减小,直到跨度为1时进行一次最仔细的排序。

算法实现:

我们定义一个increment跨度变量,就像插入排序一样只不过最外层要套一个变化incrment的循环直到1.

里面的2号for循环完成对每个点的遍历,但是这个跨度不再为1,而是increment

2号循环里面嵌套了3号循环。这个循环用来比较所有之前以increment为序列且排序好的数组和当前点的大小,用于知道这个点的位置。最后完成插入

package shell;
/*算法思路:因为插入排序遇到最极端的情况逆序这种会很繁琐。所以我们有了新的思路
* 每次以一定长度来排序,之前是以1来排序,目的是减少待排序记录的个数,并且使整个序列朝着有向的方向发展
*
* 其实就是改进版插入排序只不过间隔并不是1而是视情况而定的跨度,不过最后还要通过1排序
* */
public class Shell {
    public static void main(String[] args) {
        int[] shell = new int[]{0, 4, 2, 3, 2, 52, 2, 1, 6, 42, 54};//初始化操作数组
        int i, j;//定义循环用的判定条件、
        int increment=shell.length;//定义插入排序的跨度
        do {//对跨度进行循环,直到最后跨度为1进行一次最仔细的排序
            increment=increment/3+1;
            for (i=1+increment;i<shell.length;i++) {//第一个for是循环遍历每个点,从1+increment开始

                 if (shell[i]<shell[i-increment]){//这一步完成比较,比较以increment为跨度的两个值的大小
                     //如果需要更改顺序
                     //1.先存储这个元素
                     shell[0]=shell[i];
                     //2.通过循环来判断这个元素需要插入的顺序
                     //这个for循环换成文字表达就是:从i点以increment为跨度的前一个点开始,每次都将上一个increment距离的元素后移,
                     //直到到最后一个元素或者找到合适的位置
                     for(j=i-increment;j>0&&shell[0]<shell[j];j-=increment)
                     {
                     shell[j+increment]=shell[j];

                     }//记录后移


                         shell[j+increment]=shell[0];//插入记录

                 }
            }

        }while (increment>1);
        for(int f : shell) {
            System.out.println("shell"+f);
        }
    }


}

出现的问题:

对于increment那层循环我一开始没用do。。while,因为我把go和java 搞混了,go并不能用do。。while语句,所以一开始我的处理是increment是用for循环,判断条件是increment>=0,但是因为最后的值一直是1不会停止,所以最后加一个if语句判断当increment为1是break;


第二个问题很值得记录:

for(j=i-increment;j>0&&shell[0]<shell[j];j-=increment)
{
shell[j+increment]=shell[j];

}//记录后移

这段代码里我之前写的是:

for(j=i-increment;shell[0]<shell[j]&&j>0;j-=increment)

{
shell[j+increment]=shell[j];

}//记录后移
一直会报一个下标越界的错误,我想了半天,原来是当j小于0时他会进行for判断,for里的判断是
shell[0]<shell[j]&&j>0  这句,它会根据顺序先判断前半部分接下来才判断j>0
也就是说会先判断shell[0]<shell[负数]那么肯定会越界,所以把这两个判断反转过来就可以完美解决这个问题。
写到这里时我想到了是不是只有for这样还是所有循环语句都这样,便做了实验


第二个问题结论:所有循环语句的条件体都是按顺序判断的,所以使用时要特别注意顺序,如果顺序错误可能导致程序出现不可预知错误

参考视频(超级好理解,4分钟就懂):https://www.bilibili.com/video/av17062242?from=search&seid=11529979729971176675

猜你喜欢

转载自blog.csdn.net/sunmeok/article/details/80807548