冒泡排序
算法原理(从小到大排序为例):
- 将数组分成两部分,左边部分为已排序部分,右边部分为未排序部分
- 从未排序部分的右侧开始比较遍历,相邻两个元素比较,保证小元素在前,即如果num[j]>num[j+1],就做元素交换,否则不交换
- 交换到左右两部分的边界处,右边第一个元素即为右边部分最小元素,左边最后一个元素即为左边部分最大元素,由于左边部分是右边部分每次从小到大冒泡排序出来的,所以此时左边部分加上右边第一个元素为有序部分,有序部分的数组下标加一。
图片来源:https://blog.youkuaiyun.com/pzhtpf/article/details/7560294
空间复杂度O(1),时间复杂度O(n^2),属于稳定排序
代码如下:
/**
* 冒泡排序:从小到大排序,从后往前遍历
* space:O(1)
* time:O(n^2)
* 稳定排序
* @param nums
*/
public static void bubbleSort(int[] nums){
for(int i =0;i<nums.length;i++){
for(int j=nums.length-1;j>i;j--){
if(nums[j]<nums[j-1]) swap(nums,j,j-1);
}
}
}
private static void swap(int[] num,int i,int j){
int tmp = num[i];
num[i]= num[j];
num[j]= tmp;
}
插入排序
算法原理(从小到大排序为例):
- 将数组分成两部分,左边部分为已排序部分,右边部分为未排序部分
- 将未排序部分的第一个元素选为比较元素,在已排序部分进行比较遍历,从右往左,从大到小比较遍历,每次比较都将比较过的元素后移一位,直至 比较元素大于等于遍历元素,此时将比较元素加入数组
图片来源:极客时间,《数据结构与算法之美》12章
空间复杂度:O(1),时间复杂度:O(n^2),属于稳定排序
代码如下:
/**
* 插入排序:从小到大排序,从后往前遍历
* space:O(1)
* time:O(n^2)
* 稳定排序
* @param nums
*/
public static void insertionSort(int[] nums){
for(int i =1;i<nums.length;i++){
int j=i,tmp=nums[j--];//i表示无序部分的第一个元素下标,j--表示有序部分最后一个元素,从j--开始比较遍历
for(;j>=0;j--){
if(tmp<nums[j]){
nums[j+1] = nums[j];
}else{
break;//在有序部分找到对应位置,插入,退出循环
}
}
nums[j+1] = tmp;//要么是遍历到头,要么是找到位置
}
}
在真实的使用中,插入排序比冒泡排序使用更多,因为在比较遍历时,插入排序移动元素比冒泡排序更少,而真实使用场景中,排序数量一般不大,10,100,1000,复杂度分析中忽略的低阶,系数,常数得考虑到。后面的选择排序,本身是不稳定算法,先天就落了下乘
//冒泡3次
private static void swap(int[] num,int i,int j){
int tmp = num[i];
num[i]= num[j];
num[j]= tmp;
}
//插入1次
nums[j+1] = nums[j];
选择排序
算法原理(从小到大排序为例):
- 将数组分成两部分,左边部分为已排序部分,右边部分为未排序部分
- 从右侧未排序部分选择最小的元素,和未排序部分的第一个元素交换位置
空间复杂度:O(1),时间复杂度:最好/最坏/平均 都是 O(n^2),属于不稳定排序
如数组:5,4,5,2 在选择排序时,2与第一个5交换位置,相对顺序就被破坏了
代码如下:
/**
* 选择排序:从小到大排序
* space:O(1)
* time:O(n^2)
* 不稳定排序
* @param nums
*/
public static void selectionSort(int[] nums){
for(int i =0;i<nums.length;i++){
int minIndex = i;
for(int j=i+1;j<nums.length;j++){
if(nums[j]<nums[minIndex]){
minIndex = j;
}
}
swap(nums,i,minIndex);
}
}