好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

C#递归算法之快速排序

上两片第归算法学习:

1) 递归算法之分而治之策略
2) 递归算法之归并排序

  上一篇学习中介绍了了递归算法在排序中的一个应用:归并排序,在排序算法中还有一种算法用到了递归,那就是快速排序,快速排序也是一种利用了分而治之策略的算法,它由c.a.r发明,它依据中心元素的值,利用一系列递归调用将数据表划分成越来越小的子表。在每一步调用中,经过多次的交换,最终为中心元素找到最终的位置。与归并算法不同,快速排序是就地排序,而归并排序需要把元素在临时向量中拷贝,下面通过对以下向量进行排序来理解和加深快速排序算法的步骤:

v={800,150,300,650,550,500,400,350,450,400,900};

  利用快速排序算法对此数据表进行排序的第0级划分过程如下: 向量v的索引范围为:[first,last) = [0,10),则中心点的索引为mid = (0+10)/2=5,中心点的值为v[5] = 500

  快速排序算法的第一次划分的目的就是将向量v依据v[5]的值划分成两个子表sublist1和sublist2,其中sublist1中的值都小于v[5],而sublist2中的值都大于v[5],我们将sublist1称为左子表,sublist2称为右子表,并且确定v[5]的最终位置

下面就是实现这一目的需要我们作出的工作步骤:

1)首先将中心元素与起始位置的元素进行交换。

2)分别扫描左子表和右子表,左子表扫描起始位置为 first+1, 右子表从last-1开始。左子表从左向右扫描扫描,右子表从右向左扫描。直到左子表扫描位置大于或者等于右子表扫描位置时候结束。

在第一个步骤中,得到如下的数据表

500  150  300 650 550 800 400 350 450 400 

  而此时的左子表扫描位置处于索引1处,右子表扫描位置处于索引9处,先从左子表扫描,直到找到数据值大于中间值500的位置停止扫描,然后扫描右子表,直到找到数据值小于中间值500并且右子表的扫描位置(scandown)要小于左子表开始位置,防止数据溢出。找到之后,交换左子表与右子表中中扫描位置的元素,图示如下:

在交换v[3](650>500)与v[8](450<500)后,继续扫描左子表和右子表,如图

  直到满足条件scanup>=scandown,然后scandown所在位置就是中心元素500的最终位置,交换v[0]与v[scandown)=v[5],第一次划分级别的最终结果数据集为:400,150,300,450,350,500,800,550,650,900,此时得到的左子表为:400,150,300,450,350,右子表为:800,550,650,900

  下一个划分级别是处理上一级别产生的子表,按照相同的处理方法分别处理左子表和右子表,左子表索引位置[0,5),右子表索引位置[6,10),按照上面的处理步骤处理左子表(400,150,300,450,350)得到的最终结果为:150,300,400,450,350 右子表最终处理结果为:550,650,800,900 在处理结果中300与650分别是中心值,他们现在的位置就是最终位置

  在接下来的处理中,总是处理上一步骤中留下的子表,当子表数目<=1的时候就不用处理子表了,而子表有两个元素的时候,比较大小,然后交换两元素位置即可。

大于2个元素的子表都和上面的处理步骤一样,我们将上面的处理过程编写出一个函数

private int pivotindex(int[] v, int first, int last),那么快速排序算法就是对此函数的递归调用

?

/// <summary>

/// 交换位置

/// </summary>

/// <param name="v"></param>

/// <param name="index1"></param>

/// <param name="index2"></param>

private void swrap( int [] v, int index1, int index2)

{

  int temp = v[index1];

  v[index1] = v[index2];

  v[index2] = temp;

}

/// <summary>

/// 将向量v中索引{first,last)划分成两个左子表和右子表

/// </summary>

/// <param name="v">向量v</param>

/// <param name="first">开始位置</param>

/// <param name="last">结束位置</param>

private int pivotindex( int [] v, int first, int last)

{

  if (last == first)

  {

   return last;

  }

  if (last - first == 1)

  {

   return first;

  }

  int mid = (first + last) / 2;

  int midval = v[mid];

  //交换v[first]和v[mid]

  swrap(v, first, mid);

  int scana = first + 1;

  int scanb = last - 1;

  for (; ; )

  {

 

   while (scana <= scanb && v[scana] < midval)

   {

    scana++;

   }

   while (scanb > first && midval <= v[scanb])

   {

    scanb--;

   }

   if (scana >= scanb)

   {

    break ;

   }

   swrap(v, scana, scanb);

   scana++;

   scanb--;

  }

  swrap(v, first, scanb);

  return scanb;

 

}

public void sort( int [] v, int first, int last)

{

  if (last - first <= 1)

  {

   return ;

  }

  if (last - first == 2)

  {

   //有两个元素的子表

   if (v[first] > v[last - 1])

   {

    swrap(v, first, last - 1);

   }

   return ;

  }

  else

  {

   int pivotindex = pivotindex(v, first, last);

   sort(v, first, pivotindex);

   sort(v, pivotindex + 1, last);

  }

}

  快速排序因为每次划分都能将中心值元素找到最终的位置,并且左边值都小于中心值,右边都大于中心值,它的时间复杂度平均和归并算法一致为o(nlog2n);

  任何一种基于比较的排序算法的时间复杂度不可能小于这个数,除非不使用比较的方法进行排序。

dy("nrwz");

查看更多关于C#递归算法之快速排序的详细内容...

  阅读:49次