直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序
排序的思想分为单趟排序,然后进行多趟条件限制
1.直接插入排序
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
单趟排序end,插入tmp。如果tmp小于end,那么把end和end+1交互位置,否则要么到最前面,要么单趟结束
多趟排序考虑最后一次end在n-2的位置,控制变量即可
void InsertSort(int* a, int n)
{
//控制end在0,n-2
for (size_t i = 0; i < n - 1; ++i)
{
//单趟排序
//在[0,end]插入tmp依旧有序
int end = i;
int tmp = a[end + 1];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
2.希尔排序
先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。相当于优化的插入排序。
预排序+插入排序
//时间复杂度 n^1.3--n^2
//如果gap为1那么就是插入排序
void ShellSort(int* a, int n)
{
//单趟排序,间距为gap的插入排序
//gap为1时插入排序
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
//多次预排
for (size_t i = 0; i < n - gap; ++i)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
3.直接选择排序
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
//每次选择一个最大的数,一个最下的数
void Swap(int* p1, int* p2)
{
int tmp = *p1;
* p1 = *p2;
*p2 = tmp;
}
void SelectSort(int* a, int n)
{
int begin = 0;
int end = n - 1;
while (begin<end)
{
int mini = begin, maxi = begin;
for (int i = begin; i <= end; ++i)
{
if (a[i] > a[maxi])
{
maxi = i;
}
if (a[i] < a[mini])
{
mini = i;
}
}
Swap(&a[begin], &a[mini]);
if (begin == maxi)//一定要注意当第一个为最大的时,那么一定要和mini交换,否则就找不到maxi了
maxi = mini;
Swap(&a[end], &a[maxi]);
++begin;
--end;
}
}
4.堆排序
//先建立堆 从最后一个进行向下调整
//再排序 排序每次把最后一个和第一个交换向下调整 直到排序排完
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
AdjustDown(int* a, int n,int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 <n && a[child + 1] > a[child])
{
++child;
}
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
5.冒泡排序
void Swap(int* p, int* q)
{
int tmp = *p;
*p = *q;
*q = tmp;
}
void BubbleSort(int* a, int n)
{
//当end=0,剩下一个数字则不需要继续进行冒泡了
for (size_t end = n - 1; end > 0; --end)
{
//优化,如果不进行交换则说明已经有序
int flag = 0;
//单趟排序
for (int i = 0; i < end; i++)
{
if (a[i] > a[i + 1])
{
Swap(&a[i], &a[i + 1]);
flag = 1;
}
}
if (flag == 0)
{
break;
}
}
}
6.快速排序(3种方法)
- hoare版本
- 挖坑法
- 前后指针版本
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
//如果选第一个值做关键字,end先走
//如果选最后一个做关键字,begine先走
//时间复杂度有序最坏 N*N
// 最好在中间(N*logN)
//为了防止最坏情况 三数区中法优化
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int GetMidIndex(int* a, int begin, int end)
{
int mid = begin + ((end - begin) >> 1);
if (a[begin] < a[mid])
{
if (a[mid] < a[end])
{
return mid;
}
else if (a[begin] > a[end])
{
return begin;
}
else
{
return end;
}
}
else//a[begin] > a[mid]
{
if (a[mid] > a[end])
{
return mid;
}
else if (a[begin] < a[end])
{
return begin;
}
else
{
return end;
}
}
}
//快排部分
hoare版本
上面为hoare单趟快排过程
int PartSort(int* a, int begin, int end)
{
//三数取中
int mid = GetMidIndex(a, begin, end);
Swap(&a[mid], &a[begin]);
int key = begin;
while (begin < end)
{
while (begin < end && a[end] >= a[key])
{
--end;
}
while (begin < end && a[begin] <= a[key])
{
++begin;
}
Swap(&a[begin], &a[end]);
}
Swap(&a[key], &a[begin]);
return begin;
}
void QuickSort(int* a, int left, int right)
{ //可能左边会大于右边
if (left >= right)
return;
int keyindex = PartSort(a, left, right);
//[left,key-1] key [key+1,right]
QuickSort(a, left, keyindex - 1);
QuickSort(a, keyindex + 1, right);
}
挖坑法
//挖坑法
int PartSort2(int* a, int begin, int end)
{
int mid = GetMidIndex(a, begin, end);
Swap(&a[mid], &a[begin]);
//begine是第一个坑位
int key = a[begin];
while (begin < end)
{
while (begin < end && a[end] >= key)
{
--end;
}
a[begin] = a[end];
while (begin < end && a[begin] <= key)
{
++begin;
}
a[end] = a[begin];
}
a[begin] = key;
return begin;
void QuickSort(int* a, int left, int right)
{ //可能左边会大于右边
if (left >= right)
return;
int keyindex = PartSort1(a, left, right);
//[left,key-1] key [key+1,right]
QuickSort(a, left, keyindex - 1);
QuickSort(a, keyindex + 1, right);
}
指针法
//指针法
int PartSort2(int* a, int begin, int end)
{
int prev = begin - 1;
int cur = begin;
int key = end;
while (cur < end)
{
if (a[cur] < a[key])
{
++prev;
Swap(&a[prev], &a[cur]);
++cur;
}
else
{
++cur;
}
}
Swap(&a[++prev], &a[key]);
return prev;
}
void QuickSort(int* a, int left, int right)
{ //可能左边会大于右边
if (left >= right)
return;
int keyindex = PartSort2(a, left, right);
//[left,key-1] key [key+1,right]
QuickSort(a, left, keyindex - 1);
QuickSort(a, keyindex + 1, right);
}
7.归并排序
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
void _MergeSort(int* a, int begin, int end, int* tmp)
{
if (begin >= end)
return;
//先划分
int mid = (end + begin) >> 1;
//[begin,mid][mid+1,end]
//开始无序
_MergeSort(a, begin, mid, tmp);
_MergeSort(a, mid+1, end, tmp);
//归并两段有序空间
int begin1 = begin, end1 = mid;
int begin2 = mid + 1, end2 = end;
int index = begin;
while (begin1 <= end1 && begin2 <= end2)
{
if (begin1 < begin2)
{
tmp[index++] = a[begin1];
begin1++;
}
else
{
tmp[index++] = a[begin2];
begin2++;
}
}
if (begin1 <= end1)
{
while (begin1 <= end1)
{
tmp[index++] = a[begin1++];
}
}
else if(begin2 <= end2)
{
while (begin2 <= end2)
{
tmp[index++] = a[begin2++];
}
}
//将归并到tmp的数据拷回到原数组
memcpy(a+begin, tmp+begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
//需要临时空间
int* tmp = (int*)malloc(sizeof(int) * n);
_MergeSort(a, 0, n - 1,tmp);
free(tmp);
}
void PrintfArray(int* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
}
8.计数排序
- 统计相同元素出现次数
- 根据统计的结果将序列回收到原来的序列中
void CountSort(int* a, int n)
{
//找出最大和最小的求出范围
int min = a[0], 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* countArr = (int*)malloc(sizeof(int) * range);
memset(countArr, 0, sizeof(int) * range);
//统计相同元素出现次数
for (int i = 0; i < n; i++)
{
countArr[a[i] - min]++;
}
//根据统计的结果将序列回收到原来的序列中
int j = 0;
for (int i = 0; i < range; i++)
{
while (countArr[i]--)
{
a[j++] = i + min;
}
}
}