冒泡排序
冒泡排序算法运行起来非常慢,但在概念上它是排序算法中最简单的,因此冒泡排序算法在刚开始研究排序技术时是一个非常好的算法。
思路:
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
-
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
-
针对所有的元素重复以上的步骤,除了最后一个。
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
package com.cym.sort.bubble_sort;
/**
* 冒泡排序
*/
public class BubbleSort {
public static void sort(int [] arr){
/**
* 第一层循环表示需要循环几次才能排好序
* 第二层循环表示把符合要求的数据交换
*/
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
//定义临时变量
int temp = arr[j];
//当前面的数比后面的数大的时候就交换,这一轮下来就可以或者这数组中的最大值
if (arr[j] > arr [j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
public static void printArray(int [] array){
for (int arr :array){
System.out.print(arr+"\t");
}
System.out.println();
}
public static void main(String[] args) {
int [] array = {11,10,3,43,12,64,21,3,6,9};
System.out.println("排序前:");
printArray(array);
sort(array);
System.out.println("排序后:");
printArray(array);
}
}
输出
排序前:
11 10 3 43 12 64 21 3 6 9
排序后:
3 3 6 9 10 11 12 21 43 64
选择排序
选择排序改进了冒泡排序,将必要的交换次数从O(N2)减少到O(N)。不幸的是比较次数仍保持为O(N2)。 然而,选择排序仍然为大记录量的排序提出了一个非常重要的改进,因为这些大量的记录需要在内存中移动,这就使交换的时间和比较的时间相比起来,交换的时间更为重要。
package com.cym.sort.select_sort;
/**
* 思路分析:
* 对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换;
* 接着对不包括第一个记录以外的其他记录进行第二轮比较,得到最小的记录并与第二个记录进行位置交换;
* 重复该过程,直到进行比较的记录只有一个时为止。
*/
public class SelectSort {
public static void selectSort(int[] array) {
int k;//定义变量来存储最小值的索引
for (int i = 0; i < array.length; i++) {//外层循环遍历的次数
//让k索引从外层循环i开始,{5,2,3},如第一次循环比较是第一个数,第二次循环比较的第二个
k = i;
for (int j = i + 1; j < array.length; j++) {//内层循环获得最小值的下标
//如果array[j] < array[k]的值要小
if (array[j] < array[k]) {
k = j;//交换索引,一轮下来就能取到最小值的索引
}
}
if (i < k) {//把最小值放在未排序的第一位
int temp = array[i];
array[i] = array[k];
array[k] = temp;
}
}
}
public static void printArray(int [] array){
for (int arr :array){
System.out.print(arr+"\t");
}
System.out.println();
}
public static void main(String[] args) {
int [] array = {11,10,3,43,12,64,21,3,6,9};
System.out.println("排序前:");
printArray(array);
selectSort(array);
System.out.println("排序后:");
printArray(array);
}
}
输出
排序前:
11 10 3 43 12 64 21 3 6 9
排序后:
3 3 6 9 10 11 12 21 43 64
插入排序
在大多数情况下,插入排序算法是简单排序算法中最好的一种。虽然插入排序算法仍然需要O(N2)的时间,但是在一般情况下,它要比冒泡排序快一倍,比选择排序还要快一点。尽管它比冒泡排序算法和选择排序算法都更麻烦一些,但它也并不很复杂。它经常被用在较复杂的排序算法的最后阶段,例如快速排序。
package com.cym.sort.insert_sort;
/**
* 插入排序
*/
public class InsertSort {
/**
* 定义插入排序的方法int [] arr = {2,3,7,9,5,4,7,3};
* 分析:
* 插入排序开始拿后面的数和前面的数逐个比较,如果符合要求的交换数据
* 第一层for循环主要是确定该数组要循环几次(如上述的数组:从索引为1的开始,需要遍历7次)
* 第二层循环主要执行插入数据的操作
*/
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
//定义临时变量来储存将要插入的数据
int temp = arr[i];
//j数组用来存放排好序的数组
int j = i - 1;
/*
* 分析:{2,3,7,9,5,4,7,3},2,3,7,9都不需要变动(没有符合while的条件),所以从5开始分析
* i : 4
* j : 3
* temp: 5
* arr: 的变化情况 {2,3,7,9,5,4,7,3} --->{2,3,7,9,9,4,7,3}--->{2,3,7,7,9,4,7,3}(arr[j] == 3 < temp(5),
* 所以跳出了while循环,执行arr[j + 1] = temp;语句)--->arr[j] > {2,3,5,7,9,4,7,3}
* 这样子就实现了把5插入到合适的位置了
*/
while (j >= 0 && arr[j] > temp) {
//将索引j的值移到j+1处
arr[j + 1] = arr[j];
//把条件往前移一位
j--;
}
//把临时变量插入到j+1处
arr[j + 1] = temp;
}
}
public static void printArray(int[] array) {
for (int arr : array) {
System.out.print(arr + "\t");
}
System.out.println();
}
public static void main(String[] args) {
int[] array = {11, 10, 3, 43, 12, 64, 21, 3, 6, 9};
System.out.println("排序前:");
printArray(array);
insertSort(array);
System.out.println("排序后:");
printArray(array);
}
}
输出:
排序前:
11 10 3 43 12 64 21 3 6 9
排序后:
3 3 6 9 10 11 12 21 43 64
小 结:
排序包括比较数组中数据项的关键字和移动相应的数据项(实际上,是数据项的引用),直到它们排好序为止。
这三种算法的时间复杂度都是O(n2)。 不过,某些情况下某个算法可以比其他算法快很多。
冒泡排序算法是效率最差的算法,但它最简单。
如果具有相同关键字的数据项,经过排序它们的顺序保持不变,这样的排序就是稳定的。
几种简单排序之间的比较:
一般情况几乎不太使用冒泡排序算法。
选择排序虽然把交换次数降到了最低,但比较的次数仍然很大。当数据量很小,并且交换数据相对于比较数据更加耗时的情况下,可以应用选择排序。
在大多数情况下,假设当数据量比较小或基本上有序时,插入排序算法是三种简单排序算法中最好的选择。对于更大数据量的排序来说,快速排序通常是最快的方法;
除了在速度方面比较排序算法外,还有一种对各种算法的衡量标准是算法需要的内存空间有多大。三种算法都可以“就地”完成排序,即除了初始的数组外几乎不需要其他内存空间。所有排序算法都需要一个额外的变量来暂时存储交换时的数据项。
重新编译实例程序,如bubbleSort.java, 用它给数量更大的数据排序,通过记录大数据量排序的时间,就可以明白不同排序算法的区别,以及在自己特定的系统中为不同数量级的数据排序所需要的时间。