排序算法--Java实例/原理

原文网址:排序算法--Java实例/原理_IT利刃出鞘的博客-CSDN博客

简介

本文用Java示例来介绍排序算法。

本内容也是Java后端面试常见的问题。

排序算法概述

复杂度

分类

十种常见排序算法可以分为两大类:

  • 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。

  • 线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。

概念介绍

  • 稳定:如果a原本在b前面且a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面且a=b,排序之后 a 可能会出现在 b 的后面。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
  • In-Place:占用常数内存,不占用额外内存。比如:程序里没有创建新数组来保存数据,只用了临时变量。
  • Out-Place:占用额外内存。比如:创建了新的数组来保存或者处理数据。

排序算法应用场景及选择

1)若 n 较小(如n ≤ 50)时,可采用直接插入或简单选择排序。
2)若元素初始状态基本有序(正序),直接插入、冒泡或快速排序为宜。
3)若 n 较大,则应采用时间复杂度为O(nlogn)的排序方法:快速排序、堆排序或归并排序。

        快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短。

        堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。

        若要求排序稳定,则可选用归并排序。但本文介绍的从单个记录起进行两两归并的归并排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序数列,然后再两两归并之。因为直接插入排序是稳定的,所以改进后的归并排序仍是稳定的。

        当范围已知,且空间不是很重要的情况下可以考虑使用桶排序。

冒泡排序(Bubble Sort)

基本思想

        冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为每趟比较将当前数列未排序部分的最大的元素“沉”到数列末端,而小的元素会经由交换慢慢“浮”到数列的顶端。

算法描述

  1. 比较相邻的元素。如果前一个比后一个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。为了优化算法,可以设立一个布尔标识,每趟排序开始前设为false,如果该趟排序发生了交换就置为true,如果一趟排序结束标识仍为false表示该趟排序没有发生交换,即数组已经有序,可以提前结束排序。

动图演示

代码实现

public static int[] bubbleSort(int[] array) {
    if (array.length == 0)
        return array;
    for (int i = 0; i < array.length; i++){  //外层循环一次为一趟排序
        /*设置标识,判断这趟排序是否发生了交换。
       如果未发生交换,则说明数组已经有序,不必再排序了*/
        boolean isSwap = false;
        //内层循环一次为一次相邻比较
        for (int j = 0; j < array.length - 1 - i; j++) {
            if (array[j + 1] < array[j]) {
                int temp = array[j + 1];
                array[j + 1] = array[j];
                array[j] = temp;
                isSwap = true;
            }
        }
        if (!isSwap)
            break;
    }
    return array;
}

时间复杂度

冒泡排序平均时间复杂度为O(n2),最好时间复杂度为O(n),最坏时间复杂度为O(n2)。

最好情况:如果待排序元素本来是正序的,那么一趟冒泡排序就可以完成排序工作,比较和移动元素的次数分别是 (n - 1) 和 0,因此最好情况的时间复杂度为O(n)。

最坏情况:如果待排序元素本来是逆序的,需要进行 (n - 1) 趟排序,所需比较和移动次数分别为 n * (n - 1) / 2和 3 * n * (n-1) / 2。因此最坏情况下的时间复杂度为O(n2)。

空间复杂度

冒泡排序使用了常数空间,空间复杂度为O(1)

稳定性

当 array[j] == array[j+1] 的时候,我们不交换 array[i] 和 array[j],所以冒泡排序是稳定的。

算法拓展

鸡尾酒排序,又称定向冒泡排序、搅拌排序等,是对冒泡排序的改进。在把最大的数往后面冒泡的同时,把最小的数也往前面冒泡,同时收缩无序区的左右边界,有序区在序列左右逐渐累积。

动图如下:

代码如下:

public static void cocktailSort(int[] array) {
    int left = 0,right = array.length-1;
    while(left < right) {
        for(int i = left; i < right; i++) 
          if(array[i] > array[i+1]) 
                swap(array,i,i + 1);
        right--;
        for(int i = right; i > left; i--) 
          if(array[i] < array[i-1]) 
                swap(array,i,i-1);
        left++;
    }
}

鸡尾酒排序是稳定的。它的平均时间复杂度为O(n2),最好情况是待排序列原先就是正序的,时间复杂度为O(n),最坏情况是待排序列原先是逆序的,时间复杂度为O(n2)。空间复杂度为O(1)。

