排序算法

133 阅读2分钟

首先先把程序大体列出来

#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;
}