我们先写一个交换方法,减少代码量
private static void swap(int a, int b) {
int swap = a;
a = b;
b = swap;
}
排序的稳定性
冒泡排序
原理
形象的把排序过程比喻成水中的泡泡向上冒,它重复的走访过要排序的元素列,一次比较两个相邻的元素,如果他们的顺序不符合我们的规定,那么进行交换。重复的走访直到没有相邻元素要进行交换,也就是元素列,已经完成排序。
图解过程
代码如下
public static void sortArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//内循环终止条件为数组长度减外循环i值,减少了重复的排序
for (int j = 1; j < arr.length - i; j++) {
if (arr[j - 1] < arr[j]) {
// 冒泡排序:每次比较相邻元素
swap(arr[j - 1], arr[j]);//调用交换方法
}
}
}
}
//或者是这种形式
public static void sortArray(int[] arr) {
for (int end = arr.length - 1; end > 0; end--) {
for (int start = 1; start <= end; start++) {
if (arr[start - 1] > arr[start]) {
swap(arr[start - 1], arr[start]);
}
}
}
}
优化:这种情况,当一个数组本来就是有序,或者部分有序时,仍然会值循环比较相邻元素大小。
public static void sortArray(int[] arr) {
for (int end = arr.length - 1; end > 0; end--) {
//sortIndex 的初识值在数组完全有序时有用,值为1(不满足end>0即可)可以控制当数组是完全有序时只经过1轮内循环就直接结束
int sortIndex = 1;
for (int start = 1; start <= end; start++) {
if (arr[start - 1] > arr[start]) {
swap(arr[start - 1],arr[start]);
//交换完成后,记录当前交换的位置。此内循环执行完毕,也就知道最后一次交换的位置(此位置后面的都无需在比较交换了,减少循环次数)
sortIndex = start;
}
}
//把最后一次交换的位置赋值给end,下一轮循环到end就结束了
end = sortIndex;
}
}
优化完成后:最坏、平均时间复杂度: 0(n2),,最好时间复杂度: 0(n)
根据拍讯稳定性定义可知,冒泡排序是稳定的排序算法,但是稍有不慎,稳定的排序算法也能被写成不稳定的排序算法,比如下面的冒泡排序代码是不稳定的,“<=”表示相等也交换,那么此时相对位置
就变化了
选择排序
原理
和冒泡排序一样通过名称就可以知道他是通过选择两个元素进行比较,让每一个元素依次和其他元素进行比较,如果不满足条件就交换位置,知道所有元素全部排序完成
图解过程
代码如下
public static int[] sortArray2(int[] arr) {
// 选择排序:从第i个开始一次和后面的比较
for (int i = 0; i < arr.length - 1; i++) {
//内循环开始位置是i+1,减少重复的排序
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] < arr[j]) {
swap(arr[i], arr[j]);//调用交换方法
}
}
}
return arr;
}
插入排序
插入排序
简单直观,将待插入元素依次插入已排好的元素列中,在插入的时候依次与排好序的元素列元素进行比较大小,记录插入点,如果不满足条件就进行交换
图解插入:
代码如下:
public static int[] sortArray3(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];// 记录待插入元素
int insertPos = -1;// 初始化待插入点位置
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > temp) {
arr[j + 1] = arr[j];// 移动元素
insertPos = j;// 更换(继续寻找)插入点
} else {
break;
}
}
if (insertPos != -1) {
arr[insertPos] = temp;// 在插入点插入
}
}
return arr;
}
快速排序
原理
简称快排,顾名思义他的排序效率是非常最快的,他通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
图解过程
代码
// 快速排序
public static void sortArray4(int[] arr, int left, int right) {
// 进行判断,如果左边索引比右边索引大,是不合法的
if (left > right) {
return;
}
// 定义变量保存基准数
int base = arr[left];
// 定义i、j指向左边、右边
int i = left;
int j = right;
// 当i和j不相遇进行检索
while (i != j) {
// 向由j从右向左检索比基准数小的,如果检索到比基准数小的数就停下
while (arr[j] >= base && i < j) {
j--;// j从右向左移动
}
// i从左向右检索比基准数大的数,检索到就停下
while (arr[i] <= base && i < j) {
i++;
}
//到这里i和j都停下了,就进行交换对应元素的位置
swap(arr[i], arr[j]);
}
//如果上面while循环条件不成立,会跳出循环往下执行,说明i和j相遇了,就把基准数和这相遇位置元素交换
arr[left]=arr[i];
arr[i]=base;
//基准数就归位了,归位后左边都比它大,右边都比他小
//在分别对基准数左右进行如上操作,层层递归
sortArray4(arr,left,i-1);
sortArray4(arr,j+1,right);
}
以上代码不能实现相同元素的数组排序。改进代码如下:思路也有点变化,但是大体一样
public void quickSort(int[] arr,int left,int right){
if(left>right){
return;
}
int i=left,j=right;
int base=arr[left];
while(i<j){
while(i<j&&arr[j]>=base){
j--;
}
if(i<j){
int tem=arr[j];
arr[j]=base;
base=tem;
}
while(i<j&&arr[i]<base){
i++;
}
if(i<j){
int tem=arr[i];
arr[i]=base;
base=tem;
}
}
quickSort(arr, left, i - 1);
quickSort(arr, j + 1, right);
}
效率比较
排序方法 | 最好时间 | 平均时间 | 最坏空间 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡 | O(n) | O(n*n) | O(n*n) | O(1) | 稳定 |
选择 | O(n*n) | O(n*n) | O(n*n) | O(1) | 不稳定 |
插入 | O(n) | O(n*n) | O(n*n) | O(1) | 稳定 |
快排 | O(nlgn) | O(nlgn) | O(n*n) | O(lgn) | 不稳定 |
通过测试排序发现了,快排效率最快,java.util.Arrays类中也是使用快速排序。