排序算法(一):冒泡,简单选择,直接插入,希尔排序

注:以下提到的数组均可替换为表结构。

冒泡排序:依次比较相邻的两个数,如果不满足规定的排序规则,就交换顺序,依次往复,直至有序,主要操作为比较和交换。时间复杂度 O(n2),稳定。

从前往后

//标志是否交换过位置,若=false,说明数组已经达到有序,不必再进行。
boolean change = true;
for (int i = 0;i < length -1 && change;i++){
    change = false;
    for (int j = 0;j < length-i-1;j++){
        if(a[j] > a[j+1]){
            change = true;
            int temp = a[j+1];
            a[j+1] = a[j];
            a[j] = temp;
        }
    }
}
 
 

从后往前

boolean change = true;
for (int i = 0;i < length -1 && change;i++){
    change = false;
    for (int j = length - 1;j > i;j--){
        if(a[j-1] > a[j]){
            change = true;
            int temp = a[j];
            a[j] = a[j-1];
            a[j-1] = temp;
        }
    }
}

简单选择排序:每趟找出最小/最大的值后将其放入正确位置,与冒泡排序相比,该算法减少了交换的次数,比较次数未变,时间复杂度 O(n2),但优于冒泡,稳定。

如何找每趟最小:借助临时变量min(最小值在数组中的位置),循环未排序数组,若有比arr[min]更小的数,将min的值替换为更小值所在的位置,直至完成本趟循环。

for (int i = 0;i < length - 1;i++){
            int min = i;
            for (int j = i+1;j < length;j++){
                if (arr[j] < arr[min]){
                    min = j;
                }
            }
            if (i != min){
                int temp = arr[i];
                arr[i] = arr[min];
                arr[min] = temp;
            }
        }

直接插入排序:将一个数插入到一个已有序数组中,得到一个新的,长度+1的有序数组。特点是在插入的时候就将元素放在使新数组有序的位置。时间复杂度,最好O(n):数组本就有序,则只会进行n比较,最坏O(n2):但仍优于冒泡与简单排序。

//说明:因为旧元素已经有序,所以在有序的表中,第一个元素必为最小,最后一个元素必为最大。
        for (int i = 1;i < length - 1;i++){//i从1开始,因为默认第0个元素已有序
            if (arr[i-1] > arr [i]){//最后一个元素比新元素大,则依次将比新元素大的元素向后移,为新元素腾出位置
                int j;
                int new_ele = arr[i];//新插入元素,需要存到一个临时变量,不然在前面元素向后移动的时候,其值会被覆盖掉
                for (j = i;j > 0 && new_ele < arr [j-1];j--){
                    arr[j] = arr[j-1];//新加入元素比前一个更小,将前一个向后移动,此时j-1应该是新元素应在的位置
                }
                arr[j] = new_ele;//此处j的值为新元素最终应该插入的位置
            }
            //默认else是arr[i] > arr [i-1],即新元素比最大的元素还大,那就在数组最后,即i,不做任何变化,继续下次循环,寻找i+1的正确位置。
        }

希尔排序利用直接插入的优点,当元素基本有序或者元素很少时,直接插入排序表现很好。所以,希尔排序将带排序数组先拆分为多个分组,分别对分组中的元素进行直接插入排序后合并,得到一个基本有序的数组,最后再进行一次直接排序,得出有序数组。基本有序即小的基本在左边,大的基本在右边,不大不小的基本在中间。不稳定。

在这里,为了得到最终基本有序的数组,最初的分组不能随意分,需要采用跳跃分割策略,简单讲就是将相距相等增量的元素分到同一组。通过跳跃分组是可以达到基本有序的效果的,已想通。

具体分法可看:https://blog.csdn.net/daiyudong2020/article/details/52445044

//gap即为增量,通过不断减小增量直至1时,得到最终排序结果。其实gap=1是就是最后一次合并排序
        for (int gap = length/2;gap > 0;gap/=2){
            //此for循环内部其实就是直接插入排序,只是元素的距离不是连续的,而是选择间隔为gap的元素做直接插入排序
            for(int i = gap;i<length;i++){
                int j;
                int new_ele = arr[i];
                for(j = i;j>i-gap && new_ele < arr[j-gap];j-=gap){
                    arr[j] = arr[j-gap];
                }
                arr[j] = new_ele;
            }
        }

注意:增量序列的最后一个值必须是1

希尔算法是冲破O(n2)的第一批算法之一,希尔排序中对于增量序列的选择十分重要,直接影响到希尔排序的性能其最坏时间复杂度依然为O(n2),一些经过优化的增量序列如Hibbard经过复杂证明可使得最坏时间复杂度为O(n3/2),但最佳增量的选择是目前还是数学难题。





猜你喜欢

转载自blog.csdn.net/xybz1993/article/details/80206405