1. 基本思想
任取待排序元素序列中的某个元素作为基准值,按照该排序码将待排序序列分割为两个子序列,其左子序列中所有元素均小于基准值,其右子序列中所有元素均大于基准值,然后将其左右子序列按照上述过程重复操作,直到所有元素均排列在相应位置上为止。
操作的基本过程:若规定基准值key始终为待排序序列的最后一个元素,定义两个索引left和right,left将在待排序序列中从前往后找到第一个大于key的元素,right将在待排序序列中从后往前找到第一个小于key的元素,均找到之后进行交换,如此重复操作直到left与right重合时与基准值key交换即可。
2. 举例说明
待排序数组为array[7]={9,5,2,7,8,4,6},size=7
(1)待排序区间为[0,size),待排序数组array[7]={9,5,2,7,8,4,6},基准值key=6,left指向待排序区间的开始,即下标为0的元素,right指向待排序区间的末尾,即下标为size-1=6的元素。
开始移动left和right(移动的条件为left<right):
①array[left]=array[0]=9>key=6,故找到了一个大于基准值的元素;array[right]=array[6]=6!<key=6,故right--继续寻找,array[right]=array[5]=4<key=6,故找到一个小于基准值的元素。将这两个元素进行交换后数组变为array[7]={4,5,2,7,8,9,6}
②array[left++]=array[1]=5<key=6,故left++继续寻找,array[left]=array[2]=2<key=6,故left++继续寻找,array[left]=array[3]=7>key=6,故找到了一个大于基准值的元素;array[right--]=array[4]=8>key=6,故right--继续寻找,由于left=3!<right=3,left与right重合,故将left指向的元素与基准值交换,数组变为array[7]={4,5,2,6,8,9,7}
此时可以看到基准值key=6的左边均为小于它的元素,右边均为大于它的元素!
待排序区间需要进行划分再排序,划分原则为上一次的基准值的下标的左边为一个待排序区间、右边为一个待排序区间,基准值key=6下标为3。故左边待排序区间为[0,3),故待排序数组为array={4,5,2},右边待排序区间为[4,size),故待排序数组array={8,9,7}
(2)待排序区间为[0,3),待排序数组array={4,5,2},基准值key=2,left指向待排序区间的开始,即下标为0的元素,right指向待排序区间的末尾,即下标为3-1=2的元素。
h开始移动left和right(移动的条件为left<right):
①array[left]=array[0]=4>key=2,故找到了一个大于基准值的元素;array[right]=array[2]!<key=2,故right--继续寻找,array[right]=array[1]=5!<key=2,故right--继续寻找,由于left=0!<right=0,left与right重合,故将left指向的元素与基准值交换,数组变为array={2,5,4}
此时可以看到基准值key=2的左边均为小于它的元素,右边均为大于它的元素!
待排序区间需要再次进行划分再排序,划分原则为上一次的基准值的下标的左边为一个待排序区间、右边为一个待排序区间,基准值key=2下标为0。故左边待排序区间为[0,0),右边待排序区间为[1,3)
(3)待排序区间为[0,0)不需要排序,待排序数组array为空
(4)待排序区间为[1,3),待排序数组array={2,5,4},基准值key=4,left指向待排序区间的开始,即下标为1的元素,right指向待排序区间的末尾,即下标为3-1=2的元素。
开始移动left和right(移动的条件为left<right):
①array[left]=array[1]=5>key=4故找到了大于基准值的元素;array[right]=array[2]=4!<key=4,故right--继续寻找,由于left=1!<right=1,left与right重合,故将left指向的元素与基准值交换,数组变为array={2,4,5}
此时可以看到基准值key=4的左边均为小于它的元素,右边均为大于它的元素!
(5)待排序区间为[4,size),待排序数组array={8,9,7},基准值key=7,left指向待排序区间的开始,即下标为4的元素,right指向待排序区间的末尾,即下标为size-1=6的元素。
①array[left]=array[4]=8>key=7,故找到了一个大于基准值的元素;array[right]=array[6]=7!<key7,故right--继续寻找,array[right]=array[5]=9!<key=7,故right--继续寻找,由于left=4!<right=4,left与right重合,故将left指向的元素与基准值交换,数组变为array={7,9,8}
此时可以看到基准值key=7的左边均为小于它的元素,右边均为大于它的元素!
待排序区间需要再次进行划分再排序,划分原则为上一次的基准值的下标的左边为一个待排序区间、右边为一个待排序区间,基准值key=7下标为4。故左边待排序区间为[4,4),故待排序数组为空,右边待排序区间为[5,size),故待排序数组array={9,8}
(6)待排序区间为[4,4)不需要排序,待排序数组array为空
(7)待排序区间为[5,size),基准值key=array[size-1]=array[6]=8,left指向待排序区间的开始,即下标为5的元素,right指向待排序区间的末尾,即下标为7-1=6的元素。待排序数组array={9,8}
开始移动left和right(移动的条件为left<right):
①array[left]=array[5]=9>key=8,故找到了大于基准值的元素;array[right]=array[6]=8!<key=8,故right--继续寻找,由于left=5!<right=5,left与right重合,故将left与基准值交换,数组变为array={8,9}
此时可以看到基准值key=8的左边均为小于它的元素,右边均为大于它的元素!
至此,整个排序结束,数组变为array[7]={2,4,5,7,8,9}
通过以上文字分析,即整个数组的排序将被依次递归分为两部分进行排序,直到分为的数组中元素个数为1表示不再分组进行排序。画图总结上述步骤:
3. 递归版本实现快速排序
void QuickSort(int array[],int64_t size)
{
//当size<=1是直接return
if(size<=1)
return;
//定义一个函数_QuickSort用于递归处理数组
_QuickSort(array,0,size);
}
void _QuickSort(int array[],int64_t beg,int64_t end)
{
//当数组元素只有一个时,递归结束
if(end-beg<=1)
return;
//定义Partion函数作用是对[beg,end)区间进行排序
//排序成一个以某基准值为中心,左侧小于基准值右侧大于基准值
//返回值表示的含义是排序后基准值所在的下标
int64_t mid=Partion(array,beg,end);
//接着继续对区间[beg,mid)进行快速排序
_QuickSort(array,beg,mid);
//继续对区间[mid+1,end)进行快速排序
_QuickSort(array,mid+1,end);
}
int64_t Partion(int array[],int64_t beg,int64_t end)
{
//定义好区间的范围
int64_t left=beg;
int64_t right=end-1;
//取最后一个元素为基准值
int key=array[end-1];
//循环将元素放在合适的位置
while(left<right)
{
//(1)从左向右寻找大于key的元素
while(left<right&&array[left]<key)
{
left++;
}
//(2)从右向左寻找小于key的元素
while(left<right&&array[right]>=key)
{
right--;
}
//找到之后进行交换
if(left<right)
{
Swap(&array[left],&array[right]);
}
}
//退出while循环后,将left、right同时指向的元素与key进行交换
Swap(&array[left],&array[end-1]);
//返回基准值下标位置
return left;
}
4. 非递归版本实现快速排序
在递归实现快排中由于函数栈帧为其提供了栈结构,故在非递归版本中需要我们自己创建栈并利用栈结构实现。
void QuickSortByLoop(int array[],int64_t size)
{
//当size<=1时直接return
if(size<=1)
return;
//创建栈
SeqStack stack;
SeqStackInit(&stack);
int64_t beg=0;
int64_t end=size;
SeqStackPush(&stack,beg);
SeqStackPush(&stack,end);
//循环条件为栈中含有元素
while(stack.size>0)
{
SeqStackTop(&stack,&end);
SeqStackPop(&stack);
SeqStackTop(&stack,&beg);
SeqStackPop(&stack);
int64_t mid=Partion(array,beg,end);
SeqStackPush(&stack,beg);
SeqStackPush(&stack,mid);
SeqStackPush(&stack,mid+1);
SeqStackPush(&stack,end);
}
}