最近一直有在看java相关的资料更深入的学习进步,但是越往深处里面学习,越觉得自己缺少的东西还是有点多,要想搞好编程,数据结构和算法的功力是必不可少的,所以还是哪里不会写哪里,开整:
研究一下 冒泡排序:Bubble Sort
原理:比较两个相邻的元素,将数值大的元素交换至右端
思路: 依次比较相邻的两个数,将小数放在前面,大数放在后面。第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
第一趟比较完成后,最后一个数一定是数组中最大的一个数,所以第二趟比较的时候最后一个数不必参与比较
第二趟比较完成后,倒数第二个数也一定是数组中第二大的数,所以第三趟比较的时候最后两个数不参与比较
依次类推,每一趟比较次数-1;
…
举例: 排序数组:int[] arr ={7,3,2,5,4,1}
第一趟排序:
第一次排序:7和3比较,7大于3,交换位置:3,7,2,5,4,1
第二次排序:7和2比较,7大于2,交换位置:3,2,7,5,4,1
第三次排序:7和5比较,7大于5,交换位置:3,2,5,7,4,1
第四次排序:7和4比较,7大于4,交换位置:3,2,5,4,7,1
第五次排序:7和1比较,7大于1,交换位置:3,2,5,4,1,7
第二趟排序:
第一次排序:3和2比较,3大于2,交换位置:2,3,5,4,1,7
第二次排序:3和5比较,3小于5,不交换位置:2,3,5,4,1,7
第三次排序:5和4比较,5大于4,交换位置:2,3,4,5,1,7
第四次排序:5和1比较,5大于1,交换位置:2,3,4,1,5,7
第三趟排序:
第一次排序:2和3比较,2小于3,不交换位置:2,3,4,1,5,7
第二次排序:3和4比较,3小于4,不交换位置:2,3,4,1,5,7
第三次排序:4和1比较,4大于1,交换位置:2,3,1,4,5,7
第四趟排序:
第一次排序:2和3比较,2小于3,不交换位置:2,3,1,4,5,7
第二次排序:3和1比较,3大于1,交换位置:2,1,3,4,5,7
第五趟排序:
第一次排序:2和1比较,2大于1,交换位置:1,2,3,4,5,7
至此排序完成 :1, 2, 3, 4, 5, 7
总结: N个数字要排序完成,总共进行N-1趟排序,每 i 趟 排序比较(N-i) 比较,故用双重循环语句控制,外层控制 趟数,内层循环控制每趟比较的次数
// 控制排序的趟数
for(int i=1;i<arr.length;i++) {
// 控制 每趟 比较的次数
for(int j=0;j<arr.length-i;j++) {
// 交换位置
}
}
冒泡排序:每进行一趟排序都会找到最大的数,放在最后的位置,每进行一趟就可以少比较一次。
第一趟排序完成之后,最后一个数,必定是最大的值
第二趟排序完成之后,倒数第二个数,就是倒数第二大的数,最后一个数不参与比较排序
第三趟排序… 倒数最后两个不参与比较排序,也就是说每一趟少比较一次,一定程度上减少了算法比较的次数
时间复杂度:
若我们的数据是正序,只需要一趟即可完成排序,所需要比较的次数C和记录移动次数M均为最小值即
Cmin=n-1 = O(n)
Mmin=0
所以冒泡排序最好的时间复杂度为O(n)
[添加标志位,flag = true 在程序中则可以有最小Cmin]
若数据为倒序,需要进行n-1趟排序。每趟排序需要进行n-i 次比较(1≤i≤n-1),且每次比较都必须移动数据三次(每次交换位置需要三次移动记录)来达到数据的位置。此种情况下,比较和移动的次数均达到最大值
Cmax=n(n-1)/2 =O(n²)
Mmax=3*n(n-1)/2=O(n²)
所以冒泡排序最坏的时间复杂度为O(n²)
综上:冒泡排序的平均时间复杂度为: O(n²)
空间复杂度:
空间复杂度主要看在交换元素时那个临时变量所占的内存空间,
最优的空间复杂度就是开始元素顺序已经排好了,则空间复杂度为:0;
最差的空间复杂度就是开始元素逆序排序了,则空间复杂度为:O(n);
平均的空间复杂度为:O(1);
排序代码实现:
public class BubbleSort {
static int[] arry = {8, 5, 3, 2, 1, 7, 10, 9, 4, 13, 11};
public static void main(String[] args) {
for (int i = 1; i < arry.length; i++) { //外层循环控制排序趟数
// 内层循环 控制比较次数
for (int j = 0; j < arry.length-i; j++) {
if(arry[j]>arry[j+1]) {
int temp = arry[j];
arry[j] = arry[j+1];
arry[j+1] = temp;
}
}
}
System.out.println("排序后");
for (int i : arry) {
System.out.print(i + ",");
}
}
}
// 输出结果:
排序后
1,2,3,4,5,7,8,9,10,11,13,
代码亲测,自己手动敲的,
至此,正常情况下不想在往里面深究下去的看到这里就OK了。。。--------------------------------------------------------------
然而,我个人还是想再接着往下探究 ---->
那么问题来了,
什么是时间复杂度呢? 是怎么计算出来的呢?什么是O(n²) 了?
所以,咱们继续剖析:
再次查资料:
百度百科: 算法的时间复杂度是一个函数,它定性描述该算法的运行时间,
时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
这里面写的比较专业,需要高等数学功底才可以看的懂,像是使用用极限求解的某一种函数吧,
时间复杂度:说下个人的肤浅理解吧,字面意思呢就是执行该段代码所需要的时间的长短吧,当然是复杂度越小,执行代码消耗的时间越短越好了
关于这个里面的对应的函数就需要用高等数学中的函数来构建了: 目前对应的函数就是用O(n²) 来表示的
第一趟 比较5次 第二趟比较4次…
[5,4,3,2,1] 总共比较了 ∑n=1~5 = n(n-1)/2 = 15
按上面的这个例子 N =6 个 数字 排序总共比较了 C=15次
交换位置移动了M = [5,3,1,1,1]3 = 3(5+3+1+1+1)= 33次
故而 上面的这个例子中执行的时间复杂度,
综合大致上先认为是C+M= 15+33 = 48 来描述吧
这只是一种比较普通的例子,
在极端情况下:
正序:{1,2,3,4,5,6}
Cmin=n-1 当 n=6 时 Cmin = 5 次
Mmin=0
用代码来验证:添加flag =true
public class BubbleSort {
// 比较的次数
private static int compareCount = 0;
// 交换位置移动的次数
private static int sweepCount = 0;
static int[] arry = {1,2,3,4,5,6};
boolean flag = true;
public static void main(String[] args) {
for (int i = 1; i < arry.length; i++) { //外层循环控制排序趟数
// 内层循环 控制比较次数
for (int j = 0; j < arry.length-i; j++) {
compareCount++; // 记录比较的次数
if(arry[j]>arry[j+1]) {
int temp = arry[j];
arry[j] = arry[j+1];
arry[j+1] = temp;
flag = false;
sweepCount+=3; // 记录交换位置移动的次数
}
}
if(flag) break;
}
System.out.println("比较的次数compareCount="+compareCount);
System.out.println("交换位置移动的次数sweepCount="+sweepCount);
System.out.println("排序后");
for (int i : arry) {
System.out.print(i + ",");
}
}
}
// 输出结果:
比较的次数compareCount=5
交换位置移动的次数sweepCount=0
排序后
1,2,3,4,5,6,
倒序:{6,5,4,3,2,1}
Cmax=n(n-1)/2 =O(n²)
Mmax=3*n(n-1)/2=O(n²)
Cmax=n(n-1)/2 当n=6 时 最大的比较次数 = 15,
Mmax=3*n(n-1)/2 当n=6时 最大的移动次数= 45
代码验证:
public class BubbleSort {
// 比较的次数
private static int compareCount = 0;
// 交换位置移动的次数
private static int sweepCount = 0;
static int[] arry = {6,5,4,3,2,1};
public static void main(String[] args) {
for (int i = 1; i < arry.length; i++) { //外层循环控制排序趟数
// 内层循环 控制比较次数
for (int j = 0; j < arry.length-i; j++) {
compareCount++; // 记录比较的次数
if(arry[j]>arry[j+1]) {
int temp = arry[j];
arry[j] = arry[j+1];
arry[j+1] = temp;
sweepCount+=3; // 记录交换位置移动的次数
}
}
}
System.out.println("比较的次数compareCount="+compareCount);
System.out.println("交换位置移动的次数sweepCount="+sweepCount);
System.out.println("排序后");
for (int i : arry) {
System.out.print(i + ",");
}
}
}
// 输出结果:
比较的次数compareCount=15
交换位置移动的次数sweepCount=45
排序后
1,2,3,4,5,6,
由以上可以看出,
冒泡排序,在n相同的情况下,比较的次数
Cmin=n-1 = O(n)
Cmax=n(n-1)/2 = O(n²)
最终还是到了数学的函数公式上,函数 O(n²) 对应高等数学中的
至此剖析到了数学的函数上了。。。看来编程最底层的还是要看数学功底呀。。不说了,我也去学数学了。。