快速排序
快速排序的思想就是首先选择一个基数,将大于基数的所有值放到右侧,将小于基数的所有值放到左侧。
视频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的赋取值: