超详解常见的八种内部排序算法 -(一)冒泡排序
简单介绍
开篇
这是一篇小白开始接手写简单算法的小白文。写这边文章的主旨就是看到有小伙伴觉得算法一定会很难,纵观已有的文章,大多都是将整个处理代码帖出来的,让很多初次接触的小伙伴觉得不是很好理解。所以写这篇文章就是通过简单的示例+从特殊到一般的推导过程,帮助初次想要了解的小伙伴更好的理解算法。旨在消除小伙伴对于算法这一名词的莫名恐惧。
先简单说一下内部排序的算法类型
常见的内部排序算法
内部排序就是计算机内存中进行计算处理的算法,我们常见的内部排序算法为8种,其中可以按照不同的类型进行一些简单的分类,方便我们更好的理解,也比较容易建立我们的认知体系【金字塔原理】
术语解释
- 稳定 :如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
- 不稳定 :如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
- 时间复杂度 : 一个算法执行所耗费的时间。
- 空间复杂度 :运行完一个程序所需内存的大小,在现在都可以利用空间换取时间,空间复杂度不是我们考虑的首要选择。
——————————排序算法的归纳———————————
写在最开始的话
说明,所有的实现代码是用Java进行编写实现的,但是内部的思想是公用的
我们直接进入正题:
一 、冒泡排序
冒泡排序的思路分析
第一步 了解冒泡排序的思路
冒泡排序的思路是对于列表中的元素,两两之间进行比较,从左到右依次开始比较,上图来让理解更清晰一些:
约定默认顺序是从小到大
在这先给出最后的结论性代码,有不明白的小伙伴可以继续往下看推导过程,可以更好的理解冒泡排序。
结论代码
public static void bubbleSort(int[] arr){
//default sort is desc
// we known inner process is for i in arr[] length-1 ,any process a max recursion reduce 1
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]){
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
//打印每一次的循环内的结果
System.out.printf("The %d sorted result is \n",i);
System.out.println(Arrays.toString(arr));
}
}
//输出最终的结果
System.out.println("The last sort is "+ Arrays.toString(arr));
}
我们给定我们需要进行排序的一维数组:
[12, 4, 9, 112, -6, 5]
当我们拿到这样的数组的时候,我们先确定我们的策略,我们知道冒泡是两两之间进行比较,然后将较大的数放置在索引值较大的位置。就像气泡总是接近水面的泡泡体形是越大的,冒泡排序就很形象了。
第一轮循环
① 我们先进行第一次循环,我们从左到右,依次进行,第一次我们拿第一个元素12和第二个元素4 进行比较, 12 > 4 12>4 12>4 ,我们将12 和 4互换位置,得到结果 [ 4 , 12 , 9 , 112 , − 6 , 5 ] [4, 12, 9, 112, -6, 5] [4,12,9,112,−6,5];
② 接着我们拿第二个元素12和第三个元素 9 进行比较, 12 > 9 12>9 12>9 ,将较大的数调整到后边的位置,得到结果:[4, 9, 12, 112, -6, 5]继续下一轮;
③ 我们拿第三个元素12和第四个元素112 进行比较, 12 < 112 12<112 12<112 ,不用做位置的调整,得到结果:[4, 9, 12, 112, -6, 5],继续下一轮;
④ 我们拿第四个元素112和第五个元素 -6 进行比较, 112 > − 6 112>-6 112>−6 ,我们将较大的数放置在索引值大的位置,得到结果:得到结果:[4, 9, 12, -6, 112, 5]继续下一轮;
⑤我们拿第五个元素112和第六个元素5 进行比较,112>5 ,我们将较大的数放置在索引值大的位置,得到结果:得到结果:[4, 9, 12, -6, 5, 112]第一轮结束,我们就找到了第一个最大值。
第一轮循环我们一共花费了5次比较确定了第一个最大数,并将它放置在队列的最后一个位置。
第二轮循环
① 我们继续进行,第一次我们拿第一个元素4和第二个元素9 进行比较,4<9,我们不需要进行位置处理,得到结果[4, 9, 12, -6, 5, 112]
② 接着我们拿第二个元素 9 和第三个元素12 进行比较,我们不需要进行位置处理,得到结果:[4, 9, 12, -6, 5, 112]继续下一轮
③ 我们拿第三个元素12和第四个元素 -6 进行比较, 12 > − 6 12>-6 12>−6,我们交换两数的位置,得到结果:[4, 9, -6,12, 5, 112],继续下一轮
④ 我们拿第四个元素12和第五个元素5 进行比较, 12 > 5 12>5 12>5 ,我们将较大的数放置在索引值大的位置,得到结果:得到结果:[4, 9, -6, 5, 12, 112],我们在此就不用比较最后一个元素了,因为第一轮我们已经确认了最后一个元素就是最大的元素;我们确定了第二大的数字及其位置。
第二轮我们走了4步完成
我们上图更好的理解一下
第三轮循环
① 我们继续进行,第一次我们拿第一个元素4和第二个元素9 进行比较, 4 < 9 4<9 4<9,我们不需要进行位置处理,得到结果[4, 9, -6, 5, 12, 112]
② 接着我们拿第二个元素9和第三个元素**-6** 进行比较, 9 > − 6 9>-6 9>−6,我们交换两个数的位置,得到结果:[4, -6,9, 5, 12, 112]继续下一轮
③ 我们拿第三个元素9和第四个元素5 进行比较, 9 > 5 9>5 9>5,我们交换两个数的位置,得到结果:[4, -6, 5, 9, 12, 112],我们就得到了第三大个数值。
我们在这次循环中花费了3步处理
第四轮循环
① 我们继续进行,第一次我们拿第一个元素4和第二个元素 -6 进行比较, 4 > − 6 4>-6 4>−6,我们需要进行位置处理,将两个数交换位置,得到结果[ -6,4, 5, 9, 12, 112]
② 接着我们拿第二个元素4和第三个元素 5 进行比较, 4 < 5 4<5 4<5,我们不用处理两个数的位置,得到结果:[ -6,4, 5, 9, 12, 112],我们确定了第四大的数字,
我们在当次循环中进行了2次比较操作;
第五轮循环
① 我们继续进行,第一次我们拿第一个元素 -6 和第二个元素4 进行比较, − 6 < 4 -6<4 −6<4,我们不需要进行位置处理,得到结果[-6,4, 5, 9, 12, 112]
我们在当次循环中进行了1次比较操作;确定了第五大的数字,并将其放置在第2位,第一位是不是就是我们最小的数据和她的位置,就不需要在进行比较了,至此是不是排序都结束了。
1.我们进行排列的列表有6个元素,我们进行了5次循环,我们进行了n-1次的大循环
2.每次循环的内部操作的步骤是逐步减少的
第二步,代码推导实现
public static void bubbleSort(int[] arr){
// 第一轮循环
// i 的取值范围是从0-4,因为我们是拿当前和后一个比较的,当我们获取索引为4(第五个元素),我们可以拿到最后一个和当前元素比较的元素了
for (int i = 0; i < arr.length-1; i++) {
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("第一次循环的结果 \n");
System.out.println(Arrays.toString(arr));
// 第二轮循环
//我们第二轮值遍历前5个元素,最后一个元素已经确定不再参与比较,所以我们的i<arr.length-1-1
for (int i = 0; i < arr.length-1-1; i++) {
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("第二次循环的结果 \n");
System.out.println(Arrays.toString(arr));
// 第三轮循环
//我们第三轮值遍历前4个元素,最后2个元素已经确定不再参与比较,所以我们的i<arr.length-1-1-1
for (int i = 0; i < arr.length-1-2; i++) {
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("第三次循环的结果 \n");
System.out.println(Arrays.toString(arr));
// 第四轮循环
//我们第四轮值遍历前3个元素,最后3个元素已经确定不再参与比较,所以我们的i<arr.length-1-1-1-1
for (int i = 0; i < arr.length-1-3; i++) {
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("第四次循环的结果 \n");
System.out.println(Arrays.toString(arr));
// 第五轮循环
//我们第五轮值遍历前2个元素,最后4个元素已经确定不再参与比较,所以我们的i<arr.length-1-1-1-1
for (int i = 0; i < arr.length-1-4; i++) {
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
System.out.println("第五次循环的结果 \n");
System.out.println(Arrays.toString(arr));
//通过上边的代码我们可以总结出一个规律,就是对需要排列的列表有6个元素,我们进行了5次循环,我们进行了n-1次的大循环
/**每次循环的内部操作的步骤是逐步减少的,且减少的数我们来看下,
*第一次,arr.length-1-0
*第二次,arr.length-1-1
*第三次,arr.length-1-2
*第四次,arr.length-1-3
*第五次,arr.length-1-4
*那我们用for循环做五次大循环,循环次数为array.length-1 => for (int i = 0; i < arr.length-1; i++)
*大循环内部的小循环每一次可以看做arr.length-1-i => for (int j = 0; j < arr.length-1-i; j++)
*就得到了我们最终的代码
*/
}
最后的代码
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {12,4,9,112,-6,5};
bubbleSort(array);
System.out.println("The last sort is "+ Arrays.toString(arr));
}
//used array to receive params
public static void bubbleSort(int[] arr){
//default sort is desc
// we known inner process is for i in arr[] length-1 ,any process a max recursion reduce 1
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]){
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
}
我们在分析的时候是不是发现第五轮循环是无效的,那我们下边进行优化一下
1.我们进行排列的列表有6个元素,我们进行了5次循环,我们进行了n-1次的大循环
2.每次循环的内部操作的步骤是逐步减少的
3.如果某一趟中没有发生交换就跳出循环
//used array to receive params
public static void bubbleSort(int[] arr){
//生命一个标识记录循环是否进行了数据交换
boolean flag =false;
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
//我们处理是默认从小到大,如果想要从大到小,倒序排列,我们只需要变更我们的判断条件:arr[j]<arr[j+1]
if(arr[j]>arr[j+1]){
flag = true;
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
if(!flag){
break;
}else{
flag=false;
}
}
}
冒泡排序到这儿就结束了,如果有什么不对的地方请大家斧正。