您的位置:首页 > 编程语言 > C语言/C++

快速排序

2016-03-13 11:23 330 查看
快速排序的描述

        快速排序是一种基于分治(+递归)技术的重要排序算法,广泛认为它是解决一般问题的最佳排序算法,属于比较排序的一种,而且不需要额外的存储空间。在处理中到大型数据集时,快速排序是一个比较好的选择。

        快速排序按照元素的值对它们进行划分。具体来说,它对给定的数组中的元素进行重新排列,以得到一个快速排序的分区。一个分区中,所有在s下标之前的元素都小于等于A[s],所有在s下标之后的元素都大于等于A[s]。

                                                                 


        选择中轴(A[s])的一种行之有效的方法是通过随机选择发来选取。通过随机数的统计特性,从而保证快速排序的整体性能,因此快速排序是随机算法的一个好例子。

        显然,建立一个分区以后,A[s]已经位于它在有序数组中的最终位置,接下来我们可以继续对A[s]前和A[s]后的子数组使用同样的方法分别进行排序。

         算法:

QuickSort(A[l...r])
//用QuickSort对子数组排序
//输入:数组A[0...n-1]中的子数组A[l...r],由左右下标l和r定义。
//输出:非降序排列的子数组A[l...r]
if l < r
s <- Partition(A[l...r]) //s为中轴的位置
QuickSort(A[l...s-1])
QuickSort(A[s+1...r])

        为了建立一个分区,也有许多不同的方法对元素重新排列。在这里,我们使用一种基于两次扫描子数组的高效方法:一次是从左到右,另一次是从右到左,每次都把子数组的元素和中轴进行比较。从左到右的扫描(下面用索引 i 表示)从第二个元素开始。因为我们希望小于中轴的元素都位于子数组的第一部分,扫描会忽略小于中轴的元素,直到遇到第一个大于等于中轴的元素才会停止。从右到左的扫描(下面用索引 j 表示)从最后一个元素开始。因为我们希望大于中轴的元素位于子数组的第二部分,扫描会忽略大于中轴的元素,直到遇到第一个小于等于中轴的元素才会停止。两次扫描全部停止以后,取决于扫面的指针是否相交,会发生3种不同的情况:

        如果扫描指针i 和 j 不相交,也就是说 i < j ,我们简单的交换A[i]和A[j],再分别对i加一,j减一,然后继续扫描。 

                                               


        如果扫描指针相交,也就是说 i > j,把中轴和A[j]交换以后,我们得到了该数组的一个分区。

                                                       


        最后,如果扫描指针停下来时指向同一个元素,也就是说i = j,被指向的元素值一定等于p(为什么?),因此,我们建立了该数组的一个分区,中轴的位置s=i=j。

                                                                


        我们可以把第三种情况和指针相交的情况(i > j)结合起来,只要i >= j,就交换中轴和A[j]的位置。

            算法:

Partition(A[l...r])
//对子数组进行分区
//输入:数组A[0...n-1]中的子数组A[l...r],由左右下标l和r定义。
//输出:A[l...r]的一个分区,中轴的位置作为函数的返回值
p <- A[l]
i <- l; j <- r+1;

repeat
repeat i <- i+1 until A[i] >= p
repeat j <- j-1 until A[j] <= p

if i < j
swap(A[i], A[j])
else
swap(A[l], A[j]) // 交换中轴和A[j]的位置
break

return j

快速排序的实现

// QuickSort.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "typedef.h"

/*
*函数名:Compare_uint
*参数:pKey1 指向第一个比较元素的地址
* pKey2 指向第二个比较元素的地址
*功能:比较两个元素的大小
*返回值:1 表示Key1 大于 Key2
* -1 表示Key1 小于 Key2
* 0 表示Key1 等于 Key2
*作者:AlbertoNo1
*日期:2016-03-15
*/
INT32 Compare_uint(VOID *pKey1, VOID *pKey2)
{
/*对两个无符号整型数进行比较*/
if (*((UINT32*)pKey1) > *((UINT32*)pKey2))
{
return 1;
}
else if (*((UINT32*)pKey1) < *((UINT32*)pKey2))
{
return -1;
}
else
{
return 0;
}
}

