快速排序是二路划分算法,如果待排序序列中的重复元素过多,即便是经过三数取中和小区间优化,排序的效率也不高。这种情况下,如果采用三路划分,则可以很好地避免上述问题。
#define MAX_LENGTH_INSERT_SORT 7 // 数组长度阈值
void Swap(int* e1, int* e2)
{
int tmp = *e1;
*e1 = *e2;
*e2 = tmp;
}
void InsertSort(int* arr, int n)
{
for (int i = 1; i < n; ++i)
{
int tmp = arr[i];
int end = i - 1;
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + 1] = arr[end];
--end;
}
else
{
break;
}
}
arr[end + 1] = tmp;
}
}
int GetMidIndex(int* arr, int left, int right)
{
// 1. 三数取中
// int mid = left + (right - left) / 2;
// 2. 随机取值
srand((unsigned int)time(NULL)); // 设置随机数种子
int mid = left + rand() % (right - left + 1); // mid: [left, right]
if (arr[left] < arr[right])
{
if (arr[mid] < arr[left])
return left;
else if (arr[mid] < arr[right])
return mid;
else
return right;
}
else // arr[right] <= arr[left]
{
if (arr[mid] < arr[right])
return right;
else if (arr[mid] < arr[left])
return mid;
else
return left;
}
}
void QSort(int* arr, int left, int right)
{
if (right - left + 1 < MAX_LENGTH_INSERT_SORT) // 小区间优化
{
InsertSort(arr + left, right - left + 1);
}
else
{
int ret = GetMidIndex(arr, left, right); // 三数取中
if (ret != left)
{
Swap(&arr[left], &arr[ret]);
}
int pivotkey = arr[left];
int begin = left, end = right; // 记录排序区间的起始和终止位置
// 三路划分
int cur = left + 1;
while (cur <= right)
{
if (arr[cur] < pivotkey)
{
Swap(&arr[cur], &arr[left]);
++cur;
++left;
}
else if (arr[cur] > pivotkey)
{
Swap(&arr[cur], &arr[right]);
--right;
}
else
{
++cur;
}
}
QSort(arr, begin, left - 1);
QSort(arr, right + 1, end);
}
}
void QuickSort(int* arr, int n)
{
QSort(arr, 0, n - 1);
}
因为 arr[left] == pivotkey,当 arr[cur] < pivotkey,进行交换后,即
Swap(&arr[cur], &arr[left]);
,arr[left] < pivotkey,arr[cur] == pivotkey,所以让 left 和 cur 同时向右移,即++left; ++cur;
。因为 arr[right] 的值不确定,当 arr[cur] > pivotkey,进行交换后,即
Swap(&arr[cur], &arr[right]);
,arr[right] > pivotkey,但 arr[cur] 的值不确定,所以只让 right 向左移,即--right;
。当 arr[cur] == pivotkey 时,
++cur;
。
创作不易,可以点点赞,如果能关注一下博主就更好了~