排序算法(直接插入排序、希尔排序、选择排序、冒泡排序、快速排序)

1. 直接插入排序

假设有一组待排序数据  {4, 8, 2, 1, 45, 13, 2}。

算法思路:直接插入排序就是像整理扑克牌一样,把待排序的元素插入到已经排好序的元素中。(每次拿出无序区间中的第一个数,插入到有序区间的合适的位置)

具体实现:

当插入第 i ( i>=1 ) 个元素时,前面的 array[0],arr[1],...  arr[i-1] 已经排好序,此时 arr[i] 与 arr[i-1],arr[i-2],... arr[0]顺序进行比较,找到插入位置就将 arr[i] 插入,原来位置上的元素后移。

代码:

public static void insertSort(int[] arr){
        int len = arr.length;
        for(int i=1; i<len; i++){
            int tmp = arr[i];//临时变量存放arr[i]的值
            int j;
            for(j=i-1; j>=0; j--){
                if(tmp>=arr[j]){
                    //当前有序
                    break;
                }else {
                    //开始移动赋值
                    arr[j+1] = arr[j];
                }
            }
            arr[j+1] = tmp;
        }
}

分析: 时间复杂度 O(n^2),空间复杂度 O(1),稳定。

2. 希尔排序(是对插入排序的优化)

算法思路:希尔排序又叫缩小增量排序。是对插入排序的优化,是将待排序序列先分成 drr 组,再分别对每组进行直接插入排序。一轮之后,减小 drr ,继续对每组进行直接插入排序,...  一直到 drr=1,进行最后一轮直接插入排序。这样做的目的是为了让序列趋近于有序,加快排序。

至于这里的 drr 的取法有多种,不同的取法算法的性能也会有所差异,这里主要选取一种:

第1轮:drr = (len/3向下取整)+1;

第2轮:drr = (drr/3向下取整)+1;

. . .

一直到 drr = 1

具体实现:

代码:

import java.util.Arrays;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = new int[]{5,4,9,2,1,7,1,3,2,10};
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void shellSort(int[] arr){
        int[] drr = new int[]{4,2,1};
        for(int i=0; i<drr.length; i++){
            shell(arr,drr[i]);
        }
    }
    public static void shell(int[] arr, int gap){
        for(int i=gap; i<arr.length; i++){
            int tmp = arr[i];
            int j = 0;
            for(j=i-gap; j>=0; j-=gap){
                if(tmp >= arr[j]){
                    break;
                }else {
                    arr[j+gap] = arr[j];
                }
            }
            arr[j+gap] = tmp;
        }
    }
}

分析: 最坏时间复杂度 O(n^2),最好时间复杂度 O(n^1.3),空间复杂度O(1),不稳定。

3. 选择排序

算法思路:选择排序是每一次从待排序元素中选出最小(或最大)的一个元素,存放在序列的起始位置。直到要排序的元素全部排完。

具体实现:

假设有一组待排序数据  {4, 8, 2, 1, 45, 13, 2}

定义变量 i 来遍历序列,j = i+1;

如果 arr[j] < arr[i] 就交换位置,否则继续 j++。

代码:

public static void selectSort(int[] arr){
        for(int i=0; i<arr.length; i++){
            for(int j=i+1; j<arr.length; j++){
                if(arr[j] < arr[i]){
                    int tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
}

分析:时间复杂度O(n^2),空间复杂度O(1),不稳定。

4. 冒泡排序

算法思路:对待排序元素相邻的元素两两比较,如果顺序错误,就进行交换。(把最大的数挤到了最后)

代码:

public static void bubbleSort(int[] arr){
        for(int i=0; i<arr.length-1; i++){ //趟数
            boolean flag = false;
            for(int j=0; j<arr.length-1-i; j++){
                if(arr[j+1] < arr[j]){
                    int tmp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = tmp;
                    flag = true;
                }
            }
            if(flag == false){
                break;
            }
        }
}

分析:时间复杂度O(n^2),优化后(加了flag标记)时间复杂度为O(n),空间复杂度O(1),稳定。

5. 快速排序

算法思路:从待排序序列中任取一个元素作为基准值,按基准值将待排序序列分割成两个子序列。比基准值小的元素放到基准值左边,比基准值大的元素放到基准值右边。然后左右序列重复该过程,直到所有元素都排列在相应位置为止。

具体实现:下面方法的基准值总是取序列的第一个元素。实际上,基准值的取法有多种,取法不同,算法的性能也会有差异。

代码:

import java.util.Arrays;
import java.util.Random;
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = new int[100];
        Random random = new Random();
        for(int i=0; i<arr.length; i++){
            arr[i] = random.nextInt()/10000000;
        }
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    public static void quickSort(int[] arr, int start, int end){
        int low = start;
        int high = end;
        int key = arr[low];
        while(low<high){
            while(low < high && arr[high] >= key){
                high--;
            }
            if(low >= high){
                break;
            }else {
                arr[low] = arr[high];
            }
            while(low < high && arr[low] <= key){
                low++;
            }
            if(low >= high){
                break;
            }else {
                arr[high] = arr[low];
            }
        }
        arr[low] = key;
        if(low>start+1) quickSort2(arr,start,low-1);//对左边继续排序
        if(high<end-1) quickSort2(arr,high+1,end);//对右边继续排序
    }
}

分析:时间复杂度O(N*logN),空间复杂度O(logN),不稳定。另外,采用快排,如果序列已经有序,此时的分割是一个非常不好的分割。因为每次划分只能使待排序序列减一,并没有将序列分成两部分。此时为最坏情况,快排沦为冒泡排序,时间复杂度为O(n^2)。为了避免这种情况,就要对快排进行优化,使得每次选择的基准值能较为均匀地把序列分成两部分。下面有两种方法:

方法1:随机选取基准(取待排序序列中任意一个元素作为基准)

这是一种相对安全的策略。由于基准值是随机从序列中选取的,那么产生的分割也不会总是出现劣质的分割。但当整个序列全相等时,仍然是最坏情况,时间复杂度是O(n^2)。但实际上,随机化快速排序得到理论最坏情况的可能性很低,仅为1/(2^n),所以随机化快速排序可以对于绝大多数数据达到O(N*logN)的期望时间复杂度。

方法2:三数取中法(也就是取左端、中间、右端三个数,进行排序,将中间数作为基准值)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值