这是第一次写博客,我作为一个程序小白也不知道写点什么,所以就打算记录一下自己的一个学习过程以及对学习的理解吧!
下面是冒泡算法的详解:
package com.starryNight.test;
import java.util.Arrays;
/**
* 注释太tm多了,关键是你们要“详解”,能看懂就行,我就不在乎什么语文格式了
* @author starryNight
* @version 1.0.0
* */
public class Test {
/**
* 此方法为主要实现方法
* 用于对数组中的数据进行冒泡排序
* @param arr
* */
private static void bubbleSort_relese1_0(int arr[]){
//众所周知,冒泡算法的主要结构是这样的。。。。
int temp=0;
for (int i=0;i<arr.length;i++) {
for (int j=0;j<arr.length-1;j++) {
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
//ok,为什么要这么写呢?
//冒泡算法的原理:第一个数与第二个数进行比较,如果满足条件位置不变.
// 再把第二个数与第三个数进行比较,不满足条件则替换位置.
// 再把第三个数与第四个数进行比较,以此类推
//那为什么要减j<arr.length-1呢?以长度为5的数组举个例子,当数组第4个数比第5个数大时要实现前后交换,那么如下:
// arr[4]>arr[4+1]这样?好吧,看似没有问题,那么问题:长度为5的数组下标最大是几?
// 所以如果不减1的话,在比较的途中会发生数组越界,小伙伴们在实验时可以把-1去掉,然后看看是否会报如下异常:
// java.lang.ArrayIndexOutOfBoundsException
//那么执行过程中发生了什么呢?我们以一个数组{5,4,3,2,1}为例子
//我们将每一次外循环定义为outer loop,内循环定义为innern loop
//第一次outer loop i=0;i<5 第二次outer loop i=1;i<5
// 第一次inner loop j=0;j<4 循环完毕的结果:{4,5,3,2,1} 第一次inner loop j=0;j<4 循环完毕的结果:{3,4,2,1,5}
// 第二次inner loop j=1;j<4 循环完毕的结果:{4,3,5,2,1} 第二次inner loop j=1;j<4 循环完毕的结果:{3,2,4,1,5}
// 第三次inner loop j=2;j<4 循环完毕的结果:{4,3,2,5,1} 第三次inner loop j=2;j<4 循环完毕的结果:{3,2,1,4,5}
// 第四次inner loop j=3;j<4 循环完毕的结果:{4,3,2,1,5} 第四次inner loop j=3;j<4 循环完毕的结果:{3,2,1,4,5}
//第一次outer loop结束最终结果:{4,3,2,1,5} 第二次outer loop结束最终结果:{3,2,1,4,5}
//第三次outer loop i=2;i<5 第四次outer loop i=3;i<5
// 第一次inner loop j=0;j<4 循环完毕的结果:{2,3,1,4,5} 第一次inner loop j=0;j<4 循环完毕的结果:{1,2,3,4,5}
// 第二次inner loop j=1;j<4 循环完毕的结果:{2,1,3,4,5} 第二次inner loop j=1;j<4 循环完毕的结果:{1,2,3,4,5}
// 第三次inner loop j=2;j<4 循环完毕的结果:{2,1,3,4,5} 第三次inner loop j=2;j<4 循环完毕的结果:{1,2,3,4,5}
// 第四次inner loop j=3;j<4 循环完毕的结果:{2,1,3,4,5} 第四次inner loop j=3;j<4 循环完毕的结果:{1,2,3,4,5}
//第三次outer loop结束最终结果:{2,1,3,4,5} 第四次outer loop结束最终结果:{1,2,3,4,5}
//第五次outer loop i=4;i<5
// 第一次inner loop j=0;j<4 循环完毕的结果:{1,2,3,4,5}
// 第二次inner loop j=1;j<4 循环完毕的结果:{1,2,3,4,5}
// 第三次inner loop j=2;j<4 循环完毕的结果:{1,2,3,4,5}
// 第四次inner loop j=3;j<4 循环完毕的结果:{1,2,3,4,5}
//第五次outer loop结束最终结果:{1,2,3,4,5}
//ok,执行完毕,以上是我们{5,4,3,2,1}这个数组经过冒泡过后的最终结果
//嗯,有没有发现什么问题呢? 外循环第四次的时候就已经得到了我们想要的结果,既然如此为何要外循环5次呢?
//然后我们看看冒泡的定义:前一个叔和后一个数两两比较,小的前移大的后移。
// 我们这里是用的从小到大的顺序,每一次冒泡(结束一次外循环,我们称之为冒了一次泡,因为外循环一次结束过后最大的数会到最上面去,
//以此类推。。。)是不是最大的数跑最上面去了?既然如此当第四次泡冒完了,最小的泡泡(也就是数字)已经在最底下了。所以还需要多冒一次吗?
//那是否可以修改一下代码:i<arr.length-1,这样一来不就只冒四次泡了吗?完美
}
/**
* 这个方法,我们用来执行第一次优化的冒泡代码
* @param arr
* */
private static void bubbleSort_relese1_1(int arr[]){
//上个方法我们说到这5个数字冒四次泡,
int temp=0;
for (int i=0;i<arr.length-1;i++) {
for (int j=0;j<arr.length-1;j++) {
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
//结束完毕,结果和上面的一支,但是少了一次执行过程,因为四次之后最小的泡泡已经垫底,不需要再冒了,一下是结果:
//第一次outer loop i=0;i<5 第二次outer loop i=1;i<5
// 第一次inner loop j=0;j<4 循环完毕的结果:{4,5,3,2,1} 第一次inner loop j=0;j<4 循环完毕的结果:{3,4,2,1,5}
// 第二次inner loop j=1;j<4 循环完毕的结果:{4,3,5,2,1} 第二次inner loop j=1;j<4 循环完毕的结果:{3,2,4,1,5}
// 第三次inner loop j=2;j<4 循环完毕的结果:{4,3,2,5,1} 第三次inner loop j=2;j<4 循环完毕的结果:{3,2,1,4,5}
// 第四次inner loop j=3;j<4 循环完毕的结果:{4,3,2,1,5} 第四次inner loop j=3;j<4 循环完毕的结果:{3,2,1,4,5}
//第一次outer loop结束最终结果:{4,3,2,1,5} 第二次outer loop结束最终结果:{3,2,1,4,5}
//第三次outer loop i=2;i<5 第四次outer loop i=3;i<5
// 第一次inner loop j=0;j<4 循环完毕的结果:{2,3,1,4,5} 第一次inner loop j=0;j<4 循环完毕的结果:{1,2,3,4,5}
// 第二次inner loop j=1;j<4 循环完毕的结果:{2,1,3,4,5} 第二次inner loop j=1;j<4 循环完毕的结果:{1,2,3,4,5}
// 第三次inner loop j=2;j<4 循环完毕的结果:{2,1,3,4,5} 第三次inner loop j=2;j<4 循环完毕的结果:{1,2,3,4,5}
// 第四次inner loop j=3;j<4 循环完毕的结果:{2,1,3,4,5} 第四次inner loop j=3;j<4 循环完毕的结果:{1,2,3,4,5}
//第三次outer loop结束最终结果:{2,1,3,4,5} 第四次outer loop结束最终结果:{1,2,3,4,5}
// 观察上面得到的数据看似没有问题(如果你发现了问题也别说出来,就当你不知道,让我说!!!)。在第二次冒泡(也就是outer loop)中,5在
//第一次冒泡中就已经冒到了最上面去,这是不是意味着我们已经知道了5是最大的,但是还是傻不拉几的拿5前面的数和5比较了一次。而第三次冒泡中
//我们已经知道5是最大的,4是第二大的,然后我们却又傻不拉几的拿4和5比了一次,然后又拿4前面的数和4比了一次。第四次冒泡我怕就不说了。。
//所以当我们知道了最大的数字,就不需要拿最大的数字和前面的比较,知道了第二大的就不需要拿最大的和第二大的比,也不需要拿第二大的和前一个
//比。所以这意味着第一次冒泡我们无法确定谁最大,要两两比较4次,此时i的值为0。第二次冒泡,已近知道了最大的,所以只需两两比较3次,此时i
//的值为1。第三次冒泡,已近知道了最大的数,第二大的数,只需两两比较2次,此时i的值为2。第四次冒泡,我们已经知道了最大的,第二大的,第三大
//的,所以只需两两比较1次,此时i的值为3。那么如何靳准控制每次冒泡中两两比较的次数呢?修改代码:j<arr.length-1-i;
//这样一来第一次冒泡j<4-0,四次两两比较。第二次冒泡j<4-1,三次两两比较。第三次冒泡j<4-2,两次两两比较。第四次冒泡j<4-3,一次两两比
//较。完美
}
/**
* 此方法用于执行又一次优化的冒泡代码
* */
public static void bubbleSort_relese1_2(int arr[]){
//这是又一次优化的,原来是j<arr.length-1,现在是j<arr.length-1-i
int temp=0;
for (int i=0;i<arr.length-1;i++) {
for (int j=0;j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
//废话不多说,看数据:
//第一次outer loop i=0;i<5
// 第一次inner loop j=0;j<4-0 循环完毕的结果:{4,5,3,2,1}
// 第二次inner loop j=1;j<4-0 循环完毕的结果:{4,3,5,2,1}
// 第三次inner loop j=2;j<4-0 循环完毕的结果:{4,3,2,5,1}
// 第四次inner loop j=3;j<4-0 循环完毕的结果:{4,3,2,1,5}
//第一次outer loop结束最终结果:{4,3,2,1,5}
/*-----------------------------------------------*/
//第二次outer loop i=1;i<5
// 第一次inner loop j=0;j<4-1 循环完毕的结果:{3,4,2,1,5}
// 第二次inner loop j=1;j<4-1 循环完毕的结果:{3,2,4,1,5}
// 第三次inner loop j=2;j<4-1 循环完毕的结果:{3,2,1,4,5}
//第二次outer loop结束最终结果:{3,2,1,4,5}
/*-----------------------------------------------*/
//第三次outer loop i=2;i<5
// 第一次inner loop j=0;j<4-2 循环完毕的结果:{2,3,1,4,5}
// 第二次inner loop j=1;j<4-2 循环完毕的结果:{2,1,3,4,5}
//第三次outer loop结束最终结果:{2,1,3,4,5}
/*-----------------------------------------------*/
//第四次outer loop i=3;i<5
// 第一次inner loop j=0;j<4-3 循环完毕的结果:{1,2,3,4,5}
//第四次outer loop结束最终结果:{1,2,3,4,5}
//ok这样一来不就去掉了重复的比较么,但是还有没有什么可以改进的呢?
//请看版本bubbleSort_relese_2_0
}
private static void bubbleSort_relese_2_0(int arr[]){
//这里要讲到一个东西,就是for循环。
//for(表达式1(初始化变量,例如int i=0);表达式2(比较运算符,例如i<5);表达式3(自增变量,例如i++)){
// 语句块...
//}
//它的执行过程是这样的:
// 表达式1-->表达式2(若为真)-->语句块-->表达式3--表达式2(若为假)-->结束循环
//嗯,我们发现当两两比较结束过后,这里面的语句块执行完毕,表达式3会执行,然后由表达式2来决定是否继续循环。
//所以当我在语句块中做了我们想做的,即目的达到过后干嘛还浪费时间去执行表达式3,然后执行表达式2?直接break就好了。如下
int temp=0;
for (int i=0;i<arr.length-1;i++) {
boolean flag=true;
for (int j=0;j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=false;
}
}
if (flag) break;
}
//第一次outer loop i=0;i<5
// 第一次inner loop j=0;j<4-0 循环完毕的结果:{4,5,4,3,2}
// 第二次inner loop j=1;j<4-0 循环完毕的结果:{4,4,5,3,2}
// 第三次inner loop j=2;j<4-0 循环完毕的结果:{4,4,3,5,2}
// 第四次inner loop j=3;j<4-0 循环完毕的结果:{4,4,3,2,5}
//第一次outer loop结束最终结果:{4,4,3,2,5}
/*-----------------------------------------------*/
//第二次outer loop i=1;i<5
// 第一次inner loop j=0;j<4-1 循环完毕的结果:{3,4,2,1,5}
// 第二次inner loop j=1;j<4-1 循环完毕的结果:{3,2,4,1,5}
// 第三次inner loop j=2;j<4-1 循环完毕的结果:{3,2,1,4,5}
//第二次outer loop结束最终结果:{3,2,1,4,5}
/*-----------------------------------------------*/
//第三次outer loop i=2;i<5
// 第一次inner loop j=0;j<4-2 循环完毕的结果:{2,3,1,4,5}
// 第二次inner loop j=1;j<4-2 循环完毕的结果:{2,1,3,4,5}
//第三次outer loop结束最终结果:{2,1,3,4,5}
/*-----------------------------------------------*/
//第四次outer loop i=3;i<5
// 第一次inner loop j=0;j<4-3 循环完毕的结果:{1,2,3,4,5}
//第四次outer loop结束最终结果:{1,2,3,4,5}
}
public static void main(String[] args) {
//检验真理的时候到了,我们来看看代码是否可以正确执行
// int arr1[]={5,4,3,2,1};
// bubbleSort_relese1_0(arr1);
// System.out.println(Arrays.toString(arr1));
//
// int arr2[]={10,9,8,7,6,5,4};
// bubbleSort_relese1_1(arr2);
// System.out.println(Arrays.toString(arr2));
//
// int arr3[]={25,9,6,3,1,2,7};
// bubbleSort_relese1_2(arr3);
// System.out.println(Arrays.toString(arr3));
int arr4[]={10,9,8,7,6,5,4,3,2,1,0};
bubbleSort_relese_2_0(arr4);
System.out.println(Arrays.toString(arr4));
}
}