快速排序
快速排序最坏的情况(在接近有序或有序时)是每次选的key都是最大或最小的,时间复杂度是O(N^2);最好的情况是每次都选的key都是中位数,时间复杂度是O(N* LogN),
方法一:左右指针法
int PartSort1(int* a, int begin, int end)//单趟快排方法1:左右指针法
{
int midIndex = GetMidIndex(a, begin, end);
Swap(&a[midIndex], &a[end]);//把中间值放到最后
int keyindex = end;
while (begin < end)//选哪一边做keyindex要让另一边先走,否则begin和end会错过
{
while (begin < end && a[begin] <= a[keyindex])//找大的
{
++begin;
}
while (begin < end && a[end] >= a[keyindex])//找小的
{
--end;
}
Swap(&a[begin], &a[end]);//左边找大,右边找小,再交换
}
Swap(&a[begin], &a[keyindex]);//begin和end相遇时交换begin和Keyindex
return begin;//返回相遇的位置,将原数组分成两段,再递归
}
方法二:挖坑法
int PartSort2(int* a, int begin, int end)//单趟快排方法2:挖坑法
{
int midIndex = GetMidIndex(a, begin, end);
Swap(&a[midIndex], &a[end]);
int keyindex = a[end];//end是第一个坑(坑的意思是该位置的值被拿走了,可以覆盖新的值)
while (begin < end)
{
while (begin < end && a[begin] <= keyindex)//找大的
{
++begin;
}
a[end] = a[begin];//左边找的比key大的坑填到右边,begin位置形成新的坑
while (begin < end && a[end] >= keyindex)//找小的
{
++end;
}
a[begin] = a[end];//右边找的比key小的坑填到左边,end位置形成新的坑
}
a[begin] = keyindex;
return begin;//返回相遇的位置,将原数组分成两段
}
方法三:前后指针法
int PartSort3(int* a, int begin, int end)//单趟快排方法3:前后指针法
{
int cur = begin;
int prev=begin-1;
int keyindex = end;
while (cur < end)
{
if (a[cur] < a[keyindex] && ++prev != cur)//prev和cur相等可以不交换
{
Swap(&a[prev], &a[cur]);
}
++cur;
}
Swap(&a[++prev], &a[keyindex]);
return prev;
}
快速排序递归实现
//快速排序递归三种实现方法
void QuickSort(int* a, int left, int right)//快排空间复杂度O(LogN)
{
//assert(a);
//if (left >= right)//第一种方法
//{
// return;
//}
//int div = PartSort3(a, left, right);
//接收中间值,将整个数组分为三部分 [[left,div-1],div,[div+1,right]]再递归
//QuickSort(a, left, div - 1);
//QuickSort(a, div + 1, right);
//if (left < right)//第二种方法
//{
// int div = PartSort3(a, left, right);
// QuickSort(a, left, div - 1);
// QuickSort(a, div + 1, right);
//}
//针对第3种方法的优化,当小于10个数时不再使用递归排序,改用直接插入排序(效率更高)
assert(a);
if (left >= right)
{
return;
}
if ((right - left + 1) > 10)
{
int div = PartSort3(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
else
{
InsertSort(a + left, right - left + 1);//小区间优化
}
}
快速排序迭代法
void QuickSortNonR(int* a, int left, int right)//快速排序非递归实现(效率更高,避免栈溢出)
{
ST st;
StackInit(&st);
StackPush(&st, right);
StackPush(&st, left);
while (!StackEmpty(&st))//不为空就继续
{
int begin = StackTop(&st);
StackPop(&st);
int end = StackTop(&st);
StackPop(&st);
int div = PartSort3(a, begin, end);//分割成3部分
if (div + 1 < end)
{
StackPush(&st, end);
StackPush(&st, div+1);
}
if (begin < div - 1 )
{
StackPush(&st, div - 1);
StackPush(&st, begin);
}
}
StackDestroy(&st);
}
归并排序递归法
// 归并排序递归实现
void _MergeSort(int* a, int left, int right, int* tmp)//这是一段临时空间
{
if (left >= right)// >表示这个区间不存在了,=表示只有一个值
{
return;
}
int mid = (left + right) / 2;//将整个数组分割为两个部分[left,mid][mid+1,right]
_MergeSort(a, left, mid, tmp);//不断递归
_MergeSort(a, mid+1,right, tmp);
int begin1 = left, end1 = mid;//原数组分为两个有序数组后归并(类似合并两个有序数组OJ题)
int begin2 = mid + 1, end2 = right;
int index = begin1;
while (begin1 <= end1 && begin2 <= end2)//两个数组都没合并结束才继续
{
if (a[begin1] < a[begin2])
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while (begin1 <= end1)//当其中一个数组完成拷贝后还剩下一个,但不知道是哪个,所以判断一下,只有一个数组的数据能进入index
{
tmp[index++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
for (int i = left; i <= right; ++i)//有序数组已经拷贝到tmp,但tmp是临时开辟的数组,所以要将tmp拷贝到原数组
{
a[i] = tmp[i];
}
}
void MergeSort(int* a, int n)//归并排序递归实现,
{
assert(a);
int* tmp = malloc(sizeof(int) * n);//tmp是临时数组
_MergeSort(a, 0, n - 1, tmp);//_MergeSort的意思是MergeSort的子函数
free(tmp);
}
归并排序迭代法
//归并排序非递归实现
void MergeArr(int* a, int begin1, int end1, int begin2, int end2, int* tmp)
{
int left = begin1, right=end2;
int index = begin1;
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++];
}
for (int i = left; i <= right; i++)//有序数组已经拷贝到tmp,但tmp是临时开辟的数组,所以要将tmp拷贝到原数组
{
a[i] = tmp[i];
}
}
void MergeSortNonR(int* a, int n)
{
assert(a);
int* tmp = malloc(sizeof(int) * n);//tmp是临时数组
int gap = 1;
while (gap < n )
{
for (int i = 0; i < n; i += 2 * gap)//注意边界控制![i,i+gap-1][i+gap,i+2*gap-1]
{
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
if (begin2 >= n)//合并时只有第一组,没有第二组,就不需要合并
{
break;
}
if (end2 >= n)//合并时第二组只有部分数据,需要修正end2的边界
{
end2 = n - 1;
}
MergeArr(a, begin1, end1, begin2, end2, tmp);
}
//PrintArray(a, n);
gap *= 2;
}
free(tmp);
}