快速排序

快速排序

快速排序的思想就是首先选择一个基数,将大于基数的所有值放到右侧,将小于基数的所有值放到左侧。

视频http://www.tudou.com/listplay/Z-bWgybEtig/pqOqOznbYkY.html中讲解了快速排序的递归实现,看起来非常的简单。

c代码如下:

void QuickSort_Recursion (int * const pia, const int L, const int R)
{
   int i = L;
   int j = R;
   const int pivot = pia[(i+j)/2]; // pivot
   int temp;
   while (i <= j)
   {
       while (pia[i] < pivot)
          i ++;
       while (pia[j] > pivot)
          j --;
       if (i <= j)
       {
           temp = pia[i];
           pia[i] = pia[j];
           pia[j] = temp;
           i ++;
           j --;
       }
   }
   if (L < j)
      QuickSort_Recursion(pia, L, j);
   if (R > i)
      QuickSort_Recursion(pia, i, R);
}

一开始我认为非常的简单,于是默写了一下,运行后程序即挂掉。

需要注意的点:

1. while中,为什么要i<=j而不是i<j。

首先1点,就是如果没有i==j的条件, 那么退出while循环后i有可能等于j。就有可能出现以下3种情况。pivot是5。


那么,下面的语句该如何去写?

   if (L < j)
      QuickSort_Recursion(pia, L, j);
   if (R > i)
      QuickSort_Recursion(pia, i, R);

按照case 1,左侧应该除去j元素,成j-1,右侧i元素不动。即:

   if (L < j)
      QuickSort_Recursion(pia, L, j-1);
   if (R > i)
      QuickSort_Recursion(pia, i, R);
按照case 3,左侧j不动,右侧应该处于i元素,成i+1,即:

   if (L < j)
      QuickSort_Recursion(pia, L, j);
   if (R > i)
      QuickSort_Recursion(pia, i+1, R);

case 2,怎么都可以。

因此,程序将在此处不容易区分开来。

那么为什么将i==j条件加上去就可以?

退出while循环的时的i肯定是大于j的。情况如下:


在case 2中,丢掉j和i中间的5也没有关系,因为它已处在正确的位置。

因此,只要记住,while循环结束后,必须是i大于j,以此来记住循环条件。

2. 为什么是while (pia[i] < pivot)和while (pia[j] > pivot),而不是while (pia[i] <= pivot)或者while (pia[j] >= pivot)?

首先1点,对于要排序的数值全是一样的,比如全部都是5,那么pivot也是5,不管是while (pia[i] <= pivot)和while (pia[j] > pivot),还是while (pia[i] < pivot)和while (pia[j] >= pivot),还是while (pia[i] <= pivot)和while (pia[j] >= pivot),都将造成i++或者j--的溢出。(还没有想到其他的例子)


3. 为什么交换之后需要i++和j--?程序如下:

if (i <= j)
{
    temp = pia[i];
    pia[i] = pia[j];
    pia[j] = temp;
    i ++;
    j --;
}

第1点,此处if的条件是i <= j,那么,当i==j时,如果去掉i++,j--条件,while循环将不会退出。

第2点,交换过后的i和j都满足自身的i++或者j--条件,没有必要在下一次循环中再比较后i++或者j--。

第3点:按第1点所说,当i==j时,将它们i++或者j--是为了退出while循环,那改成如下还行?

if (i < j)
{
    temp = pia[i];
    pia[i] = pia[j];
    pia[j] = temp;
}
else
{
    i ++;
    j --;
}

试了下{1,3,5,7,9,2,4,6,8,0},没有问题,可以通过。当但遇到如下情况时,将进入死循环。因为i和j交换过后,下次比较过程中仍然不会i++和j--。可以理解为在有多个相同pivot值得情况下会出现死循环。



快速排序非递归实现

归并排序的非递归实现是从下到上两两组(1...2...4...8...16...)依次合并形成的。对于快速排序,由于所选的基数不可能正好处在组的中间,因此从下到上的方法不适合。

