快速排序
快速排序算法被列为20世纪十大算法之一。
其相当于冒泡排序的升级,只不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面。从而减少了比较次数和移动交换次数。
思路分析
- 单趟快排,将区间分成较大和较小两部分。
- 利用迭代循环,不断细化区间。
- 对区间进行合并,从而实现整体有序。
代码实现
在这里,将采用三种方法来实现快速排序(递归实现)。
三种方法虽说实现的部分代码不同,但代码的内核是相同的,都是不断细分区间再合并。
同时,也将利用非递归的方法来实现快速排序。
方法一:左右指针法
//左右指针法
int PartSort1(int* a, size_t left, size_t right)
{
int begin = left;
int end = right;
int& key = a[end];
while (begin < end)
{
while (begin < end
&& a[begin] <= key)
++begin;
while (begin < end
&& a[end] >= key)
--end;
if (begin < end)
{
swap(a[begin],a[end]);
}
}
swap(a[begin],key);
return begin;
}
//快速排序
void QuickSort(int* a, int left, int right)
{
assert(a);
if (left >= right)
return;
int div = PartSort1(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
针对有序数组的优化
//三数取中
int GetMidIndex(int* a, size_t left, size_t right)
{
int mid = left + ((right - left) >> 1);
if (a[left] < a[mid])
{
if (a[mid] < a[right])
return mid;
else if (a[left] > a[right])
return left;
else
return right;
}
else//left >= mid
{
if (a[mid] > a[right])
return mid;
else if (a[left] < a[right])
return left;
else
return right;
}
}
//左右指针法
int PartSort1(int* a, size_t left, size_t right)
{
int begin = left;
int end = right;
int mid = GetMidIndex(a, left, right);
swap(a[mid], a[end]);
int& key = a[end];
while (begin < end)
{
while (begin < end
&& a[begin] <= key)
++begin;
while (begin < end
&& a[end] >= key)
--end;
if (begin < end)
{
swap(a[begin], a[end]);
}
}
swap(a[begin], key);
return begin;
}
//快速排序
void QuickSort(int* a, int left, int right)
{
assert(a);
if (left >= right)
return;
int div = PartSort1(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
方法二:挖坑法
//挖坑法
int PartSort2(int* a, int left, int right)
{
assert(a);
int begin = left;
int end = right;
int key = a[end];
while (begin < end)
{
while (begin < end
&& a[begin] <= key)
++begin;
a[end] = a[begin];
while (begin < end
&& a[end] >= key)
--end;
a[begin] = a[end];
}
a[begin] = key;
return begin;
}
//快速排序
void QuickSort(int* a, int left, int right)
{
assert(a);
if (left >= right)
return;
int div = PartSort2(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
方法三:前后指针法
//前后指针法
int PartSort3(int* a, int left, int right)
{
assert(a);
int cur = left;
int prev = left - 1;
while (cur < right)
{
if (a[cur] < a[right]
&& ++prev != cur)
swap(a[prev], a[cur]);
cur++;
}
swap(a[++prev], a[right]);
return prev;
}
//快速排序
void QuickSort(int* a, int left, int right)
{
assert(a);
if (left >= right)
return;
int div = PartSort3(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
方法四:非递归实现
//非递归实现快速排序
void QuickSortNonR(int* a, int left, int right)
{
stack<int> s;
s.push(right);
s.push(left);
while (!s.empty())
{
int begin = s.top();
s.pop();
int end = s.top();
s.pop();
int div = PartSort1(a, begin, end);
if (begin < div - 1)
{
s.push(div - 1);
s.push(begin);
}
if (div + 1 < end)
{
s.push(end);
s.push(div + 1);
}
}
}
时间复杂度
- 因为会对数组进行不对的分裂再合并
- 所以会有平均时间复杂度为O(N*lgN)
- 当数组有序时,会出现最坏时间复杂度0(N^2)
- 但因为三数取中法的引进,即针对有序数列进行了优化,所以我们在这里不看最坏时间复杂度
- 因此,快速排序的时间复杂度我们认为是0(N*lgN)
归并排序
利用归并的思想实现的排序方法。
假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列;再继续两两归并,重复,直至得到一个长度为n 的有序序列为止。
思路分析
1、根据递归&迭代的思想来实现此排序
2、将区间分成两个小区间,对两个区间进行重新排序,比较排序后的结果放到一个新的数组里面。
3、小区间不断合并,不断合并,最后得到的即为有序序列。
代码实现
//归并排序
void _MergeSort(int* a, int left, int right, int* tmp)
{
assert(a);
if (left >= right)
return;
int mid = left + ((right - left) >>1);
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int index = left;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1]<a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
memcpy(a+left, tmp + left, sizeof(int)*sizeof(right - left + 1));
}
void MergeSort(int* a,size_t n)
{
assert(a);
int* tmp = new int[n];
_MergeSort(a, 0, n, tmp);
//delete[] tmp;
}
时间复杂度
时间复杂度为O(nlgN),是一个稳定的算法