一、什么是排序算法
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。
二、排序的稳定性
在了解排序算法之前,我们先了解下排序的稳定性: 所谓的排序稳定性是指在排序的前后,元素的相对次序是否发生改变。若相对次序发生改变,那么证明该排序算法是不稳定的。 举个🌰,数组a的值为[1,5,3,5,4],我们可以看到排序前加粗的5排在前一个未加粗的5后面,经过某种排序算法后数组a的值为[1,3,4,5,5],此时加粗的5排在了未加粗的5前面,这样元素的相对次序就发生了改变,所以这种排序算法的稳定性就是不稳定的。
三、基础的排序算法
1.冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。简单来说就是元素间两两相比较,当比对到数组中最后一个元素时,最大|最小的元素就落到数组中最后一个位置,再不断的重复此过程最终数组就变得有序了。
动图演示
(点击查看)
代码实现
先定义一个用于交换的方法,以便复用:
public static void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
冒泡排序:
//冒泡排序,时间复杂度O(n²),具有稳定性
public static void bubbleSort(int[] nums){
int len=nums.length-1;
for (int i=len;i>0;i--){
for (int j = 0; j <i ; j++) {
if (nums[j]>nums[j+1]){
swap(nums,j,j+1);
}
}
}
}
冒泡排序的时间复杂度为O(n²),又因为再对比时相等的值不会交换,所以具有稳定性。
2.选择排序
算法描述
选择排序是一种简单直观的排序算法,它也是一种交换排序算法,和冒泡排序有一定的相似度,可以认为选择排序是冒泡排序的一种改进。选择排序的原理就是:从未排序的数组中找到数组中的最大(小)值,放到未排序数组的起始位置,重复此过程最终整个数组就变成了一个有序数组。
动图演示
(点击查看)
代码实现
//选择排序,时间复杂度O(n²),不具有稳定性
public static void selectSort(int[] nums){
int len=nums.length-1;
for (int i = 0; i <=len ; i++) {
int minIndex=i;
for (int j = i+1; j <=len ; j++) {
if (nums[j]<nums[minIndex]){
minIndex=j;
}
}
swap(nums,i,minIndex);
}
}
通过选择排序的代码我们可以发现,选择排序相比于冒泡排序减少了核心的交换次数,有着一定的优化。但总体来讲选择排序的时间复杂度总体来说也是O(n²)
,选择排序不具备排序的稳定性。
3.插入排序
算法描述
插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。简单来说,就是从左到右去维护一个稳定的区间,再不断的根据后续的元素插入到区间内的对应位置,最终数组整体的元素就变得有序了。
动图演示
(点击查看)
代码演示
//插入排序,时间复杂度O(n²),具备稳定性,且常数项最小
public static void insertSort(int[] nums){
int len=nums.length-1;
for (int i = 1; i <=len; i++) {
for (int j = i-1; j>=0 ; j--) {
if (nums[j+1]<nums[j]){
swap(nums,j+1,j);
}
}
}
}
虽然插入排序的时间复杂度也为O(n²)
,但它的常数项确实最小的,它也是冒泡排序的一种改良,我们可以发现这种算法减少了无用的比较和交换,当数据量小的时候常数项也决定着排序算法的效率。
如,在JDK 7 java.util.Arrays所用的sort
方法的实现中,当待排数组长度小于47时,会使用插入排序。插入排序具备排序的稳定性。