//*********************三数取中法(快排的优化)****************************
//未优化|我们总是把数组尾元素固定为key的值,然后通过调整让key回到它正确的位置
//前快排|然后以key为中心,把数组划分为key左边和key右边两个区域,然后继续在左边
//的缺陷|和右边划分区域,直到最后区域里没有元素或者只有一个元素的时候,排序结束
// |理想的是key的位置刚好在数组的中间,那么两边划分区域就快一点,但是如果
// |key的值很小或者很大呢?那么左边或者右边至少有一边是没有元素的,这样效率就低了
//思路:因为key的值最好每次都能把数组分成二分的两半,所以key的值最好是区域内比较居中的
// 值,所以每次把区域内的首元素、尾元素、中间的元素做比较,选出不大不小的那个,
// 然后把选出来的这个值,交换到数组的尾部,以便调整后它能回到数组中间的位置
//取首元素和尾元素及其中间元素中不大不小的数当作key值
int
GetMidNum(
int
*
a
,
int
left
,
int
right
)
{
assert
(
a
);
if
(
left
>=
right
)
return
-1;
int
mid =
left
+ (
right
-
left
) / 2;
if
(
a
[
left
] <
a
[
right
])
{
if
(
a
[
right
] <
a
[mid])
return
right
;
else
if
(
a
[
left
] <
a
[mid])
return
mid;
else
return
left
;
}
else
{
if
(
a
[
left
] <
a
[mid])
return
left
;
else
if
(
a
[
right
] <
a
[mid])
return
mid;
else
return
right
;
}
}
int
PartSort(
int
*
a
,
int
left
,
int
right
)
{
//每次进来取数值比较居中的元素的位置
int
midIndex = GetMidNum(
a
,
left
,
right
);
//保证key在需排序的区域内的右边
swap(
a
[midIndex],
a
[
right
]);
//不能写为 swap(key, a[right])
int
key =
a
[midIndex];
int
cur =
left
;
int
prev =
left
- 1;
while
(cur <
right
)
{
//只要a[cur] < key成立,则++prev,不管最后进不进去if循环
if
(
a
[cur] < key && ++prev != cur)
{
swap(
a
[prev],
a
[cur]);
}
cur++;
}
swap(
a
[
right
],
a
[++prev]);
return
prev;
}
void
QuickSort_OP(
int
*
a
,
int
left
,
int
right
)
{
assert
(
a
);
if
(
left
>=
right
)
return
;
//当区域很小的时候,插入排序比快排递归效率高
if
(
right
-
left
<= 20)
{
InsertSort(
a
+
left
,
right
-
left
+ 1);
}
int
mid = PartSort(
a
,
left
,
right
);
QuickSort_Non(
a
,
left
, mid - 1);
QuickSort_Non(
a
, mid + 1,
right
);
}
void
QuickSort_OPTest()
{
int
arr[] = { 2, 0, 4, 9, 3, 6, 8, 7, 1, 5 };
//int arr[] = { 2, 5, 4, 9, 3, 5, 8, 7, 1, 5 };
int
_size =
sizeof
(arr) /
sizeof
(arr[0]);
QuickSort_Non(arr, 0, _size - 1);
Print(arr, _size);
}