数组排序算法大比拼:快排、归并、冒泡哪个更快?

2024-01-26 12:38:40 浏览数 (1)

哈喽,各位小伙伴们,你们好呀,我是喵手。

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在计算机科学中,排序算法一直是一个热门话题,因为排序是数据处理中最基本的任务。在实际应用中,无论是数据库还是Web应用程序,排序任务都是必备的。因此,选择适当的排序算法是非常重要的。

  常见的排序算法有快速排序、归并排序、冒泡排序、选择排序等。本篇文章将重点讨论快速排序、归并排序和冒泡排序这三种算法,分析它们的优缺点、应用场景和性能表现,为读者提供一个全面的排序算法比较。

摘要

  本文对快速排序、归并排序和冒泡排序三种算法进行了比较。从算法原理、时间复杂度、空间复杂度、优缺点以及适用场景等方面进行了分析和比较。最后,作者总结得出了结论:在大多数情况下,快速排序是最优的选择。

正文

简介

快速排序

  快速排序是一种分治的排序算法,它将一个数组分成两个子数组,然后递归地对子数组进行排序。具体步骤如下:

  1. 首先选择一个枢轴元素(pivot)。
  2. 将数组中小于等于枢轴元素的部分移动到数组左侧,大于枢轴元素的部分移动到数组右侧。
  3. 对左右子数组递归地进行步骤1和步骤2操作。

时间复杂度为O(nlogn),空间复杂度为O(logn)。

归并排序

  归并排序是一种分治算法,它将一个数组分成两个子数组,然后递归地对子数组进行排序,最后将两个有序的子数组合并成一个有序的数组。具体步骤如下:

  1. 递归地把当前序列平均分成两半。
  2. 对左右子序列分别递归地进行排序。
  3. 将左右排好序的子序列合并成一个有序序列。

时间复杂度为O(nlogn),空间复杂度为O(n)。

冒泡排序

  冒泡排序是一种简单的排序算法,它通过多次遍历列表,比较相邻的元素,并交换它们的位置来完成排序。具体步骤如下:

  1. 从第一个元素开始,比较相邻的两个元素,如果前一个元素比后一个元素大,则交换这两个元素的位置。
  2. 对列表中的每个相邻元素做同样的工作,执行完一轮后,最后一个元素会是最大的数。
  3. 针对所有未排好序的元素重复以上步骤,直至没有任何一对数字需要比较为止。

时间复杂度为O(n^2),空间复杂度为O(1)。

源代码解析

快速排序

代码语言:java复制
public void quickSort(int[] arr, int left, int right) {
    if (left < right) {
        int pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex   1, right);
    }
}

private int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    int i = left   1, j = right;
    while (i <= j) {
        if (arr[i] <= pivot) {
            i  ;
        } else if (arr[j] > pivot) {
            j--;
        } else {
            swap(arr, i  , j--);
        }
    }
    swap(arr, left, j);
    return j;
}

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

代码分析:

  这是一个快速排序的实现,其中quickSort方法是主函数,而partition方法是分割函数,swap方法用于交换数组元素。

  在快速排序中,通过选择一个基准元素(通常是第一个元素),将整个数组分为两部分:小于基准的在左边,大于基准的在右边。接着,通过递归对左右两部分分别进行排序,最终得到一个有序数组。

  partition方法实现了数组的分割功能,通过维护两个指针i和j来将数组分为小于等于基准元素和大于基准元素的两部分。具体实现是,从左往右遍历数组,如果arri <= pivot,则i向右移动;否则,从右往左遍历数组,如果arrj > pivot,则j向左移动;否则,交换arri和arrj的值,i和j都往前移动。最终,将基准元素pivot与arrj交换位置,返回j作为新的基准元素的下标。

  swap方法用于交换数组元素的值,通过中间变量temp实现。

归并排序

代码语言:java复制
public void mergeSort(int[] arr, int left, int right) {
    if (left < right) {
        int mid = (left   right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid   1, right);
        merge(arr, left, mid, right);
    }
}

private void merge(int[] arr, int left, int mid, int right) {
    int[] temp = new int[right - left   1];
    int i = left, j = mid   1, k = 0;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k  ] = arr[i  ];
        } else {
            temp[k  ] = arr[j  ];
        }
    }
    while (i <= mid) {
        temp[k  ] = arr[i  ];
    }
    while (j <= right) {
        temp[k  ] = arr[j  ];
    }
    for (int l = 0; l < temp.length; l  ) {
        arr[left   l] = temp[l];
    }
}

代码分析:

  这段代码实现了归并排序算法,其中:

  • mergeSort方法是递归地将数组分成左右两部分进行排序,并将它们合并起来;
  • merge方法将两个已排好序的左右部分合并到一个新数组中。

具体分析:

  • mergeSort方法的参数leftright指定了当前排序的范围;
  • 首先,根据leftright计算出中间位置mid,然后递归地对左半部分和右半部分进行排序;
  • 接着,调用merge方法将左右两个已排好序的部分进行合并。

