快速排序

        快排和归并排序是比较经常见到的两种排序算法,下面我将用我自己的话来讲解快速排序和归并的知识点。

    (一)快速排序

   首先要明白,快速排序是基于冒泡排序的基础上做得改进,冒泡排序的交换次数太多了,快速排序就是为了来减少交换次数的,那接下来就好好的分析一下整个过程。

      快速排序定义:快排是利用分治的思想,如果要排序的数组中下标是从p到r之间的一组数据,我们选择p到r之间的任意一个数据作为分区点(pivot(分区点)),我们遍历p到r之间的数据,将小于pivot放到左边,将大于pivot放到右边,将pivot放到中间,这样经过这一步骤之后,数组p到r之间的数据分成了三部分。前面p到q-1都是小于pivot的,中间pivot, 后面的q+1到r之间都是大于pivot的。

     

  根据分治的思想,我们可以想到用递归来实现快速排序;

     递归的跳出条件就是:当p和r相等的时候,待排序的数组里面只剩下一个元素了。

     递推公式就是:f(x)=f(p...q-1)+f(q+1,r)  。

   大概的递归伪代码如下

quickSort(arr,p,r)
{
if(p<r)
    { q
=partion(arr,p,r) quickSort(arr,p,q-1) quickSort(arr,q+1,r)
    }   }

  接下来最关键的就是这分区函数的实现;

    分区函数的实现,有很多种方式。下面这个是我认为最好理解的。

 我们先选取最右边的作为基准参数,这个i和j的作用大概如下:把数组arr分割成[p到i-1]是小于pivot,类似于选择排序。我们假定arr[i]代码的就是小于基准的数据。

       static int Partion(ref List<int> intList,int l,int r)
        {
            var arr = intList.ToArray();
            var pivot = arr[r];
            int j = l;
            int i = 0;
            //
            for(j = l; j <=r; j++)
            {
                if (arr[j] <= pivot)
                {
                    var temp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = temp;
                    if(arr[j]!=pivot)
                    i++;
                }
            }
            intList = arr.ToList();
            return i;
        }

 (二)时间复杂度和空间复杂度分析

  快速排序是原地,不稳定排序算法。原地排序算法,上面已经做到了。

       空间复杂度为o(1)。不稳定的排序算法例如:6,8,7,6,4,9,5  分区完了之后,两个6的前后顺序会改变。

  那么时间复杂度怎么分析:

       这里主要是用到了递归,和第一次分区的时间复杂度为o(n);

  最好的情况下,我们刚刚好平均分成了两份,如果我们对n个元素进行快速排序所需要的时间T(n),那么时间复杂度的公式如下:

      T(1)=1;

      T(n)=2(T/n)+n;

   通过这个公式,如何来求解T(n)呢,还不够直观,那我们再进一步分解计算过程。

     T(n)=2*T(n/2)+n

      =2*(2*T(n/4)+n/2)+N=4*T(n/4)+2*n

           =4*(2*T(n/8)+n/4)+2*n

      =以此类推... 得到时间复杂度为o(nlogn)。

  最坏情况下,就是o(n²)。

   思考题:为什么快速排序需要定义两个变量(i和j)

    哪里都有讲快速排序的,啥的,我也看了很多篇别人写的文章。但是好多文章里面都是直接就说拿两个i,j变量,就开始比较了。

    快速排序,简单的说就是,先确定一个基准,然后就把比这个基准大的放到一边,比这个基准小的放到一边。放到一边的时候,是不管它是有序还是无序的。接下来在把之前的操作重复一下,其实就是一个递归的过程。

    为什么要定义两个变量进行快速排序呢?

    其实是这样的,快速排序,为啥叫快速排序呢,快在哪里,快在它减少了交换次数,还有一个原因就是快速排序是在快速排序是不用新开内存的,可以减少内存的使用。它就是在原地完成排序的。可能这样讲,你还是不明白,那我们来进行假设,如果只定义一个变量i的话呢,然后进行比较大小,那不就成了冒泡排序了吗。刚刚还说了快速排序是在原地完成排序的,不需要另外开辟空间资源的。你单独定义一个变量显然是不能实现的。

   为啥要定义如果基准数量在这边,那么就要先从另外一边开始扫描呢。

   先想想如果是放在同一边的话,那不就成了个冒泡排序吗。

   

posted @ 2017-04-21 22:12  GDOUJKZZ  阅读(633)  评论(0编辑  收藏  举报