排序算法
一、定义
排序就是将一组“无序”的对象按照某种规则使其变得“有序”的过程。
二、常见算法及分类
常见排序算法如下:
1.冒泡排序
2.选择排序
3.直接插入排序
4.希尔排序
5.快速排序
6.堆排序
7.归并排序
它们都属于内部排序,也就是只考虑数据量较小仅需要使用内存的排序算法。
分类:
1.插入排序(直接插入排序、希尔排序)
2.交换排序(冒泡排序、选择排序)
3.选择排序(选择排序、堆排序)
4.归并排序
三、说明
1.稳定:排序序列中如果a=b,原序列中a在b的前边,排序之后a仍然在b的前边
2.不稳定: 排序序列中如果a=b,原序列中a在b的前边,排序之后a可能在b的后边
1.冒泡排序(Bubble Sort)
1.1 算法描述
在要排序的序列中,对当前还未排好序的范围中的所有数,相邻的两个数依次进行比较,让较大的数向上冒(向后走),较小的向下沉(向前走),也就是如果前边的数大于后边的就交换两个数的位置。这样的话每一轮就会将当前还未排好序的范围中最大的数排到当前范围的最后边。冒泡排序是一种稳定的排序算法。
动图演示:
1.2 算法实现
//冒泡排序
import java.util.Arrays;
public class BubbleSort {
public static int[] bubbleSort(int[] a){
for(int i = 1; i < a.length; i++){//外层循环控制需要比较多少轮
for(int j = 0; j < a.length - i; j++){//内层循环控制每一轮比较具体的比较过程
if(a[j] > a[j + 1]){//如果前边的数大于后边的交换两数位置
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
return a;
}
public static void main(String[] args) {
int[] a = {24, 63, 15, 33, 51, 35, 76};
System.out.println(Arrays.toString(bubbleSort(a)));
}
}
1.3 复杂度分析
最佳时间复杂度:O(n)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
空间复杂度:O(1)
2.选择排序(Selection sort)
2.1 算法描述
选择排序是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
动图演示:
2.2 算法实现
import java.util.Arrays;
public class SelectionSort {
public static int[] selectSort(int[] a){
for(int i = 0; i < a.length; i++){//外层循环代表找到的未排序范围中最小元素存的位置
int min = i;//初始假定当前位置的值就是最小的,保存当前索引
for(int j = i + 1; j < a.length; j++){//内循环代表在未排序范围中查找最小元素所在下标
if(a[min] > a[j]){//更新下标
min = j;
}
}
if(min != i){//如果最小值下标不是当前位置,更新此位置的值
int temp = a[min];
a[min] = a[i];
a[i] = temp;
}
}
return a;
}
public static void main(String[] args) {
int[] a = {24, 63, 15, 33, 51, 35, 76};
System.out.println(Arrays.toString(selectSort(a)));
}
}
2.3 复杂度分析
最佳时间复杂度:O(n^2)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
空间复杂度:O(1)
3. 直接插入排序(Insertion Sort)
3.1 算法描述
1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤2~5
直接插入排序是一种稳定的排序算法
动图演示:
3.2 算法实现
//插入排序
import java.util.Arrays;
public class InsertionSort {
public static int[] insertionSort(int[] a){
//开始可以认为第0个位置是排好序
for(int i = 0; i < a.length - 1; i++){//外层循环表示要移动到的元素位置
int index = i;//已排好序范围的最后一个元素
int value = a[i + 1];//记录要移动的值
while(index >= 0 && a[index] > value){//在index >= 0的条件下只要index位置的值大于value,就将index位置的值向后挪
a[index + 1] = a[index];
index--;
}
a[index + 1] = value;//找到值小于value的位置或者到了过了0位置,那么value就应该排在这个位置的后边
}
return a;
}
public static void main(String[] args) {
int[] a = {24, 63, 15, 33, 51, 35, 76, 33};
System.out.println(Arrays.toString(insertionSort(a)));
}
}
3.3 复杂度分析
最佳时间复杂度:O(n)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
空间复杂度:O(1)
4. 希尔排序(ShellSort)
4.1算法描述
希尔排序法又称缩小增量法。是一种不稳定的排序算法。
希尔排序法的基本思想是:先选定一个整数gap,把待排序数组中所有记录分成多个组,所有距离为选定整数的记录分在同一组内,并对每一组内的记录进行直接插入排序。然后,取下一个整数(一般取上一个整数 / 2 或 / 3),重复上述分组和排序的工作。当到达=1时,再对所有数据进行直接插入排序。
1.希尔排序是对直接插入排序的优化。
2.当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
希尔排序直接解释不好理解,结合图来说更加直观
如图:
第一次排序gap = 5,数组中距离为5的元素为一组,也就是图中相同颜色为一组,组内进行直接插入排序;
第二次排序gap = (gap / 2) = 2,相同颜色为一组,继续进行组内直接插入排序;
第三次排序gap = 1,此时的数组更接近有序,进行最后一次排序。
4.2 算法实现
import java.util.Arrays;
public class ShellSort {
public static int[] shellSort(int[] arr){
int len = arr.length;
int gap = len / 2;
while(gap > 0){
for(int i = 0; i < len - gap; i++){
int index = i;//已排好序范围的最后一个元素
int value = arr[index + gap];//要移动的元素
while(index >= 0 && arr[index] > value){//只要index位置的值大于value,将index位置的元素往后移
arr[index + gap] = arr[index];//往后移
index -= gap;//向前走
}
arr[index + gap] = value;//插入元素
}
gap /= 2;
}
return arr;
}
public static void main(String[] args) {
int[] a = {24, 63, 15, 33, 51, 35, 76};
System.out.println(Arrays.toString(shellSort(a)));
}
}
4.3 复杂度分析
最佳时间复杂度:O(n)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(n^1.3)
空间复杂度:O(1)