快速排序的递归实现中,是依次在左右分组中找出基数再次左右分组,逐个递归调用。基于此,我们逐次对左子组进行拆分,将右子组索引压入栈中。等左子组拆分排序完毕,再逐次从栈中弹出,如此循环直到栈中无索引,结束排序。

因此,首先要先造一个保存索引的结构体,一个入栈和出栈函数。

typedef struct _TQuickSortStackIndex
{
    int left;
    int right;
}TQuickSortStackIndex;

typedef struct _TQuickSortStack
{
    int maxCounter;
    int thisCounter;
    TQuickSortStackIndex * ptIndex;
}TQuickSortStack;
typedef char TBool;

TBool PushIndexToStackIndex (TQuickSortStack * const ptStack, const TQuickSortStackIndex * const ptIndex)
{
    if (NULL!=ptStack && NULL!=ptStack->ptIndex && NULL!=ptIndex)
    {
        if (ptStack->thisCounter < ptStack->maxCounter)
        {
            (void) memcpy (ptStack->ptIndex+ptStack->thisCounter, ptIndex, sizeof(TQuickSortStackIndex));
            ptStack->thisCounter ++;
            return TRUE;
        }
    }

    return FALSE;
}
TBool PopupIndexFromStackIndex (TQuickSortStackIndex * const ptIndex, TQuickSortStack * const ptStack)
{
    if (NULL!=ptStack && NULL!=ptStack->ptIndex && NULL!=ptIndex)
    {
        if (0 < ptStack->thisCounter)
        {
            (void) memcpy (ptIndex, ptStack->ptIndex+ptStack->thisCounter-1, sizeof(TQuickSortStackIndex));
            ptStack->thisCounter --;
            return TRUE;
        }
    }

    return FALSE;
}
这两个函数比较简单,就是将索引保存到栈的数组中,和从栈的数组中取数。

非递归的快速排序函数如下:

TBool QuickSort_Iteration (int a[], const int start, const int end, TQuickSortStack * const ptStack)
{
    int i = start;
    int j = end;
    int left, right;
    int pivot;
    int temp;
    TQuickSortStackIndex tIndex;
    if (NULL!=ptStack && NULL!=ptStack->ptIndex)
    {
        while (1)
        {
            left = i;
            right = j;
            pivot = a[i+((j-i)>>1)];
            while (i <= j)
            {
                while (a[i] < pivot)
                    i ++;
                while (a[j] > pivot)
                    j --;
                if (i <= j)
                {
                    temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                    i ++;
                    j --;
                }
            }
            if (i < right)
            {
                tIndex.left = i;
                tIndex.right = right;
                if (!PushIndexToStackIndex (ptStack, &tIndex))
                    return FALSE;
            }
            if (left < j)
            {
                i = left;
            }
            else
            {
                if (!PopupIndexFromStackIndex (&tIndex, ptStack))
                    return TRUE;
                i = tIndex.left;
                j = tIndex.right;
            }
        }
        return TRUE;
    }
    return FALSE;
}
该函数的上半部分和递归函数的部分一致,只是在下半部分将索引压入栈中。

demo如下

int testArray [] = {1, 3, 5, 7, 9, 0, 2, 4, 6, 8, 12, 5, 2, 25}; //{6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};//
int main (void)
{
    int i;
    const int len = sizeof(testArray)/sizeof(testArray[0]);
    TQuickSortStack tStack = {0};
    tStack.ptIndex = (TQuickSortStackIndex *)malloc (sizeof(TQuickSortStackIndex)*len);
    tStack.maxCounter = len;
    tStack.thisCounter = 0;

    for (i=0; i<len; i++)
    {
        printf("%d ", testArray[i]);
    }
    QuickSort_Iteration (testArray, 0, len-1, &tStack);
    printf("\n");
    for (i=0; i<len; i++)
    {
        printf("%d ", testArray[i]);
    }

    return 1;
}

关于tStack中maxCounter的赋取值:



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值