排序算法 算法图解+java实现

下面使用的图解资源来自于 我的第一本算法书,里面的内容都比较基础简单、容易理解,下面排序算法中有提及到的,大家不理解的可以自己下载,学习学习,我觉得算法还是很重要的,但是搞得全是概念的话,会让我们学渣觉得没啥意思,学霸就不要看了,再见来不及挥手,鬼刀一开看不见走位走位,直接看算法导论去吧。
链接:https://pan.baidu.com/s/1pykWDv36c4Itux3fJjjYsQ
提取码:xsul
希尔排序、计数排序、基数排序、桶排序都来自于 趣谈编程 的相关博客,因为觉得讲的比较简单生动,当然有兴趣的同学可以关注他,学习更多有趣的编程知识。

下面算法中用到的交换两个元素位置的swap方法,代码如下:

private static void swap(Object[] arr, int i, int j) {
   
    Object t = arr[i];
    arr[i] = arr[j];
    arr[j] = t;
}

冒泡排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

 public static void sort(Comparable[] arr){
   
        int n = arr.length;
        boolean swapped = false;

        do{
   
            swapped = false;
            for( int i = 1 ; i < n ; i ++ ){
   
           		 if( arr[i-1].compareTo(arr[i]) > 0 ){
   
                    swap( arr , i-1 , i );
                    swapped = true;
                }
            }
            // 优化, 每一趟Bubble Sort都将最大的元素放在了最后的位置
            // 所以下一次排序, 最后的元素可以不再考虑
            n --;
        }while(swapped);
}

进一步优化

 public static void sort(Comparable[] arr){
   
        int n = arr.length;
        int newn; // 使用newn进行优化
        do{
   
            newn = 0;
            for( int i = 1 ; i < n ; i ++ ){
   
            	if( arr[i-1].compareTo(arr[i]) > 0 ){
   
                    swap( arr , i-1 , i );
                    // 记录最后一次的交换位置,在此之后的元素在下一轮扫描中均不考虑
                    newn = i;
                }
            }
            n = newn;
        } while(newn > 0);
}

选择排序

在这里插入图片描述
在这里插入图片描述
代码实现:

public static void sort(Comparable[] arr){
   
        int n = arr.length;
        for( int i = 0 ; i < n ; i ++ ){
   
            // 寻找[i, n)区间里的最小值的索引
            int minIndex = i;
            for( int j = i + 1 ; j < n ; j ++ ){
   
            	// 使用compareTo方法比较两个Comparable对象的大小
                if( arr[j].compareTo( arr[minIndex] ) < 0 ){
   
                	minIndex = j;
                }  
            }
            swap( arr , i , minIndex);
        }
}

进一步优化:

public static void sort(Comparable[] arr){
   
        int left = 0, right = arr.length - 1;
        while(left < right){
   
            int minIndex = left;
            int maxIndex = right;
            // 在每一轮查找时, 要保证arr[minIndex] <= arr[maxIndex]
            if(arr[minIndex].compareTo(arr[maxIndex]) > 0){
   
            	swap(arr, minIndex, maxIndex);
            }
            for(int i = left + 1 ; i < right; i ++){
   
                if(arr[i].compareTo(arr[minIndex]) < 0){
   
                 	minIndex = i;
                } else if(arr[i].compareTo(arr[maxIndex]) > 0){
   
                 	maxIndex = i;
                }
            }
            swap(arr, left, minIndex);
            swap(arr, right, maxIndex);
            left ++;
            right --;
        }
}

插入排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

public static void sort(Comparable[] arr){
   
        int n = arr.length;
        for (int i = 0; i < n; i++) {
   
            // 寻找元素arr[i]合适的插入位置
           
            // 写法1
            for( int j = i ; j > 0 ; j -- ){
   
                if( arr[j].compareTo( arr[j-1] ) < 0 ){
   
                    swap( arr, j , j-1 );
                } else{
   
                  	break;
                }
			}
			
            // 写法2
            for( int j = i; j > 0 && arr[j].compareTo(arr[j-1]) < 0 ; j--){
   
				swap(arr, j, j-1);
			}           

            // 写法3
            Comparable e = arr[i];
            int j = i;
            for( ; j > 0 && arr[j-1].compareTo(e) > 0 ; j--)
                arr[j] = arr[j-1];
            arr[j] = e;
        }
    }

