常见排序算法

1、冒泡排序

1.1 冒泡排序算法的算法过程如下:

①. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

②. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

③. 针对所有的元素重复以上的步骤,除了最后一个。

④. 持续每次对越来越少的元素重复上面的步骤①~③,直到没有任何一对数字需要比较。

1.2 代码实现:

public class BubbleSort {

  public static void sort(int[] array){

    if (null == array || array.length == 0){
      return;
    }

    int len = array.length;
    for (int i = 0; i < len; i++){
      for (int j = 0; j < len - 1 - i; j++){

        if (array[j] > array[j+1]){
          int tmp = array[j];
          array[j] = array[j+1];
          array[j+1] = tmp;
        }

      }
    }

  }

}

1.3 效率

       该算法是稳定的排序算法,同样也是最容易实现的排序,当然最坏的一种场景就是每次都进行交换,一共遍历并交换接近n²/2次,时间复杂度为O(n²)。最理想的场景就是内循环遍历一次发现排序是对的,退出循环,此时的时间复杂度为O(n). 平均来讲, 时间复杂度为O(n²). 由于冒泡排序中只有缓存的tmp变量需要内存空间, 因此空间复杂度为常量O(1)。

平均时间复杂度最好情况最坏情况空间复杂度
O(n 2 )O(n)O(n 2 )O(1)

2、快速排序

2.1 算法描述

        快速排序使用分治策略来把一个序列分为两个子序列。步骤为:

①. 从数列中挑出一个元素,称为”基准”(pivot)。

②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

2.2 代码实现:

public class QuickSort {

  public static void sort(int[] array, int low, int high){

    if (null == array || array.length ==0 || low >= high){
      return;
    }

    int left = low;
    int right = high;
    //基准值
    int key = array[low];

    while (left < right){

      //右->左找到第一个比基准值小的坐标
      while (left < right && key <= array[right]){
        right--;
      }
      //左->右找到第一个比基准值大的坐标
      while (left < right && key >= array[left]){
        left++;
      }
      //目前排序为: array[right] < key < array[left],要保证右边值要最大,需要交换
      if (left < right){
        int tem = array[left];
        array[left] = array[right];
        array[right] = tem;
      }

    }
    //目前排序为: array[left] < array[low] < array[right]
    //校准基准值,准备分治递归快排,要保证array[low]是最小值
    int tmp = array[left];
    array[left] = array[low];
    array[low] = tmp;

    //对key左边进行快排
    sort(array, low, left - 1);
    //对key右边进行快排
    sort(array, left + 1, high);
  }

}

2.3 效率

       快速排序并不稳定,快速排序每次交换的元素都有可能不是相邻的, 因此它有可能打破原来值为相同的元素之间的顺序。

平均时间复杂度最好情况最坏情况空间复杂度
O(nlogn)O(nlogn)O(n 2 )O(1)

3、希尔排序

3.1 算法描述

①. 选择一个增量序列t1,t2,…, tk ,其中ti>tj,tk=1;(一般初次取数组半长,之后每次再减半,直到增量为1)

②. 按增量序列个数k,对序列进行k 趟排序;

③. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

3.2 代码实现

public class ShellSort {

  public static void sort(int[] array){
    int gap = array.length >> 1;
    for (; gap > 0; gap = gap >> 1){
      //不断缩小gap,直到1为止
      for (int i = 0; (i + gap) < array.length; i++){
        //使用当前gap进行组内插入排序
        for (int j = 0; (j + gap) < array.length; j += gap){
          //交换
          if (array[j] > array[j+gap]){
            array[j] = array[j] + array[j+gap];
            array[j+gap] = array[j] - array[j+gap];
            array[j] = array[j] - array[j+gap];
          }
          
        }
      }

    }
  }

}

3.3 效率

       不稳定排序算法,希尔排序第一个超过O(n 2 )的排序算法;是简单插入排序的改进版;它与插入排序的不同之处在于,它会优先比较距离较远的元素,直接插入排序是稳定的;而希尔排序是不稳定的,希尔排序的时间复杂度和步长的选择有关,常用的是Shell增量排序,也就是N/2的序列,Shell增量序列不是最好的增量序列。

4、直接插入排序

4.1 算法描述

       基本思想:将数组中的所有元素依次跟前面已经排好的元素比较,若选择的元素比已经排序的元素小,则进行交换,直到全部元素都比较过为止。具体描述如下:

①. 从第一个元素开始,该元素默认已经排好序。

②. 获取下一个元素,在已经排好序的元素序列中从后往前扫描。

③. 如果该元素(已经排好序)大于新元素,则将该元素移到下一个位置。

④. 重复步骤③,直到找到已经排序的元素小于或者等于新元素的位置。

⑤. 将新元素插入到该位置。

⑥. 重复步骤②—⑤。

4..2 代码实现

public class InsertSort {
    /**
     * 移位法
     * @param arr
     */
    public static void sort1(int[] arr){
        if (null == arr || arr.length == 0){
            return;
        }
        for (int i = 1; i < arr.length; i++){
            int j = i - 1;
            //提前取出待插入数据保存
            int tmp = arr[i];
            //若比待插入的数据大就后移
            while (j >= 0 && arr[j] > arr[i]){
                arr[j+1] = arr[i];
                j--;
            }
            //找到比待插入数据小的位置,将待插入数据插入
            arr[j+1] = tmp;
        }
    }

    /**
     * 交换法
     * @param arr
     */
    public static void sort2(int[] arr){
        if (null == arr || arr.length == 0){
            return;
        }
        for (int i = 1; i < arr.length; i++){
            int j = i - 1;
            while (j >= 0 && arr[j] > arr[i]){
                //只要大就交换
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}

4.3 算法效率

      不稳定的排序算法。

平均时间复杂度最好情况最坏情况空间复杂度
O(n 2 )O(n)O(n 2 )O(1)

5、选择排序

5.1 算法描述

      在未排序序列中找到最小(大)元素,存放到未排序序列的起始位置。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。可以描述如下:

①. 从待排序序列中,找到关键字最小的元素;

②. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;

③. 从余下的 N – 1 个元素中,找出关键字最小的元素,重复①、②步,直到排序结束。

5.2 代码实现

public class SelectSort {
  
  public static void sort(int[] array){
    for (int i = 0; i < array.length - 1; i++){
      int min =  i;
      for (int j = i + 1; j < array.length ; j++){
        if (array[j] < array[i]){
          min = j;
        }
      }
      if (min != i){
        array[min] = array[min] + array[i];
        array[i] = array[min] - array[i];
        array[min] = array[i];
      }
    }
  }
  
}

5.3 算法效率

      不稳定排序算法,即使原数组已排序完成,它也将花费将近n²/2次遍历来确认一遍。 但是呢,它并不耗费额外的内存空间。

平均时间复杂度最好情况最坏情况空间复杂度
O(n 2 )O(n 2 )O(n 2 )O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值