首先先把程序大体列出来
#include <stdio.h>#include <time.h>#define N 10void output(int *a, int n);void insertSort(int *a, int n);int main(int argc, char const *argv[]){ int i, j; int a[N] = {72, 41, 26, 45, 5, 83, 3, 37, 82, 45}; Sort(a, N);//这里是相应的排序方法 output(a, N); return 0;}void output(int *a, int n){ //输出函数 for (int i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n");}
1. 插入排序
插入排序, 思路就是将从第二个数开始的数依次和它前面的序列相比较,找到自己的位置,然后插进去
c语言:
void insertSort(int *a, int n)//插入排序
{
int temp;
int i, j;
for (i = 1; i < n; i++)
{
temp = a[i];
for (j = i - 1; j >= 0; j--)
{
if (temp < a[j])
a[j + 1] = a[j]; // 序列后移,第一次时a[j+1]其实就是a[i]
else //找到插入的位置就准备插入进去,先跳出
break;
}
a[j + 1] = temp;
}
}
时间复杂度:O(n2), 但是对于这几个数据来说真看不出什么,毫秒小数点后八位都输出不出来
顺便说一下c语言怎么输出程序用时
# include <stdio.h>
# include <windows.h>
# include <time.h>
main()
{
long op,ed;
startTime=clock();
Sleep(1000);
endTime=clock();
printf("%ldms\n",endTime-startTime);
}
2. 希尔排序
希尔排序是插入排序的改进算法,它的思路其实很散,因此也更难以理解。这里因为要放在一起所以没有对逻辑进行进一步的划分。
void XierSort(int *a, int n)
{ // 希尔排序
int temp;
int i, pos, k, s;
int dk[3] = {5, 3, 1}; // 增量间隔
for (i = 0; i < 3; i++)
{
int d = dk[i]; // dk[i]代表间隔d
for (pos = 0; pos < d; pos++) // 在间隔里依次遍历
{
//之后的循环是进行插入排序
for (k = pos + d; k < n; k += d)
{
temp = a[k];
for (s = k - d; s >= 0; s -= d)
{
if (a[s] > temp)
a[k + d] = a[k];
else
break;
}
a[s + d] = temp;
}
}
}
}
看着是四层循环,但是其实时间复杂度应该在O(n1.3)~O(n1.5)
我们先来看看外层循环, 先定义了一个d[k]用于存放增量间隔,其中M是可以改变的,选择的增量是可以改变的。如
int
dk[5] = {5, 4, 3, 2, 1}; // 增量间隔
就题目来讲,dk的选择为5, 3, 1,其实是最优的一种选择。
void XierSort(int *a, int n)
{ // 希尔排序
int temp;
int i, pos, k, s;
int dk[M] = {5, 3, 1}; // 增量间隔
for (i = 0; i < M; i++)
{
int d = dk[i]; // dk[i]代表间隔d
for (pos = 0; pos < d; pos++) // 在间隔里依次遍历
{
//之后的循环是进行插入排序
...
}
}
}
我们再看看内部循环,这是思路最拧的部分
for (k = pos + d; k < n; k += d)
{
temp = a[k];
for (s = k - d; s >= 0; s -= d)
{
if (a[s] > temp)
a[k + d] = a[k];
else
break;
}
a[s + d] = temp;
}
框架其实就是插入排序,但条件有变化。
简述思路:将间隔为d的数据看为一组(注意,这里不止两个,可能有很多个),然后进行插入排序。
-
第一次循环时,组里有pos一个元素,将pos+d放进组里并且保证其有序。
-
第二次循环时,组里有pos,pos+d两个元素,将pos+2d放进组里并保证有序。
-
...
3. 交换排序
冒泡排序
这个没什么可说的,最基本的排序
void bubbleSort(int *a, int n)
{
int temp;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j + 1])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
时间复杂度为O(n2), 稳定算法。
这里先留个坑,冒泡排序还有一种更优化的模式双向冒泡排序,之后做补充。
快速排序
快排是最快的排序方式,在它面前,时间复杂度是虚的
void quickSort(int *a, int first, int end){ int mid; if (first < end) { mid = QSort(a, first, end); quickSort(a, first, mid - 1); quickSort(a, mid + 1, end); }}int QSort(int *a, int first, int end){ int pivot = a[first]; while (first < end) { while (pivot <= a[end] && first < end) { end--; } a[first] = a[end]; while (pivot >= a[first] && first < end) { first++; } a[end] = a[first]; } a[first] = pivot; return first;}
时间复杂度是**O(nlbn)**但因为运用了递归,所以空间复杂度会很高。
先来分析框架部分
void quickSort(int *a, int first, int end)
{
int mid;
if (first < end)
{
mid = QSort(a, first, end);
quickSort(a, first, mid - 1);
quickSort(a, mid + 1, end);
}
}
先用第一个数作为枢纽,用QSort函数对其进行第一次排序,使它的左边都小于枢纽,右边都大于枢纽。之后再对其左边和右边分别递归。
再来看核心代码
int QSort(int *a, int first, int end)
{
int pivot = a[first];//定义枢纽为这个子序列的第一个,这个时候就体现出变量取名的重要性了
while (first < end)
{
//第一步,从最远处开始找到比枢纽值小的值
while (pivot <= a[end] && first < end)
{
end--;
}
//运行到这里就找到了比provot小的值(a[end])或者已经first>=end了
a[first] = a[end];
while (pivot >= a[first] && first < end)
{
first++;
}
//运行到这里就找到了比provot大的值(a[first])或者已经first>=end了
a[end] = a[first];
}
a[first] = pivot;//最后再让“中间”值等于枢纽值
return first;
}