数据结构和算法分析

算法基础 排序(一)

2016-09-22  本文已影响83人  比沉默寡言话多

桶排序
冒泡排序
快速排序

1.桶排序

所谓的桶排序就是列出所有的可能进行排序

        //9,4,2,7,5,2,9,10,1   用来排序的数组  并且确定这些数字的可能为0到10的整数
        int arr[] = {9,4,2,7,5,2,9,10,1};
        //声明11个桶,对应所有的可能
        int bucketArr[11] = {0};
        //用来打印的数组
        NSMutableString *reslutStr = [NSMutableString string];
        //确定用来排序的数组的个数
        int count = sizeof(arr)/4;
        //遍历要排序的数组,比如遍历到一个数是5,那么就在5(bucketArr[5])对应的那个桶加1
        int i;
        for (i = 0; i < count; i++) {
            bucketArr[arr[i]]++;
        }
        //遍历所有的桶,如果第6个桶(bucketArr[5])对应的数字是2,那就输出两个5
        int j;
        for (i = 0; i < 11; i++) {
            for (j = 0; j < bucketArr[i]; j++) {
                //结果加到可变字符串中
                [reslutStr appendFormat:@"%d ",i];
            }
        }
        //打印结果
        NSLog(@"%@",reslutStr);
桶排序结果.png
(可以忽略第一句话,只是说明程序可以换种输入方式)
上面的程序中,你可以放一个输入函数,直接确定输入n个数,然后每次输入直接加到对应的桶里面.
但是我们依然要执行n次的将数放入桶中,
再执行m次的遍历桶和n次的把数字加到可变数组中.m为桶的个数.
所以时间复杂度就是m+2n.我们在计算时间复杂度时会忽略较小的常数.所以最终的时间复杂度就是O(M+N)
小结:这里的桶排序只是简化版的.桶排序是所有排序中最快的,当然是大多数情况下.当然也有一些缺点,比如他很费内存.因为他要列出所有的可能.比如可能性必须是有限的,出现小数他就没辙了.

2.冒泡排序

冒泡排序就是比较相邻两个数进行排序

        //9,4,2,7,5,2,9,10,1,53,32,24  从大到小排序
        int arr[] = {9,4,2,7,5,2,9,10,1,53,32,24};
        //得到数组元素个数
        int count = sizeof(arr)/4;
        int i,j,sub;
        for (j = 0; j < count - 1; j++) {//比如{3,5,2,6} 如果你要把6移到第一个位置,你需要重复排序3遍 所以这里重复count-1次
            for (i = 0; i < count - 1; i++) {//重头到尾排一遍
                if (arr[i] < arr[i+1]) {
                    //交换位置
                    sub = arr[i];
                    arr[i] = arr[i+1];
                    arr[i+1] = sub;
                }
            }
        }
        //数组遍历打印
        for (i = 0; i < count; i++) {
            printf("%d ",arr[i]);
        }
        
冒泡排序结果.png

这里如果我们有n个数需要进行排序,那么我们一次排序需要进行n-1遍,然后需要重复排序n-1次才能保证完全排序.一共需要(n-1)²
所以这里我们的时间复杂度就是O(N²)

小结:冒泡排序除了逻辑简单以外,就没什么优点了.他的时间复杂度让人很失望.

3.快速排序

快速排序用的是分治合并的思想(不知道有没有这种思想,我觉得这么说挺通的,😈😈😈)


int arr[] = {9,4,2,7,5,2,9,10,1,53,32,24};
        int count = sizeof(arr)/4;
        //找一个基准数 为了方便就把第一个数当做基准数 然后将比基数小的放基数左边,比基数大的放基数右边
        int i = 1;
        int j = count - 1;
        int base = arr[0];
        int sub ;
        
        while(i!=j)
        {
            //让j先进行探测,能确保最后一次交换是将基数与比基数小的值交换
            while(arr[j]>=base && i<j)
                j--;
            //再从左往右找
            while(arr[i]<=base && i<j)
                i++;
            //交换两个数在数组中的位置
            if(i<j)//当i!=j
            {
                sub=arr[i];
                arr[i]=arr[j];
                arr[j]=sub;
            }
        }
        //相等交换基数
        
        arr[0]=arr[i];
        arr[i]=base;

这里是快速排序的一次排序,一次排序过后我们等到两个数组,基数左边和右边,然后我们需要对其进行再排序,于是就想到用递归.毕竟你不好用循环做.
当只有一个数的时候跳出递归
整理后得到

//9,4,2,7,5,2,9,10,1,53,32,24  从小到大排序
int arr[] = {9,4,2,7,5,2,9,10,1,53,32,24};

void quickSort(int left,int right){
    if (left >= right) {
        return;
    }
    int base = arr[left];
    int i = left;
    int j = right;
    int sub;
    while(i!=j)
    {
        //让j先进行探测,能确保最后一次交换是将基数与比基数小的值交换
        while(arr[j]>=base && i<j)
            j--;
        //再从左往右找
        while(arr[i]<=base && i<j)
            i++;
        //交换两个数在数组中的位置
        if(i<j)//当i!=j
        {
            sub=arr[i];
            arr[i]=arr[j];
            arr[j]=sub;
        }
    }
    //相等交换基数
    
    arr[left]=arr[i];
    arr[i]=base;
    quickSort(left, i-1);
    quickSort(i+1, right);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        
        int count = sizeof(arr)/4;
        quickSort(0, count-1);
        for (int i = 0; i < count; i++) {
            printf("%d ",arr[i]);
        }
        
    }
  
快速排序结果.png

接下来就谈谈他的时间复杂度
http://www.cnblogs.com/pugang/archive/2012/07/02/2573075.html
这篇博客里对其进行了一定的说明,他是用一个公式来说明的.

然后我想说的是,我换种方式,自己计算了一下他的时间复杂度,要是错了(反正我觉得不会有多少人来看),你说我就改.
首先第一次排序,最好的是分成平均分成两部分 那么第一次排序进行了n/2次 确定了1个数字的位子.
第二次排序 也是进行了n/2次 这里的是算上两部分一共的次数,确立的两个数字
第三次...
然后得到

第1次 运行n/2 确立2^0个数字
第2次 运行n/2 确立2^1个数字
第3次 运行n/2 确立2^2个数字
...
第x次 运行n/2 确立2^(x-1)个数字
此时满足 2^0 + 2^1 +2^2 + 2^ 3 +...+2^(x-1) = n;
解得 x = log(n+1) 这里2为默认底.
然后时间复杂度就是 x * n/2 = n/2 log(n+1)
计算时间复杂度时忽略较小的数字 所以就得到O(nlogn).

而最坏的结果是每一次选中的基数都是最大或者最小的数,然后相当于每次运行n-1 ,n-2,n-3,n-4,n-5..所以我们可以认为程序一共执行了(n^2 - n)/2 次 所以时间复杂度就是O(N^2).

小结:快速排序是一个好算法,比起桶排序省空间,比起冒泡则快了太多.所以这是一个用的非常多的算法.


总结:我也是刚开始学,有什么不对的,你说我就改!

上一篇下一篇

猜你喜欢

热点阅读