/*
*函数名:Swap
*参数:pKey1 指向第一个交换元素的地址
* pKey2 指向第二个交换元素的地址
* uiKeySize 交换元素所占内存大小(Byte)
*功能:交换两个元素的位置
*返回值:无
*作者:AlbertoNo1
*日期:2016-03-15
*/
VOID Swap(VOID *pKey1, VOID *pKey2, UINT32 uiKeySize)
{
VOID *pTemp = NULL;

if (NULL == (pTemp = malloc(uiKeySize)))
{
return;
}

memcpy(pTemp, pKey1, uiKeySize);
memcpy(pKey1, pKey2, uiKeySize);
memcpy(pKey2, pTemp, uiKeySize);
}

/*
*函数名:Partition
*参数:pData 待分区数组数据的首地址
* uiElmSize 数据元素所占内存大小(Byte)
* iLeft 数组的开始下标
* iRight 数组的结束下标
* compare 两个元素的比较函数
*功能:对子数组进行分区划分,并返回中轴位置
*返回值:中轴元素所在位置
*作者:AlbertoNo1
*日期:2016-03-15
*/
INT32 Partition(VOID *pData,UINT32 uiElmSize, INT32 iLeft, INT32 iRight, INT32 (*comp
4000
are)(void *key1, void *key2))
{
INT32 i,j;
CHAR *pucData = NULL;

INT32 uiRand = 0;

pucData = (CHAR*)pData;

/*选择中轴位置*/
uiRand = (rand()%(iRight-iLeft+1)) + iLeft;

/*中轴值和第一个元素交换*/
Swap(&pucData[iLeft*uiElmSize], &pucData[uiRand*uiElmSize], uiElmSize);

i = iLeft;
j = iRight+1;

while(1)
{
/*向右侧移动,直到找到一个大于等于中轴值的元素*/
do
{
i = i+1;
}while((i<=iRight)&&(compare(&pucData[i*uiElmSize], &pucData[iLeft*uiElmSize])<0));

/*向左侧移动,直到找到一个小于等于中轴值的元素*/
do
{
j = j-1;
}while((compare(&pucData[j*uiElmSize], &pucData[iLeft*uiElmSize])>0));

if (i < j)
{
/*交换元素位置*/
Swap(&pucData[i*uiElmSize], &pucData[j*uiElmSize], uiElmSize);
}
else
{/* i >= j */
/*中轴元素和A[j]交换,一次分区结束*/
Swap(&pucData[iLeft*uiElmSize], &pucData[j*uiElmSize], uiElmSize);
break;
}
}

/*返回两个分区的分割位置*/
return j;
}

/*
*函数名:QuickSort
*参数:pData 待分区数组数据的首地址
* uiSize 数据的元素个数
* uiElmSize 数据元素所占内存大小(Byte)
* iLeft 数组的开始下标
* iRight 数组的结束下标
* compare 两个元素的比较函数
*功能:对子数组进行快速排序
*返回值:无
*作者:AlbertoNo1
*日期:2016-03-15
*/
VOID QuickSort(VOID *pData, UINT32 uiSize, UINT32 uiElmSize, INT32 iLeft, INT32 iRight, INT32 (*compare)(void *key1, void *key2))
{
INT32 iPivotPos = 0;

if (iLeft < iRight)
{
/*分区划分,j为最后划分分区的位置*/
iPivotPos = Partition(pData, uiElmSize, iLeft, iRight, compare);

/*对左侧分区继续快速排序*/
QuickSort(pData,uiSize, uiElmSize, iLeft, iPivotPos-1, compare);
/*对右侧分区继续快速排序*/
QuickSort(pData,uiSize, uiElmSize, iPivotPos+1, iRight, compare);
}

return;
}

int _tmain(int argc, _TCHAR* argv[])
{
INT32 iRet = 0;
UINT32 uiLoop = 0;

UINT32 auiData[] = {10,15,15,18,20,20,20,36,48,51,51,77,77};
//UINT32 auiData[] = {77,77,51,51,48,36,20,20,20,18,15,15,10};
//UINT32 auiData[] = {77,15,20,18,51,51,36,10,77,15,20,20,48};
//UINT32 auiData[] = {77,77};
//UINT32 auiData[] = {77};

QuickSort(auiData, sizeof(auiData)/sizeof(auiData[0]), sizeof(auiData[0]), 0, ((sizeof(auiData)/sizeof(auiData[0]))-1),Compare_uint);

printf("Quick Sort Success.\n");
printf("Result:\n");

for (uiLoop = 0; uiLoop < sizeof(auiData)/sizeof(auiData[0]); uiLoop++)
{
printf("%d ", auiData[uiLoop]);
}

getchar();

return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息