排序算法-冒泡排序
冒泡排序(Bubble Sort),应该算是比较简单也是很经典的排序算法。也比较好理解,先说一下结论,它的时间复杂度O(n^2),具有稳定性,重复遍历元素序列,依次比较相邻两个数,如果第一个比第二个大,就交换他们两个。(一般指排序从小到大)这样的话每一轮结束后,最大的就跑到后面去了,就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
一. c语言版
【原版1】
/* 比较是相邻的两个元素比较,大的元素往后调。 经过冒泡后,会将最大给浮上水面(从小到大排序) -循环,比较相邻两个数值,如果 第一个比第二个大就交换位置 -对每一对相邻的元素做同样的工作,从开始到最后一对,经过一轮后,最大值会在数组最后 -持续重复上面的操作,直到没有数字需要对比 */ void BubbleSort1(int *arr,int len) { int i,j;//两个变量 来控制双循环来进行排序 for( i=0; i<len-1 ; i++){//外循环为排序趟数 一共len个数 走len-1趟 for( j=0 ; j<len-1-i; j++){//内循环代表每一轮的冒泡处理 第i趟比较len-i次 if(arr[j] > arr[j+1]){//每次处理后最大值都在后面 swap(&arr[j],&arr[j+1]); //这个是交换两个数的地址 } } } }
【优化1】
/* 进一步 优化一下冒泡排序 8 3 2 4 5 =>第一轮 3 2 4 5 8 所以8已经有序了 第二轮 2 3 4 5 8 所以2到5已经有序了 但是原版的还是会继续循环下去 用标记order,判断是否有序 第三轮 已经都有序,不需要再进行比较了,直接跳出循环 */ void BubbleSort1(int *arr,int len) { int i,j; for( i=0; i<len-1 ; i++){ bool order = true;//有序标记 for( j=0 ; j<len-1-i; j++){ if(arr[j] > arr[j+1]){ swap(&arr[j],&arr[j+1]);//有交换 就表示无序 order = false; } } if(order){ break;//整个数组已经有序,跳出循环 } } }
【优化2】
/* 再优化 确定的是 有序区的长度和排序的轮数是相等的。 比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 . 但是对于部分有序的数组,可能后面已经有序,大于排序的轮数。 因此后面的许多次元素比较是没有意义的。 我们可以在每一轮排序的最后,记录下最后一次元素交换的位置, 那个位置也就是无序数列的边界,再往后就是有序区了。 */ void BubbleSort2(int *arr,int len) { int i,j; int lastIndex = 0;//记录最后一次交换的位置 int sortBorder= len - 1;//无序数组的边界,后面就是有序的 for(i=0; i<len-1; i++){ bool order = true; for(j=0; j<sortBorder; j++){//所以只需要比较到这个边界 if(arr[j]>arr[j+1]){ swap(&arr[j],&arr[j+1]); order = false; lastIndex = j;//更新最后交换的位置 } } sortBorder = lastIndex;//重新确定无序的边界 if(order){ break; } } }
【完整代码】
#include<stdio.h> #include <stdbool.h> #include <time.h> #define MAX 10000 //数组长度 void BubbleSort(int *arr,int len);//冒泡排序 void BubbleSort1(int *arr,int len); //优化 判断是否已经有序 void BubbleSort2(int *arr,int len); //优化 判断有序与无序边界 void getRandArray(int *arr,int len,int Range); //随机生成一个一维数组 void getNearRandArray(int *arr,int len,int times);//生成一个部分有序的数组 void swap(int *a,int *b); //交换两个变量 void dump(int arr[],int len); //打印数组 /** 如果比较这三个冒泡排序, 可以利用 getNearRandArray构造出一个部分有序的数组 对比三组时间快慢 */ int main() { clock_t begin , end; double time; int arr[MAX],arr1[MAX],arr2[MAX]; getRandArray(arr,MAX,10000);//生成无序数组 getNearRandArray(arr,MAX,10);//生成部分有序的数组 memcpy(arr1,arr,sizeof(arr));//拷贝arr memcpy(arr2,arr,sizeof(arr));//拷贝arr // dump(arr,MAX); begin = clock();//开始记录 BubbleSort(arr,MAX); end = clock(); //结束记录 time = (double)(end - begin)/CLOCKS_PER_SEC; printf("【 array length is %d 】【冒泡排序】【cost time is %lf 】\n",MAX,time); // dump(arr,MAX); begin = clock();//开始记录 BubbleSort1(arr1,MAX); end = clock(); //结束记录 time = (double)(end - begin)/CLOCKS_PER_SEC; printf("【 array length is %d 】【冒泡排序2】【cost time is %lf 】\n",MAX,time); // dump(arr1,MAX); begin = clock();//开始记录 BubbleSort2(arr2,MAX); end = clock(); //结束记录 time = (double)(end - begin)/CLOCKS_PER_SEC; printf("【 array length is %d 】【冒泡排序3】【cost time is %lf 】\n",MAX,time); // dump(arr2,MAX); return 0; } //冒泡排序 /** 时间复杂度O(n^2),具有稳定性 经典算法,也是比较简单的一种排序算法 比较是相邻的两个元素比较,大的元素往后调。 经过冒泡后,会将最大给浮上水面(从小到大排序) -循环,比较相邻两个数值,如果 第一个比第二个大就交换位置 -对每一对相邻的元素做同样的工作,从开始到最后一对,经过一轮后,最大值会在数组最后 -持续重复上面的操作,直到没有数字需要对比 */ void BubbleSort(int *arr,int len) { int i,j;//两个变量 来控制双循环来进行排序 for( i=0; i<len-1 ; i++){//外循环为排序趟数 一共len个数 走len-1趟 for( j=0 ; j<len-1-i; j++){//内循环代表每一轮的冒泡处理 第i趟比较len-i次 if(arr[j] > arr[j+1]){//每次处理后最大值都在后面 swap(&arr[j],&arr[j+1]); //这个是交换两个数的地址 } } } } /** 进一步 优化一下冒泡排序 8 3 2 4 5 =>第一轮 3 2 4 5 8 所以8已经有序了 第二轮 2 3 4 5 8 所以2到5已经有序了 但是原版的还是会继续循环下去 用标记order,判断是否有序 第三轮 已经都有序,不需要再进行比较了,直接跳出循环 */ void BubbleSort1(int *arr,int len) { int i,j; for( i=0; i<len-1 ; i++){ bool order = true;//有序标记 for( j=0 ; j<len-1-i; j++){ if(arr[j] > arr[j+1]){ swap(&arr[j],&arr[j+1]);//有交换 就表示无序 order = false; } } if(order){ break;//整个数组已经有序,跳出循环 } } } /* 再优化 确定的是 有序区的长度和排序的轮数是相等的。 比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 . 但是对于部分有序的数组,可能后面已经有序,大于排序的轮数。 因此后面的许多次元素比较是没有意义的。 我们可以在每一轮排序的最后,记录下最后一次元素交换的位置, 那个位置也就是无序数列的边界,再往后就是有序区了。 */ void BubbleSort2(int *arr,int len) { int i,j; int lastIndex = 0;//记录最后一次交换的位置 int sortBorder= len - 1;//无序数组的边界,后面就是有序的 for(i=0; i<len-1; i++){ bool order = true; for(j=0; j<sortBorder; j++){//所以只需要比较到这个边界 if(arr[j]>arr[j+1]){ swap(&arr[j],&arr[j+1]); order = false; lastIndex = j;//更新最后交换的位置 } } sortBorder = lastIndex;//重新确定无序的边界 if(order){ break; } } } //随机生成一个数组 随机数范围在[0,Range) void getRandArray(int *arr,int len,int Range) { srand(time(NULL));//设置随机种子 int i = 0; for (i=0; i<len; i++){ arr[i] = rand() % Range;//范围在Range以内 } } //随机生成一个部分有序的数组 void getNearRandArray(int *arr,int len,int times) { int i = 0; for(i;i<len;i++){//先构造一个有序的数组 arr[i] = i; } srand(time(NULL));//设置种子 for(i=0;i<times;i++){//尽量有序 循环交换多少次 int posx = rand()%(len-1);//在[1,len-1]上随机位置 int posy = rand()%(len-1); swap(&arr[posx],&arr[posy]);//随机交换两个位置的数 } } //交换两个值 利用指针交换位置 void swap(int *a,int *b) { int temp; temp = *a; *a = *b; *b = temp; } //打印输出数组 void dump(int *arr,int len) { int i=0; for(i=0; i<len; i++) printf("%d ",arr[i]); printf("\n");//换行 }
二. PHP版
【PHP知识点】
1.随机数的产生 【rand()】产生随机整数 a.如果没有传入参数,默认从0到getrandmax()之间的伪随机整数 (getrandmax()在win下32767) b.如果传入参数rand(2,10),也就是从[2,10]返回之间进行获取随机整数 c.之前获取随机数,都需要提前设置随机种子srand(),不过PHP已经 由系统自动完成的。 【mt_rand()】生成更好的随机数 同rand(),不过效果更好 a.不写参数,默认从0到mt_getrandmax() b.PHP 的 rand() 函数默认使用 libc 随机数发生器。 c.产生随机数值的平均速度比 libc 提供的 rand() 快四倍。 2.统计时间 microtime() — 返回当前 Unix 时间戳和微秒数 a.默认参数False,返回字符串 "microsec sec" , 其中 sec 为自 Unix 纪元(0:00:00 January 1, 1970 GMT)起的秒数, microsec 为微秒部分。 b.参数设置为 TRUE,则返回一个浮点数, 表示自 Unix 纪元起精确到微秒的以秒为单位的当前时间。 3.交换两个变量 【异或】相同为0 a.任一变量X与其自身进行异或结果为0,即 X^X=0 b.任一变量X与0进行异或结果不变,即 X^0=X c.异或运算具有可结合性,即 a^b^c = (a^b)^c = a^(b^c) d.异或运算具有可交换性,即 a^b = b^a
【冒泡排序】
<?php //冒泡排序 class Bubble { const min = 0; const max = 10000; private $len = 100;//默认构造的数组长度 private $times= 10;//无序交换次数 public $arr = [];//要排序的数组 public function __construct($len=0,$times=0,$min=Bubble::min,$max=Bubble::max) { $this->len = empty($len) ? $this->len:$len;//判断要生成数组的长度 $this->times= empty($times) ? $this->times:$times; //无序交换次数 $this->arr = $this->getNearRandArray($this->len,$this->times,$min,$max);//生成无序数组 } /** * 冒泡排序 */ public function bubbleSort() { $out = $this->arr; for($i=0;$i<$this->len-1;$i++){ for($j=0;$j<$this->len-1-$i;$j++){ ($out[$j]>$out[$j+1]) && $this->swap($out[$j],$out[$j+1]);//大数冒泡 } } return $out; } //改进 添加order 判断是否数组已经有序 public function bubbleSort1() { $out = $this->arr; for($i=0;$i<$this->len-1;$i++){ $order = true; for($j=0;$j<$this->len-1-$i;$j++){ ($out[$j]>$out[$j+1]) && $this->swap($out[$j],$out[$j+1]); $order = false; } if($order){ break; } } return $out; } //改进 判断有序区域的界限 public function bubbleSort2() { $out = $this->arr; $lastIndex = 0;//最后一次交换的位置 $sortBorder= $this->len - 1;//无序数组边界 for($i=0;$i<$this->len-1;$i++){ $order = true; for($j=0;$j<$this->len-1-$i;$j++){ ($out[$j]>$out[$j+1]) && $this->swap($out[$j],$out[$j+1]); $order = false; $lastIndex = $j;//记录最后 交换的位置 } $sortBorder = $lastIndex;//重新确定无序的边界 防止无效比较 if($order){ break; } } return $out; } /** * 交换两个变量 * 第一步 a = a ^ b 完成 * 经过第一步运算后a 变量的结果为 a ^ b; * 第二步 b = a ^ b 等号右边即是 (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a, * 经过第二步运算后b中的值为a; * 第三步 a = a ^ b 此时赋值号右边的a保存的仍然是 a ^ b 的值,而赋值号右边的b已经是原始的a了, * 即等号右边的 a ^ b = (a ^ b) ^ a = a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b, 该值赋值给a,即 a = b, * 经过第三步运算后a中的值为b; */ private function swap(&$a,&$b) { $a = $a^$b; $b = $b^$a; $a = $a^$b; } /** * 生成部分有序的数组 * Params * $len 数组大小 * $min , $max 随机数范围 * $times 无序交换位置次数 * return * 返回数组 */ private function getNearRandArray($len,$times,$min,$max) { $arr = [];//生成部分有序的数组 for($i=0; $i<$len; $i++){ $arr[$i] = $i; } for($i=0; $i<$times; $i++){//打乱有序 循环交换多少次 $posx = mt_rand()%($len-1);//在[1,len-1]上随机位置 $posy = mt_rand()%($len-1); $this->swap($arr[$posx],$arr[$posy]);//随机交换两个位置的数 } return $arr; } } $obj = new Bubble(100,5); $stime=microtime(true); $obj->bubbleSort(); $etime=microtime(true); $total=$etime-$stime; echo "<br />[冒泡排序1]执行时间为:{$total} 秒<br/>"; $stime=microtime(true); $obj->bubbleSort1(); $etime=microtime(true); $total=$etime-$stime; echo "<br />[冒泡排序2]执行时间为:{$total} 秒<br/>"; $stime=microtime(true); $obj->bubbleSort1(); $etime=microtime(true); $total=$etime-$stime; echo "<br />[冒泡排序3]执行时间为:{$total} 秒<br/>";
自己数据结构与算法很差,于是决定从基础入手,一步一个脚印,体验算法之美,由于打算走PHP方向,所以用PHP来进行编写代码,希望能对我的成长有一定帮助。第一次在牛客上发博,希望大家可以互相帮助,有错误及时指出,共同进步,加油!!!🐂