交换排序
''交换'' 就是 : 在序列中 根据 两个元素"键值""的大小 来交换两个元素在序列中的位置; 而 "交换排序" 就是 让 "键值"大的 向序列的末尾移动 , "键值"小的 向序列的前面移动;
(像是我们上体育课时排队, 排成一排 (序列) , 老师喊道:"高的往后站,低的往前" , 这时长得高的同学就往后站,低的同学就往前站 ; 在这里 "身高" 就是 "键值" , 我们按 身高来排序, 这时候如果有高的同学站前面,低的同学站后面,此时就交换这两个同学的位置 , 来让高的在后面,低的在前面);
常见的交换排序
1. 冒泡排序
2. 快速排序
在学习快速排序之前,先学习冒泡排序,简单理解一下什么是"交换";
冒泡排序
冒泡排序比较简单易懂,像鱼吐泡泡一样,泡泡从小到大 ;
例如: 我们在按从低到高 排队 , 队伍里有一个高的同学 排在了前面 ;
(这个同学是20 , 应该排在最后面,不是排在第二)
用冒泡排序来排序:
1.用一个箭头,从第一个同学开始, 用箭头指向的同学和他右边的同学比较,如果箭头同学比右边同学 大,就和右边同学交换位置 , 如果箭头同学比右边同学小, 那么就不交换位置 ;
2.然后把箭头往后移动一位,继续比较箭头同学和右边同学的大小,大叫交换位置,小就不交换位置;
3.直到箭头移动到最后一个同学, 就代表这次的冒泡结束;
(每次冒泡的作用就是把最大的,给冒泡到最后面去)
4.一个序列里有n个,那么需要进行n-1次冒泡; (像上图,把20冒泡到最后面了,那么让把箭头继续指向第一个同学,继续冒泡,这里有7个同学,那么需要重复进行6次冒泡);
冒泡原理
冒泡的原理: 就是每次冒泡,都把最大的数给移动到后面去, 为什么有n个元素就进行n-1次冒泡?
举个例子: 有一个数组 arr = [7 , 6, 5 , 4 , 3 , 2 , 1] ; 我要让数组arr有序(从小到大排序) ;
用冒泡排序排序:
第一次冒泡: [ 6, 5 , 4 , 3 , 2 , 1 , 7 ] ; (把最大的7给移动到最后面了)
第二次冒泡: [ 5 , 4 , 3 , 2 , 1 , 6 , 7 ] ; (把除了7,最大的6,给移动到后面了)
第三次冒泡: [ 4 , 3 , 2 , 1 , 5 , 6 , 7 ] ; (把除了6,7 , 最大的5 给移动到后面了)
第四次冒泡: [ 3 , 2 , 1 ,4 , 5 , 6 , 7 ] ; (把除了5,6,7, 最大的4 给移动到后面了)
第五次冒泡: [ 2 , 1 , 3 ,4 , 5 , 6 , 7 ] ; (把除了4,5,6,7, 最大的3 给移动到后面了)
第六次冒泡: [ 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ; (把除了3,4,5,6,7, 最大的2 给移动到后面了)
最后: 7个数 , 我们把前 6 个最大的数给冒泡了, 排序好了, 只剩下一个 1 , 就没有必要再进行冒泡了;
冒泡排序的复杂度
时间复杂度: O(N^2) ,序列长 n , 就进行n-1次冒泡 , 每次要比较n-1次;结合起来就是(n-1)*(n-1);
空间复杂度: O(1) , 因为是在原来的序列上进行操作, 没有什么额外申请空间 ;
稳定性: 稳定 ;
代码
import java.util.Arrays;
public class Test2 {
public static void main(String[] args) {
int[] array = {12 , 3, 50 , 33 , 21 , 20 , 17 , 15 , 9} ; // 定义一个数组供我们排序 ;
// 调用冒泡排序排序数组 ;
bubbleSort(array);
//打印数组查看数组里元素排序的怎么样了
System.out.println(Arrays.toString(array));
}
//冒泡排序
public static void bubbleSort(int[] array){
//记录数组长度, 用来排序
int len = array.length - 1 ; // 因为我们要防止 指针 越界, 所有要-1;
for (int i = 0; i < len; i++) {
//这里的j就相当于箭头, 箭头元素 和 箭头右边的元素进行比较 ;
for(int j = 0 ; j < len ; j ++){
// 如果箭头元素,大于右边元素就交换两个元素的位置 ;
if(array[j] > array[j+1]){
swap(array,j, j+1);
}
}
}
}
//交换数组元素位置的方法
private static void swap(int[] array , int x , int y){
int tmp = array[x];
array[x] = array[y];
array[y] = tmp ;
}
}
冒泡排序的小优化
1.减少比较次数
一个 数组 arr = [17 , 26, 5 , 9 , 8 , 12 , 1] ; 对他进行一次冒泡 ;
arr = [17 , 26, 5 , 9 , 8 , 12 , 1] ; 一次冒泡后 ;
arr = [17 , 5 , 9 , 8 , 12 , 26] ; 最大26被移动到了最后 ;
此时,继续对数组进行冒泡 ;
这时候,第6次冒泡里, 17 还有和 26 比较的 必要吗?
没有必要了, 26是序列里最大的数,我们前面已经将26冒泡到最后面了, 17就没必要再和26比较了;
所以第6次比较可以省略了,不需要17 和 26 比较了 ;
将17冒泡后, 第3大的数是 12 , 这时候 , 12 还有 和 17 跟 26 比较的必要吗?
后面的[17,26] 里已经是排序好的了, 没必要在对这些已经排序好的数进行比较了 ;
所以我们的优化是循环里的 j 进行优化, 让 j 不进入到 已经排序好的数; 这样就不对已经有序的数进行比较 , 减少比较次数 ;
for(int j = 0 ; j < len - i ; j ++){ // 让 j < len - i , 就不会再和已经有序的数进行比较
if(array[j] > array[j+1]){
swap(array,j, j+1);
}
2. 序列有序的优化
除了上面的 减少比较次数 , 还有 一种 情况 -> "序列已经有序"" ;
如果序列里的元素已经有序了 , 那么还有必要对序列进行排序吗? 没必要了 ;
冒泡排序是通过两个数比较之后,进行交换或不交换,来让序列有序的 ;
如果序列里的数都是有序的, 那么就不会出现交换, 没有交换过,就代表序列里已经有序了,可以直接停止冒泡排序了 ;
定义一个: flg 变量 , 如果发生交换操作,就让 flg = true, 每次冒泡后判断一下flg, 如果flg == false ,
就返回,没必要继续冒泡下去了 ;
for (int i = 0; i < len; i++) { // 定义一个 flg , 如果 发生交换 , 那么就让flg =true; boolean flg = false ; for(int j = 0 ; j < len - i ; j ++){ if(array[j] > array[j+1]){ flg = true ; // 发生交换,让flg = true; swap(array,j, j+1); } } if(flg == false){ // 判断一下如果,没有发生交换, 就直接返回; return ; } }
优化后的总代码
import java.util.Arrays;
public class Test2 {
public static void main(String[] args) {
int[] array = {12 , 3, 50 , 33 , 21 , 20 , 17 , 15 , 9} ; // 定义一个数组供我们排序 ;
// 调用冒泡排序排序数组 ;
bubbleSort(array);
//打印数组查看数组里元素排序的怎么样了
System.out.println(Arrays.toString(array));
}
//冒泡排序
public static void bubbleSort(int[] array){
//记录数组长度, 用来排序
int len = array.length - 1 ; // 因为我们要防止 指针 越界, 所有要-1;
for (int i = 0; i < len; i++) {
// 定义一个 flg , 如果 发生交换 , 那么就让flg =true;
boolean flg = false ;
for(int j = 0 ; j < len - i ; j ++){
if(array[j] > array[j+1]){
flg = true ; // 发生交换,让flg = true;
swap(array,j, j+1);
}
}
if(flg == false){ // 判断一下如果,没有发生交换, 就直接返回;
return ;
}
}
}
//交换数组元素位置的方法
private static void swap(int[] array , int x , int y){
int tmp = array[x];
array[x] = array[y];
array[y] = tmp ;
}
}