稳定排序
前提:只有当在"二次"排序时不想破坏原先的次序,稳定性才有意义
概念:数据排序前后依然能保持相同数据的相对顺序
例子:对数组[3,3,1]进行选择排序算法进行排序,拿出最大的数3和最小的数1交换位置后变为[1,3,3],此时第一个3的下标本来是0,现在变成了2,排在了第二个3的后面.没有保持原先第一个3依旧保持在第二个3的前面的的相对顺序,这就是不稳定的排序
意义:如果我们只对一串数字排序,那么稳定与否确实不重要,因为一串数字的属性是单一的,就是数字值的大小.但是排序元素往往不只一个属性,像我们对一群人进行年龄排序,但是人除了年龄还有身高体重属性,在年龄相同时如果不想破坏原先身高体重的次序,就必须使用稳定的排序算法
例子:
像这样一组人的数据[[age:15,height:15,weight:45],[age:5,height:25,weight:55],[age:5,height:35,weight:65]]我们需要排序后保持 体重身高的由大到小顺序像这样[[age:5,height:25,weight:55],[age:5,height:35,weight:65],[age:15,height:15,weight:45]],如果我们使用选择排序这样的不稳定算法,排序一次得到的结果是[[age:5,height:35,weight:65],[age:5,height:25,weight:55],[age:15,height:15,weight:45]]这样人的体重身高的顺序就被破坏了
冒泡排序
时间复杂度:O(n²)
空间复杂度:O(1)
简单描述:数组中的数据两两之间相比,大的数排到最后面,像冒泡一样
具体实现逻辑:
(1)外层循环for控制循环次数
(2)内层循环for控制比较次数
(3)每次循环之后,找到这次比较中最大的数,比较次数减一
优化思路:如果一趟排序之后没有发生位置交换变化,那么此时就是有序的
详细实现演示:
//创建一个数组
int[] nums = {11,78,3,5,6,7};
//交换数据的中间变量
int temp;
//算法描述
//1. i从0开始,i与i+1进行比较,如果i>i+1就进行交换
//2. i不断增加,直到i<n-1(n是数组元素的个数,n-1是数组元素的最后一个元素),一趟下来,
// 可以让数组元素中最大值排在数组的最后面
//第一趟排序
//第一位和第二位比较
if (nums[0] > nums[1]){
//交换
temp = nums[0];
nums[0] = nums[1] ;
nums[1] = temp;
}
//第二位与第三位比较
if (nums[1] > nums[2]){
temp = nums[1];
nums[1] = nums[2];
nums[2] = temp;
}
//第三位和第四位比较
if (nums[2] > nums[3]){
temp = nums[2];
nums[2] = nums[3];
nums[3] = temp;
}
//第四位和第五位比较
if (nums[3] > nums[4]){
temp = nums[3];
nums [3]= nums[4];
nums[4] = temp;
}
//第五位和第六位比较
if (nums[4] > nums[5]){
temp = nums[4];
nums[4] = nums[5];
nums[5] = temp;
}
System.out.println(Arrays.toString(nums));
//第二趟排序(此时最后一位已经是数组最大的数了,不用再参与比较,这次我们找的是第二大的数)
//第一位和第二位比较
if (nums[0] > nums[1]){
//交换
temp = nums[0];
nums[0] = nums[1] ;
nums[1] = temp;
}
//第二位与第三位比较
if (nums[1] > nums[2]){
temp = nums[1];
nums[1] = nums[2];
nums[2] = temp;
}
//第三位和第四位比较
if (nums[2] > nums[3]){
temp = nums[2];
nums[2] = nums[3];
nums[3] = temp;
}
//第四位和第五位比较
if (nums[3] > nums[4]){
temp = nums[3];
nums [3]= nums[4];
nums[4] = temp;
}
System.out.println(Arrays.toString(nums));
简化代码:
//创建一个数组
int[] nums = {11,78,3,5,6,7};
//交换数据的中间变量
int temp;
//代码简化
//用双重循环
//外循环: 控制循环的次数 (4个数据排序,只需要循环3次,因为每次 循环都会找到最大的数,3次循环找到了三个数据,最后一个自然就是最小的)
//内循环: 控制每趟循环比较的次数
for (int i = 0; i < nums.length - 1; i++) {
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j+1]){
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(nums));
优化代码:
//从上面的例子可以看出,在数据足够乱的情况下需要进行五趟比较才能将数据完整排好序
//但是实际上第二趟之后数据已经是排好序的,我们的程序在第二躺之后还是会进行第三\四\五趟
//这里我们进行优化:
//当一趟排序中并没有出现交换位置.此时数据就是排好序的,直接退出来
//创建一个数组
int[] nums = {11,78,3,5,6,7};
//交换数据的中间变量
int temp;
int count = 0;
//如果发生了位置交换,记录下来
boolean isChange;
//外层循环是排序趟数
for (int i = 0; i < nums.length - 1; i++) {
//每趟排序重新初始化
isChange = false;
//内层循环是当前趟数需要比较的次数
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j+1]){
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
isChange = true;
}
}
//每一趟进行判断是否发生了位置交换
if (!isChange){
break;
}
//记录循环的趟数
count++;
}
System.out.println(Arrays.toString(nums));
System.out.println("循环的趟数为"+count);
选择排序
时间复杂度:O(n²)
空间复杂度:O(1)
简单描述:每次选出最大(或者最小)的数插入到末尾(或者起始)
具体实现:
(1)外层循环for控制次数
(2)内层循环for找出最大值的下标
(3)找到最大值下标后,进行交换
优化代码:同时获取最大值和最小值,然后分别插入尾部和首部
代码演示:
int[] nums = {2,2,1,5,3,4};
//第一趟排序
//假定max是最大的
int max = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
//使用中间变量进行变量交换
int temp;
temp = nums[5];
nums[5] = nums[3];
nums[3] = temp;
System.out.println(Arrays.toString(nums));
//第二趟排序(再次从数组中获取最大的数,除了已经排好的那个)
int max2 = 0;
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] > max2){
max2 = nums[i];
}
}
//再将获取到的最大值与数组倒数第二位交换
temp = nums[4];
nums[4] = nums[3];
nums[3] = temp;
System.out.println(Arrays.toString(nums));
代码简化:
//代码简化
int[] nums = {2,2,1,5,3,4};
//记录当前趟数的最大值下标
int index = 0;
//变量交换的第三方变量
int temp;
//外层循环控制需要排序的趟数
for (int i = 0; i < nums.length - 1; i++){
//每趟开始前把最大下标初始化
index = 0;
//内层循环控制遍历数组个数,并得到对大值下标
for (int j = 0; j < nums.length - i; j++){
if (nums[j] > nums[index]){
index = j;
}
}
//内循环结束,找到最大值
temp = nums[index];
nums[index] = nums[nums.length - i -1];
nums[nums.length - i - 1] = temp;
}
System.out.println(Arrays.toString(nums));
代码优化:
//优化
int[] nums = {2,2,1,5,3,4};
//最大值下标
int max = 0;
//最小值下标
int min = 0;
//变量交换的第三方变量
int temp;
int count = 0;
//外层循环
for (int i = 0; i < nums.length - 1; i++) {
count ++;
//初始化最大值最小值的下标
max = i;
min = i;
//内层循环
for (int j = i; j < nums.length - i; j++) {
if (nums[j] > nums[max]){
max = j;
}
if (nums[j] < nums[min]){
min = j;
}
}
//每次取完值判断两个下标的情况
// 1.最大值最小值下标重合,排序完成
// 2.最小值下标超出最大值下标
if (max == min || max < min){
break;
}
System.out.println(nums[max]);
//每趟循环完成,插入最大值最小值
temp = nums[max];
nums[max] = nums[nums.length - i- 1];
nums[nums.length - i - 1] = temp;
System.out.println(nums[min]);
temp = nums[min];
nums[min] = nums[i];
nums[i] = temp;
}
System.out.println("===========================");
System.out.println(count);
System.out.println(Arrays.toString(nums));
插入排序
快速排序
归并排序
希尔排序
堆排序
桶排序(基数排序)
(未完待续)
稳定算法:冒泡排序、插入排序、归并排序、基数排序
不稳定算法 :选择排序、快速排序、希尔排序、堆排序