选择排序(Selection Sort)

基本思想

简单选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

上边只是部分内容,为便于维护,本文已迁移到此地址:排序算法-Java实例/原理 - 自学精灵

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Java快速排序算法实例是:public static void quickSort(int[] arr, int low, int high) { int i = low, j = high; int temp; int pivot = arr[(low + high) / 2]; while (i <= j) { while (arr[i] < pivot) { i++; } while (arr[j] > pivot) { j--; } if (i <= j) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; i++; j--; } } if (low < j) quickSort(arr, low, j); if (i < high) quickSort(arr, i, high); } ### 回答2: 快速排序是一种常用的排序算法,它利用分治的思想将一个大问题分成多个小问题进行解决。下面是一个使用Java实现的快速排序算法的示例: ```java public class QuickSort { public static void main(String[] args) { int[] arr = {9, 5, 7, 3, 1, 8, 6, 2, 4}; quickSort(arr, 0, arr.length - 1); System.out.println(Arrays.toString(arr)); } public static void quickSort(int[] arr, int low, int high) { if (low < high) { int pivot = partition(arr, low, high); quickSort(arr, low, pivot - 1); quickSort(arr, pivot + 1, high); } } public static int partition(int[] arr, int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j < high; j++) { if (arr[j] < pivot) { i++; swap(arr, i, j); } } swap(arr, i + 1, high); return i + 1; } public static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } ``` 以上代码实现了快速排序算法。首先,在main方法中定义一个待排序的数组。然后调用`quickSort`方法进行排序,该方法接受待排序数组、起始位置和结束位置作为参数。在`quickSort`方法内部,首先判断起始位置是否小于结束位置,如果是,则调用`partition`方法将数组划分为两部分,以一个基准值作为分界点,将比基准值小的元素放在基准值左边,比基准值大的元素放在基准值右边。然后递归地对基准值左边和右边的子数组进行排序。`partition`方法实现了划分的过程,采用双指针的方式,从左到右遍历数组,将小于基准值的元素与指针i指向的元素交换位置,同时将指针i向右移动。最后将基准值与指针i的元素交换位置,使得基准值处于正确的位置上。最终,数组将会按从小到大的顺序排序。 在主函数中输出排序后的数组,得到的结果为:[1, 2, 3, 4, 5, 6, 7, 8, 9]。这证明快速排序算法对给定的数组进行了正确的排序。 ### 回答3: 快速排序是一种常用的排序算法,其算法思想是通过选择一个基准元素,将待排序数组分成两个子数组,一个子数组中的元素都小于基准元素,另一个子数组中的元素都大于基准元素,然后对这两个子数组分别进行快速排序,最后将排好序的子数组合并即可得到整个数组的有序序列。 下面以Java代码为例,简单实现一下快速排序算法: ```java public class QuickSort { public static void quickSort(int[] array, int low, int high) { if (low < high) { // 分区操作,将数组分成两个子数组 int pivot = partition(array, low, high); // 对左子数组进行快速排序 quickSort(array, low, pivot - 1); // 对右子数组进行快速排序 quickSort(array, pivot + 1, high); } } private static int partition(int[] array, int low, int high) { // 选择基准元素 int pivot = array[low]; while (low < high) { // 从右向左找第一个小于基准元素的位置 while (low < high && array[high] >= pivot) { high--; } // 将小于基准元素的值移到左侧 array[low] = array[high]; // 从左向右找第一个大于基准元素的位置 while (low < high && array[low] <= pivot) { low++; } // 将大于基准元素的值移到右侧 array[high] = array[low]; } // 将基准元素放入最终位置 array[low] = pivot; // 返回基准元素的索引位置 return low; } public static void main(String[] args) { int[] array = {6, 5, 3, 1, 8, 7, 2, 4}; int low = 0; int high = array.length - 1; quickSort(array, low, high); System.out.println("排序结果:"); for (int i : array) { System.out.print(i + " "); } } } ``` 以上是一个简单的快速排序算法实例。在主函数中,创建一个待排序的数组,并调用`quickSort`函数进行排序,并输出排序结果。通过运行程序,可以看到输出的排序结果为`1 2 3 4 5 6 7 8`,表明快速排序算法能够正确地对数组进行快速排序

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT利刃出鞘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值