一、冒泡排序
原理:
比较两个相邻的元素,将值大的元素交换到右边。
比如:
要排序数组:[10,1,35,61,89,36,55]
第一趟排序:
第一次排序:10和1比较,10大于1,交换位置 [1,10,35,61,89,36,55]
第二趟排序:10和35比较,10小于35,不交换位置 [1,10,35,61,89,36,55]
第三趟排序:35和61比较,35小于61,不交换位置 [1,10,35,61,89,36,55]
第四趟排序:61和89比较,61小于89,不交换位置 [1,10,35,61,89,36,55]
第五趟排序:89和36比较,89大于36,交换位置 [1,10,35,61,36,89,55]
第六趟排序:89和55比较,89大于55,交换位置 [1,10,35,61,36,55,89]
第一趟总共进行了6次比较,排序结果:[1,10,35,61,36,55,89]
最大的数跑到了最后,下一趟就不用再比较这个数。
第二趟排序:
第一次排序:1和10比较,1小于10,不交换位置 1,10,35,61,36,55,89
第二次排序:10和35比较,10小于35,不交换位置 1,10,35,61,36,55,89
第三次排序:35和61比较,35小于61,不交换位置 1,10,35,61,36,55,89
第四次排序:61和36比较,61大于36,交换位置 1,10,35,36,61,55,89
第五次排序:61和55比较,61大于55,交换位置 1,10,35,36,55,61,89
第二趟总共进行了5次比较,排序结果:1,10,35,36,55,61,89
第三趟排序:
第一次排序:1和10比较,1小于10,不交换位置 1,10,35,36,55,61,89
第二次排序:10和35比较,10小于35,不交换位置 1,10,35,36,55,61,89
第三次排序:35和36比较,35小于36,不交换位置 1,10,35,36,55,61,89
第四次排序:36和61比较,36小于61,不交换位置 1,10,35,36,55,61,89
第三趟总共进行了4次比较,排序结果:1,10,35,36,55,61,89
分析:N个数字要排序,总共进行N-1趟排序,每i趟的排序次数为(N-i)次。
时间复杂度:
冒泡排序总的平均时间复杂度为:O(n2) ,时间复杂度和数据状况无关。
代码实现:
/**
* 冒泡排序
* @Author Feng, Ge
*/
public static void bubbleSort(long[] arr){
long start = System.currentTimeMillis();
if(arr==null || arr.length < 2 ){
return;
}
for(int i=0; i<arr.length-1; i++){
System.out.println("第" + (i+1)+"趟");
for (int j=0; j<arr.length-i-1; j++){
if(arr[j] > arr[j+1]){
long temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
long end = System.currentTimeMillis();
System.out.println("冒泡排序耗时:" + (end-start));
}
public static void main(String[] args){
long[] arr = {
81,57,34,32,6,91,19,25,36,34};
System.out.println("排序前:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print("、");
}
System.out.println("");
bubbleSort(arr);
System.out.println("排序后:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print("、");
}
System.out.println("");
}
排序前:
81、57、34、32、6、91、19、25、36、34、
第1趟
第2趟
第3趟
第4趟
第5趟
第6趟
第7趟
第8趟
第9趟
冒泡排序耗时:1
排序后:
6、19、25、32、34、34、36、57、81、91、
上面的是最原始的冒泡排序,总共9趟才完成排序,下面是冒泡的改进版:
public static void bubbleSort(long[] arr) {
long start = System.currentTimeMillis();
if (arr == null || arr.length < 2) {
return;
}
boolean flag = true;
for (int i = 0; i < arr.length - 1; i++) {
flag = true;
System.out.println("第" + (i+1)+"趟");
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
long temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = false;
}
}
if(flag){
break;
}
}
long end = System.currentTimeMillis();
System.out.println("冒泡排序耗时:" + (end - start));
}
排序前:
81、57、34、32、6、91、19、25、36、34、
第1趟
第2趟
第3趟
第4趟
第5趟
第6趟
冒泡排序耗时:0
排序后:
6、19、25、32、34、34、36、57、81、91、
增加flag可以使得原本就有序的趟不用在执行,能有效减少不必要的比较,这里改进后只执行了6趟。
二、快速排序
快速排序是冒泡排序的改进版,比冒泡算法快很多(为啥快这么多,一会讲讲完原理和代码就会明白)。
原理:
快速排序首先在这个序列中随便找一个数作为基准,然后把小于这个基准的数放在基准的左边,大于基准的放在右边,然后分别在左右两边选择新的基准,重复上面的过程(对应代码里面的递归)。
比如:{5,7,6,1,9,10,4,2,3,8}这个数组
先在这个数组选择一个数作为基准,一般选择第一个,就选5作为基准。
采用双向指针的形式,定义分别指向数组的最开始和最尾端的位置:
首先将j向左移,直到j指向的数小于5;
再将i向右移,直到i指向的数大于5。最终i指向7,j指向3。
将3和7交换,数组变为{5,3,2,1,9,10,4,6,7,8}。第一次交换结束。
接下来继续探测、交换,探测、交换…
第二次交换结束后数组变为{5,3,2,1,4,10,9,6,7,8}。
j指向9的位置,i指向4的位置,j继续向左移动,直到i的位置才找到小于5的值。
此时i=j,我们只需将基准数落在这个位置:将5和4的值交换。数组变为{4,3,2,1,5,10,9,6,7,8}。
至此,第一轮交换结束。
数组被划分为两个区,基准数5左边是小于5的{4,3,2,1},基准右边是大于5的{10,9,6,7,8}。
再分别对这两个区进行第二轮交换。整个过程如下:
再比如上面冒泡排序中的数组,快速排序过程如下(圈起来的为所选的基准数):
时间复杂度:
快速排序最优的情况下时间复杂度为:O( nlogn );
快速排序的平均时间复杂度也是:O(nlogn);
快速排序最差的情况下时间复杂度为:O( n^2 )
代码实现:
public static void quickSort(long[] arr,int low,int high){
int i,j;
long temp;
long t;
if(low>high){
return;
}
i=low;
j=high;
//temp就是基准位
temp = arr[low];
while (i<j) {
//先看右边,依次往左递减
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,依次往右递增
while (temp>=arr[i]&&i<j) {
i++;
}
//如果满足条件则交换
if (i<j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
public static long[] createArray(int len, int max) {
long[] arr = new long[len];
for (int i = 0; i < arr.length; i++) {
arr[i] = (long) (Math.random() * max);
}
return arr;
}
public static void main(String[] args){
long []arr=createArray(10, 100);
System.out.println("排序前:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print("、");
}
System.out.println("");
quickSort(arr,0, arr.length-1);
System.out.println("排序后:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print("、");
}
System.out.println("");
}
排序前:
8、24、3、80、11、72、30、65、71、43、
排序后:
3、8、11、24、30、43、65、71、72、80、
三、选择排序
原理:
先找到数组中最小的元素,然后将它和数组的第一个元素交换位置(如果这个元素其实就是自己本身的话,那么就和自己本身进行交换)。然后在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置,以此循环下去,整个排序过程只需要遍历 n-1 趟。
时间复杂度:
java选择排序算法是一种不稳定的算法,它的时间复杂度为 O(n2),空间复杂度为 O(1)
代码实现:
public class SelectSortDemo {
public static long[] createArray(int len, int max) {
long[] arr = new long[len];
for (int i = 0; i < arr.length; i++) {
arr[i] = (long) (Math.random() * max);
}
return arr;
}
public static void main(String[] args) {
long[] arr = createArray(10, 100);
System.out.println("排序前:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print("、");
}
System.out.println("");
selectSort(arr);
System.out.println("排序后:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
System.out.print("、");
}
System.out.println("");
}
public static long[] selectSort(long[] data) {
if (data.length == 0 || data.length == 1) {
return data;
}
for (int i = 0; i < data.length - 1; i++) {
// 假设最开始时,i位置的数最小
int min = i;
for (int j = i + 1; j < data.length; j++) {
if (data[min] > data[j]) {
// 此时最小的位置移动到j,最小的数即data[j]
min = j;
}
}
// 调换min位置和i位置的值(若一轮比较下来都没有变更最小值的索引,即min==i,则无需调换顺序)
if (min != i) {
long temp = data[min];
data[min] = data[i];
data[i] = temp;
}
}
return data;
}
}
排序前:
70、77、52、24、81、45、69、78、45、69、
排序后:
24、45、45、52、69、69、70、77、78、81、