- 直接插入排序,希尔排序
- 选择排序,堆排序
- 冒泡排序,快速排序
- 归并排序
- 计数排序
直接插入排序
关键元素tmp前面的元素已经是排好序的,把tmp放到合适的地方
void InsertSort(int* a,size_t n)
{
assert(a);
for(size_t i=0;i<n-1;++i)
{
int end=i;
int tmp=a[end+1];
while(end>=0 && a[end]>tmp)
{
a[end+1]=a[end];
--end;
}
a[end+1]=tmp;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
希尔排序
直接插入排序的优化,先进行预排序在进行直接插入排序 将数组分成多个小数组,对每个小组分别进行直接插入排序,整个数组的顺序在这样过后会更接近有序,再对整个数组进行直接插入排序
void ShellSort(int* a,size_t n)
{
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+tmp];
while(end>=0 && a[end]>tmp)
{
a[end+gap]=a[end];
end-=gap;
}
a[end+gap]=tmp;
}
}
InsertSort(a,n);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
对比 | 直接排序 | 希尔排序 |
---|---|---|
时间复杂度 | 最好O(n),最坏O(n^2) | O(N^1.25~1.6) |
空间复杂度 | O(1) | O(1) |
选择排序
每次遍历选取最小(大)的数,然后和最左边(右)的元素交换,再从下一个元素开始遍历(范围缩小),重复上述过程,直到剩下最后一个元素
void SelectSort(int* a,size_t n)
{
assert(a);
for(int i=0;i<n;++i)
{
int min=i;
for(int j=0;j<n;j++)
{
if(a[min]>a[j])
{
min =j;
}
}
Swap(&a[min],&a[i]);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
上面的代码只是每次找出最小的,下面的是同时找最小的和最大的
void SelectSort2(int* a,size_t n)
{
assert(a);
int left=0;
int right=n-1;
while(left<right)
{
int min=left;
int max=left;
for(int i=left;i<=right;i++)
{
if(a[min]>a[i])
{
min =i;
}
if(a[max]<a[i])
{
max=i;
}
}
Swap(&a[left],&a[min]);
if(left!=max)
//防止出现max在左,min在右,min与left交换,max值被改变
{
Swap(&a[right],&a[max]);
}
left++;
right++;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
堆排序
先建堆,之后将收尾进行交换,然后向下调整,直到根结点
堆排序的详解戳链接
void AdjustDown(DataType* a, size_t n, int root)
{
int parent = root;
size_t child = 2*parent+1;
while (child < n)
{
if (child + 1 < n&&a[child + 1] < a[child])
{
++child;
}
if (a[child]<a[parent])
{
DataType tmp = a[child];
a[child] = a[parent];
a[parent] = tmp;
parent = child;
child = 2*parent+1;
}
else
{
break;
}
}
}
void AdjustDown(DataType* a, size_t n, int root)
{
int parent = root;
size_t child = 2*parent+1;
while (child < n)
{
if (child + 1 < n&&a[child + 1] < a[child])
{
++child;
}
if (a[child]<a[parent])
{
DataType tmp = a[child];
a[child] = a[parent];
a[parent] = tmp;
parent = child;
child = 2*parent+1;
}
else
{
break;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
对比 | 选择排序 | 堆排序 |
---|---|---|
时间复杂度 | O(n^2) | O(n*logn) |
空间复杂度 | O(1) | O(1) |
冒泡排序
void Bubble(int* a,size_t n)
{
assert(a && n>0);
for(int end=n;end>0;--end)
{
int flag=0;
for(int i=1;i<end;++i)
{
if(a[i-1]>a[j])
{
Swap(&a[i-1],&a[i]);
flag=1;
}
}
if(flag==0)
break;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
快速排序
首先选数组的最右或最左的元素为key,begin=left;end=right;begin从左到右找比key大的元素,找到就停下来,end从右往左找比key小的元素,找到就停下啦,交换他俩,重复上述,直到begin>=end,不是和key交换,key只是保存最右元素的临时变量
左右指针法
int PartSort1(int* a,int left,int right)//左右指针快排
{
int key=a[right];
int begin=left,end=right;
while (begin < end)
{
while (begin < end && a[begin] <= key)
{
begin++;
}
while (begin < end && a[end] >= key)
{
end--;
}
Swap(&a[begin], &a[end]);
}
Swap(&a[begin], &a[right]);
return begin;
}
void QuickSort(int* a,int left,int right)//快排
{
assert(a);
if (left >= right)
{
return;
}
int div = LeftRightPointer(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
填坑法/挖坑法
int PartSort2(int* a,int left,int right)
{
int key=a[right];
int begin=left,end=right;
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
前后指针法
int PartSort3(int* a,int left,int right)
{
int prev=left-1;
int cur=left;
while(cur<right)
{
if(a[cur]<a[right] && ++prev!=cur)
Swap(&a[cur],&a[prev]);
++cur;
}
Swap(&a[++prev],&a[right]);
return prev;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
归并排序
与快排类似,归排不需要key,而是递归二分,直到只剩下一个或者两个的时候,回溯的时候,比较归并排序,回溯完毕后,整个数组就有序了,需要额外创建一个数组保存数据
void MergeSort(int* a,int* tmp,int left,int right)
{
if(left>=right)
return;
int mid=left+(right-left)>>1;
MergeSort(a,tmp,left,mid);
MergeSort(a,tmp,mid+1,right);
int begin1=left,end1=mid;
int begin2=left,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++];
index=left;
while(index<=right)
{
a[index]=tmp[index];
index++;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
计数排序
遍历一遍数组,得到数组的范围,创建一个大小为范围的数组,即哈希表,初始化为0,从头遍历数组,数字重复出现一个,对应位置上的数加一,从头遍历新数组,数值不为0的位置的下标存储到原数组中,对应位置上的数值是多少,就存储多少个