1.插入排序
步骤:
1.从第一个元素开始,只有一个元素时,该元素必定有序,所以从数组的第二个元素开始排序
2.从数组取出排好序的部分后的第一个数字tmp,和已经排好序的部分进行比较
3.将tmp从后往前依次和有序部分的数进行比较,设置一个寻找比较的指针为end,如果该tmp小于end指向的数,则将end指向的数赋值给end+1所指向的位置,end--
4.重复步骤3,直到找到已排序元素中小于tmp的数
5.如果已排序所有元素都大于tmp,则将tmp插入到下标为0的位置
6.重复步骤2~5
思路:
在待排序的元素中,假设前n-1个元素已有序,现将第n个元素插入到前面已经排好的序列中,使得前n个元素有序。按照此法对所有元素进行插入,直到整个序列有序。
但我们并不能确定待排元素中究竟哪一部分是有序的,所以我们一开始只能认为第一个元素是有序的,依次将其后面的元素插入到这个有序序列中来,直到整个序列有序为止。
代码:
#include<stdio.h>
void InsertSort(int* a, int n) {
for (int i = 1; i < n; i++)
{
int end = i-1;
int tmp = a[i];
while (end >= 0)
{
if (tmp < a[end])//比插入的数大就向后移
{
a[end + 1] = a[end];
end--;
}
else//比插入的数小,跳出循环
{
break;
}
}
//将tmp赋值运算放在循环外面,是因为有两种情况,一种是找到比tmp小的,break结束循环,第二种情况情况是找不到比tmp小的,退出循环,如果把赋值运算放在else,第二种情况就无法处理
a[end + 1] = tmp;
//tmp放到比插入的数小的数的后面
}
}
int main() {
int a[] = { 7,8,2,0,3,1,7,9,4,5 };
InsertSort(a,sizeof(a)/sizeof(a[0]));
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%d ", a[i]);
}
}
时间复杂度:数组是逆序或者接近逆序是最坏情况,为O(N*N)
数组是顺序或者接近顺序是最好情况,为O(N)。
空间复杂度:O(1)
2.希尔排序
步骤:
1.先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作…
2.当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。
思路:
希尔排序,先将待排序列进行预排序,使待排序列接近有序,然后再对该序列进行一次插入排序,此时插入排序的时间复杂度为O(N),
代码如下:
void ShellSort(int* a, int n) {
int gap = n;
while (gap != 1)
{
gap = gap / 3 + 1;
for (int i = 0; i < n - gap; i++)
{
int end = i;
int tmp = a[i+gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
时间复杂度平均:O(N^1.3)
空间复杂度:O(1)
3.冒泡排序
思路:
左边大于右边交换一次,依次遍历整个数组,一趟排下来最大的在右边,将最大的数排除在外,再重复这个过程,直到待排序的数为1或者有序结束。
代码如下:
void BubbleSort(int* a, int n) {
for (int j = n - 1; j > 0; j--)
{
int flag = 0;
for (int i = 0; i < j; i++)
{
if (a[i] > a[i + 1])
{
int tmp = a[i];
a[i] = a[i + 1];
a[i + 1] = tmp;
flag = 1;
}
if (flag == 0)
{
break;
}
}
}
}
时间复杂度:最坏情况:O(N^2)
最好情况:O(N)
空间复杂度:O(1)
4.选择排序
思路:
每次从待排序列中选出一个最大值,然后放在序列的尾部位置,直到全部待排数据排完即可。
void SelectSort(int* a, int n)
{
int right = n-1;
while (right)
{
int cur = right-1;
int max = a[right];
while (cur >= 0)
{
if (max < a[cur])
{
int tmp = a[cur];
a[cur] = max;
max = tmp;
}
cur--;
}
a[right] = max;
right--;
}
}
时间复杂度:最坏情况:O(N^2)
最好情况:O(N^2)
空间复杂度:O(1)
5.堆排序
//降序
void HeapSort(int* a, int n)
{
//建小堆
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
int end = n - 1;
//把最小的换到最后一个位置,不把最后一个数看作堆里的
//每次选出剩下数中最小的
//从后往前放
while (end > 0)
{
int tem = a[end];
a[end] = a[0];
a[0] = tem;
//选出次小的数
AdjustDown(a, end, 0);
--end;
}
}
6.快速排序
5.1 hoare版本(左右指针法)
5.1 hoare版本(左右指针法)
思路:
1、选出一个key,一般是最左边或是最右边的。
2、定义一个begin和一个end,begin从左向右走,end从右向左走。(需要注意的是:若选择最左边的数据作为key,则需要end先走;若选择最右边的数据作为key,则需要bengin先走)。
3、在走的过程中,若end遇到小于key的数,则停下,begin开始走,直到begin遇到一个大于key的数时,将begin和right的内容交换,end再次开始走,如此进行下去,直到begin和end最终相遇,此时将相遇点的内容与key交换即可。(选取最左边的值作为key)
4.此时key的左边都是小于key的数,key的右边都是大于key的数
5.将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作,此时此部分已有序
代码:
int PastSort(int* a, int left, int right) {
Max(&a[left], &a[right], &a[(left + right) / 2]);
int keyi = left;
while (left < right)
{
while (left < right && a[right] >= a[keyi])
{
right--;
}
while (left < right && a[left] <= a[keyi])
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[keyi], &a[right]);
return right;
}
QuickSort(int* a, int begin, int end) {
if (begin >= end) {
return;
}
int keyi = PastSort(a, begin, end);
QuickSort(a, begin, keyi-1);
QuickSort(a, keyi+1, end);
}
时间复杂度:n*logn
5.2 挖坑法
思路:
挖坑法思路与hoare版本(左右指针法)思路类似
1.选出一个数据(一般是最左边或是最右边的)存放在key变量中,在该数据位置形成一个坑
2、还是定义一个L和一个R,L从左向右走,R从右向左走。(若在最左边挖坑,则需要R先走;若在最右边挖坑,则需要L先走)
后面的思路与hoare版本(左右指针法)思路类似
代码:
int PastSort(int* a, int left, int right) {
Max(&a[left], &a[right], &a[(left + right) / 2]);
int key = a[left];
int hole = left;
while (left < right)
{
while (left < right && a[right] >= key)
{
right--;
}
a[hole] = a[right];
hole = right;
while (left < right && a[left] <= key)
{
left++;
}
a[hole] = a[left];
hole = left;
}
a[hole] = key;
return hole;
}
QuickSort(int* a, int begin, int end) {
if (begin >= end) {
return;
}
int keyi = PastSort(a, begin, end);
QuickSort(a, begin, keyi-1);
QuickSort(a, keyi+1, end);
}
5.3 前后指针法
思路:
1、选出一个key,一般是最左边或是最右边的。
2、起始时,prev指针指向序列开头,cur指针指向prev+1。
3、若cur指向的内容小于key,则prev先向后移动一位,然后交换prev和cur指针指向的内容,然后cur指针++;若cur指向的内容大于key,则cur指针直接++。如此进行下去,直到cur到达end位置,此时将key和++prev指针指向的内容交换即可。
经过一次单趟排序,最终也能使得key左边的数据全部都小于key,key右边的数据全部都大于key。
然后也还是将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作
代码:
void Swap(int* a, int* b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
int PastSort(int* a, int left, int right) {
int prev = left;
int cur = left + 1;
int keyi = left;
while (cur <= right)
{
if (a[cur] < a[keyi] && prev++ != cur)
{
Swap(&a[prev], &a[cur]);
}
cur++;
}
Swap(&a[keyi], &a[prev]);
keyi = prev;
return keyi;
}
void QuickSort(int* a, int begin, int end) {
if (begin >= end)
{
return;
}
int keyi = PastSort(a, begin, end);
QuickSort(a, begin, keyi - 1);
QuickSort(a, keyi+1, end);
}
5.4 非递归
void QuickSort(int* a, int begin, int end) {
Stack s;
InitStack(&s);
StackPush(&s, end);
StackPush(&s, begin);
while (!StackEmpty(&s))
{
int left = StackTop(&s);
StackPop(&s);
int right = StackTop(&s);
StackPop(&s);
if (left < right)
{
int keyi = PastSort(a, left, right);
StackPush(&s, right);
StackPush(&s, keyi+1);
StackPush(&s, keyi-1);
StackPush(&s, left);
}
}
}