目录
1.快速排序
1.1快速排序的基本思想
快速排序是一种二叉树交换结构的排序法,首先取序列中的某个元素作为基准值,用这个基准值把整个序列分割成两个序列。例如升序就是把小于基准值的元素放在左子序列,大于基准值的数放在右子序列,最后左右子序列重复这个过程直到完成排序。
图片演示:
1.2代码实现(hoare版本递归):
void swap(int* p1, int *p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int PartQuickSort(int* a, int left, int right)
{
int keyi = left;
int begin = left;
int end = right;
while (begin < end)
{
while (begin < end && a[end] >= a[keyi])
{
end--;
}
while (begin < end && a[begin] <= a[keyi])
{
begin++;
}
swap(&a[begin], &a[end]);
}
swap(&a[keyi], &a[end]);
keyi = end;
return keyi;
}
void QuickSort(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi = PartQuickSort(a, begin, end);
QuickSort(a, begin, keyi - 1);
QuickSort(a, keyi + 1 , end);
}
上面的代码基准值是取数组的第一个元素,其实还可以用三数取中法优化一下,找到数组第一个元素、中间的元素和最后一个元素中间大的那个数。
例如:
int GetMidIndex(int* a, int left, int right)
{
int mid = (left + right) / 2;
if (a[left] < a[mid])
{
if (a[mid] < a[right])
{
return mid;
}
else if (a[left] < a[right])
{
return right;
}
else
{
return left;
}
}
else
{
if (a[mid] > a[right])
{
return mid;
}
else if (a[left] > a[right])
{
return right;
}
else
{
return left;
}
}
}
int PartQuickSort(int* a, int left, int right)
{
int mid = GetMidIndex(a, left, right);
swap(&a[mid], &a[left]);
int keyi = left;
int begin = left;
int end = right;
while (begin < end)
{
while (begin < end && a[end] >= a[keyi])
{
end--;
}
while (begin < end && a[begin] <= a[keyi])
{
begin++;
}
swap(&a[begin], &a[end]);
}
swap(&a[keyi], &a[end]);
keyi = end;
return keyi;
}
void QuickSort(int* a, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi = PartQuickSort(a, begin, end);
QuickSort(a, begin, keyi - 1);
QuickSort(a, keyi + 1 , end);
}
2.归并排序
2.1归并排序的思想
归并排序的算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。先使每个子序列有序,再使子序列段间有序。将两个有序序列合成一个有序序列称为二路归并。
现将序列平均分成两个序用二路归并法对两个子序列递归的排序。当递归到每个序列只有一个元素时,我们称之为序列有序就开始归并,搞一个临时数组,把两个有序序列合成一个有序序列。
2.2递归实现
void _MergeSort(int* a, int begin, int end, int* tmp)
{
if (begin == end)
{
return;
}
int mid = (begin + end) / 2;
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid+1,end, tmp);
int begin1 = begin;
int end1 = mid;
int begin2 = mid + 1;
int end2 = end;
int i = begin;
while ( begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
2.3小优化一下
当序列递归到小于10个元素时可以用插入排序进行直接排序 可以减少80%多的递归
void _MergeSort(int* a, int begin, int end, int* tmp)
{
if (begin == end)
{
return;
}
//小区间优化
if (end - begin + 1 < 10)
{
InsertSort(a + begin, end - begin + 1);
return;
}
int mid = (begin + end) / 2;
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid+1,end, tmp);
int begin1 = begin;
int end1 = mid;
int begin2 = mid + 1;
int end2 = end;
int i = begin;
while ( begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
3计数排序
计数排序适用于一定范围的整数排序。在取值范围不是很大的情况下,它的性能在某些情况甚至快过那些O(nlogn)的排序,例如快速排序、归并排序。
3.1计数排序的思想
计数排序需要搞一个数组来对应序列中的元素,例如序列:112233405566就开一个数组下标0~6
遍历序列,元素出现就在数组对应位置+1
最后按照数组的计数还原序列
动图示例:
3.2代码实现
为了防止序列最小值为100或者10000,造成开数组时的空间浪费,可以先遍历一次要排序的序列找出序列最小的元素,所有元素在技术时减去这个最小值,还原序列时再加回来。
void CountSort(int* a, int n)
{
int min = a[0];
int max = a[0];
for (int i = 0; i < n; i++)
{
if (a[i] < min)
{
min = a[i];
}
if (a[i] > max)
{
max = a[i];
}
}
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
if (count == NULL)
{
perror("malloc fail");
}
memset(count, 0, sizeof(int) * range);
for (int i = 0; i < n; i++)
{
count[a[i] - min]++;
}
int k = 0;
for (int i = 0; i < range; i++)
{
while (count[i]--)
{
a[k++] = i+min;
}
}
}