merge方法:

  • 首先创建一个临时数组temp,长度为right - left 1,用于存放排序后的元素;
  • 然后创建三个指针ijk,分别指向左半部分、右半部分和临时数组的最开头;
  • 依次比较左右两部分的元素大小,将较小的元素放入临时数组中,并将指针后移;
  • 当其中一个指针到达对应部分的末尾时,将另一部分剩下的元素依次放入临时数组中;
  • 最后将临时数组中的元素复制到原数组中指定部分中。

  整个过程中,时间复杂度为O(nlogn),其中归并的时间复杂度为O(n)。

冒泡排序

代码语言:java复制
public void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i  ) {
        for (int j = 0; j < arr.length - i - 1; j  ) {
            if (arr[j] > arr[j   1]) {
                swap(arr, j, j   1);
            }
        }
    }
}

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

代码分析:

  这段代码实现了一个冒泡排序算法,可以对一个整数数组进行排序。

  其中,bubbleSort方法使用了两层循环,第一层循环控制排序的轮数,第二层循环进行相邻元素之间的比较和交换操作。每进行一轮排序,就会将未排序部分中最大的元素“浮”到数组的最后面,因此排序后的数组是从小到大排列的。

  swap方法则用来交换数组中两个元素的位置,它接收三个参数,分别是数组、需要交换的两个元素的下标值。

  该算法的时间复杂度为O(n^2),因为需要进行两层循环,且每轮排序只能将一个元素放到它应该在的位置上。但它的空间复杂度为O(1),因为只需要用到常量级别的额外空间。

其中你也可以这么改写:

应用场景案例

快速排序

  1. 数组中有大量相同元素的排序。因为快速排序通过枢轴元素将数组分成两个子数组,可以保证相同元素被分到同一个子数组中,从而避免了重复排序。
  2. 大数据量的排序。快速排序的时间复杂度为O(nlogn),在大数据量的情况下,快速排序比其他排序算法更快。

归并排序

  1. 处理链表排序。因为链表访问速度很慢,而采用归并排序可以将访问最小化,从而加速排序。
  2. 大规模数据排序但又不能分配足够的内存。归并排序的空间复杂度为O(n),可以在内存受限的情况下完成大规模数据的排序。

冒泡排序

  1. 数据量较小且数组基本有序的情况下。冒泡排序的时间复杂度为O(n^2),在数据量小且数组基本有序的情况下,冒泡排序的性能可能比较优秀。

优缺点分析

快速排序

优点:

  1. 时间复杂度为O(nlogn),在大规模数据排序的情况下性能非常优秀。
  2. 在处理有大量重复数据的情况下,快速排序性能比其他排序算法更优秀。

缺点:

  1. 对于基本有序的数据或有大量重复数据的数据时,快速排序的时间复杂度可能会退化到O(n^2)。
  2. 在最坏情况下,当数据已经排好序时,快速排序的时间复杂度也会退化到O(n^2)。

归并排序

优点:

  1. 时间复杂度为O(nlogn),在大规模数据排序的情况下性能非常优秀。
  2. 归并排序稳定,不会改变相同元素的相对顺序。

缺点:

  1. 空间复杂度为O(n),需要额外的存储空间。
  2. 归并排序对于小数据集的排序性能不如插入排序。

冒泡排序

  冒泡排序是一种简单的排序算法,具有以下优点和缺点:

优点:

  1. 算法思路简单,易于理解和实现;
  2. 对于小规模的数据排序效率高;
  3. 是一种稳定的排序算法,即在排序过程中相同元素的位置不会改变。

缺点:

  1. 对于大规模数据的排序效率较低,时间复杂度为O(n²),且性能不稳定;
  2. 冒泡排序需要进行多次比较和交换操作,每次比较都会产生交换,因此对于数据交换次数较多时不适用;
  3. 空间复杂度较高,需要使用额外的存储空间存储交换过程中的中间值。

排序对比结论

  根据如上三种排序方式对比,总的来说,快速排序和归并排序的时间复杂度相似,但快速排序的空间复杂度更佳。因此,在大规模数据排序的情况下,快速排序是最优的选择。另外,快速排序还适用于处理有大量重复数据的情况。

  冒泡排序的时间复杂度较高,不适用于大数据量的排序。但在数据量较小且数组基本有序的情况下,冒泡排序可能比较适合。

  总结来说,快速排序是最优的选择,在大规模数据排序的情况下性能非常优秀,适用于大多数情况。归并排序适用于处理链表排序和大规模数据排序但又不能分配足够的内存的情况。冒泡排序适用于数据量较小且数组基本有序的情况下。

总结

  本文介绍了快速排序、归并排序和冒泡排序三种常见的排序算法。从算法原理、时间复杂度、空间复杂度、优缺点以及适用场景等方面进行了分析和比较。快速排序时间复杂度为O(nlogn),空间复杂度为O(logn),在大规模数据排序的情况下性能最优。归并排序时间复杂度也为O(nlogn),但空间复杂度为O(n),适用于处理链表排序和大规模数据排序但又不能分配足够的内存的情况。冒泡排序时间复杂度较高,不适用于大数据量的排序,适用于数据量较小且数组基本有序的情况下。总的来说,根据不同的应用场景选择合适的排序算法可以提高排序的效率。

... ...

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!

⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

我正在参与我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

0 人点赞