排序算法-冒泡排序

冒泡排序(Bubble Sort),应该算是比较简单也是很经典的排序算法。也比较好理解,先说一下结论,它的时间复杂度O(n^2),具有稳定性,重复遍历元素序列,依次比较相邻两个数,如果第一个比第二个大,就交换他们两个。(一般指排序从小到大)这样的话每一轮结束后,最大的就跑到后面去了,就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

BubbleSort

一. 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来进行编写代码,希望能对我的成长有一定帮助。第一次在牛客上发博,希望大家可以互相帮助,有错误及时指出,共同进步,加油!!!🐂

全部评论

相关推荐

点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务