static void Swap(int *arr, int low, int high)
{
int tmp = arr[low];
arr[low] = arr[high];
arr[high] = tmp;
}
static int Pivotkey(int *arr, int low, int high)//三数取中
{
int mid = (high - low) / 2 + low;
if (arr[low] > arr[high])
{
Swap(arr, low, high);
}
if (arr[mid] > arr[high])
{
Swap(arr, mid, high);
}
//经过以上两步,high大于low和mid
if (arr[mid] > arr[low])
{
Swap(arr, mid, low);
}
//经过以上三步,mid <= low <= high
return arr[low];
}
static int Partition(int *arr, int low, int high, int *pleftlen, int *prightlen)//三数取中加处理相等的数据
{
int pivot = Pivotkey(arr, low, high);
//这两个给交换到基准旁时使用
int first = low;
int last = high;
//这两个给放两边时使用
int left = low;
int right = high;
while (low < high)
{
while ((low < high) && arr[high] >= pivot)
{
if (arr[high] == pivot)
{
Swap(arr, high, right);
right--;
(*prightlen)++;
}
high--;
}
arr[low] = arr[high];
while ((low < high) && arr[low] <= pivot)
{
if (arr[low] == pivot)
{
Swap(arr, low, left);
left++;
(*pleftlen)++;
}
low++;
}
arr[high] = arr[low];
}
arr[low] = pivot;
//将两边相等的移到low的两边
int i = first;
int j = low - 1;
while (i < left && arr[j] != pivot)
{
Swap(arr, i, j);
i++;
j--;
}
i = low + 1;
j = last;
while (j > right && arr[i] != pivot)
{
Swap(arr, i, j);
i++;
j--;
}
return low;
}
static void Quick(int *arr, int low, int high)
{
if (low < high)
{
int leftlen = 0;
int rightlen = 0;
int par = Partition(arr, low, high, &leftlen, &rightlen);
Quick(arr, low, par - 1 - leftlen);
Quick(arr, par + 1 + rightlen, high);
}
}
void QuickSort(int *arr, int len)//快排
{
Quick(arr, 0, len - 1);
}
上面的快排是优化过的,对于有序数据及相同数据的优化。
三数取中,这样可以在数据有序时,时间复杂度不至于沦为O(n^2)。
但是当数据重复时,三数取中,取得那个基准还是那个数,这样每次的分割,还是只能分出去一个数,时间复杂度还是O(n^2)。
于是就有了这个优化:每次的partition,都将跟pivotkey相等的数据放在pivotkey两边,下来的递归不将与pivotkey相等的数据算在内。
三数取中,这样可以在数据有序时,时间复杂度不至于沦为O(n^2)。
但是当数据重复时,三数取中,取得那个基准还是那个数,这样每次的分割,还是只能分出去一个数,时间复杂度还是O(n^2)。
于是就有了这个优化:每次的partition,都将跟pivotkey相等的数据放在pivotkey两边,下来的递归不将与pivotkey相等的数据算在内。
代码本身不难理解,主要就是快排的时间复杂度的理解不好理解。理想状态下为什么是O(nlogn),最差状态下为什么是O(n^2)。
其实这样理解快排的话,时间复杂度就好理解了。快排就是将数据切成一个一个的,这也就意味着,怎样数据切的越快,那它的时间复杂度就越低。
显然,一半一半得切,最快。一个一个得切,最慢。
一半一半得切,需要切logn次;一个一个得切,需要切n次。
所以,如果每次切,都将数据切成两半了,那就是最好的了。这样时间复杂度就为O(nlogn),空间复杂度为O(logn)。
相反,如果每次切,都只能切一个,那就是最差情况了。这样的时间复杂度为O(n^2),空间复杂度为O(n);
其实这样理解快排的话,时间复杂度就好理解了。快排就是将数据切成一个一个的,这也就意味着,怎样数据切的越快,那它的时间复杂度就越低。
显然,一半一半得切,最快。一个一个得切,最慢。
一半一半得切,需要切logn次;一个一个得切,需要切n次。
所以,如果每次切,都将数据切成两半了,那就是最好的了。这样时间复杂度就为O(nlogn),空间复杂度为O(logn)。
相反,如果每次切,都只能切一个,那就是最差情况了。这样的时间复杂度为O(n^2),空间复杂度为O(n);