希尔排序

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。

怎样可以对插入排序算法做出优化呢?我们不妨从插入排序的两个特点入手:
1.在大多数元素已经有序的情况下,插入排序的工作量较小
这个结论很明显,如果一个数组大部分元素都有序,那么数组中的元素自然不需要频繁地进行比较和交换。
2.在元素数量较少的情况下,插入排序的工作量较小
这个结论更加显而易见,插入排序的工作量和n的平方成正比,如果n比较小,那么排序的工作量自然要小得多。

如何对原始数组进行预处理呢?聪明的科学家想到了一种分组 排序的方法,以此对数组进行一定的“粗略调整”。
在这里插入图片描述
所谓分组,就是让元素两两一组,同组两个元素之间的跨度,都是数组总长度的一半,也就是跨度为4。
在这里插入图片描述
如图所示,元素5和元素9一组,元素8和元素2一组,元素6和元素1一组,元素3和元素7一组,一共4组。

接下来,我们让每组元素进行独立排序,排序方式用直接插入排序即可。由于每一组的元素数量很少,只有两个,所以插入排序的工作量很少。每组排序完成后的数组如下:
在这里插入图片描述
这样一来,仅仅经过几次简单的交换,数组整体的有序程度得到了显著提高,使得后续再进行直接插入排序的工作量大大减少。这种做法,可以理解为对原始数组的“粗略调整”。

但是这样还不算完,我们可以进一步缩小分组跨度,重复上述工作。把跨度缩小为原先的一半,也就是跨度为2,重新对元素进行分组:
在这里插入图片描述
如图所示,元素5,1,9,6一组,元素2,3,8,7一组,一共两组。

接下来,我们继续让每组元素进行独立排序,排序方式用直接插入排序即可。每组排序完成后的数组如下:
在这里插入图片描述
此时,数组的有序程度进一步提高,为后续将要进行的排序铺平了道路。

最后,我们把分组跨度进一步减小,让跨度为1,也就等同于做直接插入排序。经过之前的一系列粗略调整,直接插入排序的工作量减少了很多,排序结果如下:
在这里插入图片描述
让我们重新梳理一下分组排序的整个过程:
在这里插入图片描述
像这样逐步分组进行粗调,再进行直接插入排序的思想,就是希尔排序,根据该算法的发明者,计算机科学家Donald Shell的名字所命名。

上面示例中所使用的分组跨度(4,2,1),被称为希尔排序的增量,增量的选择可以有很多种,我们在示例中所用的逐步折半的增量方法,是Donald Shell在发明希尔排序时提出的一种朴素方法,被称为希尔增量

在某些极端的情况下,希尔排序的时间复杂度仍然是O(n^2),甚至比直接插入排序更慢,比如下面的例子:
在这里插入图片描述
上面这个数组,如果我们照搬之前的分组思路,无论是以4为增量,还是以2为增量,每组内部的元素都没有任何交换。一直到我们把增量缩减为1,数组才会按照直接插入排序的方式进行调整。

对于这样的数组,希尔排序不但没有减少直接插入排序的工作量,反而白白增加了分组操作的成本。

每一轮希尔增量之间都是等比的,这就导致了希尔增量存在盲区,为了避免这种极端情况,科学家们发明了更为严谨的增量方式。

如何为希尔排序选择更有效的增量方式呢?

为了保证分组粗调没有盲区,每一轮的增量需要彼此“互质”,也就是没有除1之外的公约数。

于是,人们相继提出了很多种增量方式,其中最具代表性的是Hibbard增量Sedgewick增量

Hibbard的增量序列

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值