影响排序算法性能的几个要素

  • 时间性能

  • 辅助空间

  • 算法的复杂性

冒泡排序

冒泡排序的基本思想

两两相邻记录的关键字,如果反序则交换,直4到没有反序的记录为止

Code

void BubbleSort(int k[],int n)
{
    int i,j,temp;

    for(i=0;i<n-1;i++)
    {
        for(j=n-1;j>i;j--)
        {
            if(k[j-1] > k[j])
            {
                temp = k[j-1];
                k[j-1] = k[j];
                k[j] = temp;
            }
        }
    }
}

冒泡排序的要点

  • 两两注意是相邻的两个元素的意思

  • 如果有n个元素需要比较n-1次,每一轮减少1次比较

优化

void BubbleSort(int k[],int n)
{
    int i,j,temp,flag;
    flag = 1;
    for(i=0;i<n-1&&flag;i++)
    {
        flag = 0;
        for(j=n-1;j>i;j--)
        {
            if(k[j-1] > k[j])
            {
                temp = k[j-1];
                k[j-1] = k[j];
                k[j] = temp;
                flag = 1;
            }
        }
    }
}

选择排序

选择排序的基本思想

选择排序算法就是通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换

Code

#include <stdio.h>

void SelectSort(int k[],int n)
{
    int i,j,min,temp;

    for(i=0;i<n-1;i++)
    {
        min = i;

        for(j=i+1;j<n;j++)
        {
            if(k[j]<k[min])
            {
                min = j;
            }
        }

        if(min!=i)
        {
            temp = k[min];
            k[min] = k[i];
            k[i] = temp;
        }
    }
}

直接插入排序

直接插入排序的基本思想

直接插入排序算法(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录数增加1的有序表

Code

void InsertSort(int k[],int n)
{
    int i,j,temp;

    for(i=1;i<n;i++)
    {
        if(k[i] < k[i-1])
        {
            temp = k[i];

            for(j=i-1;k[j] > temp;j--)
            {
                k[j+1] = k[j];
            }

            k[j+1] = temp;
        }
    }
}

希尔排序

Code

void ShellSort(int k[],int n)
{
    int i,j,temp;
    int gap = n;
    do
    {
        gap = gap/3 + 1;
        for(i=gap;i<n;i++)
        {
            if(k[i] < k[i-gap])
            {
                temp = k[i];

                for(j=i-gap;k[j] > temp;j-gap)
                {
                    k[j+gap] = k[j];
                }

                k[j+gap] = temp;
            }
        }
    }while(gap > 1)
}

堆排序

要点

QQ截图20170203230254.jpg

QQ截图20170203230344.jpg

根节点一定是堆中所有节点最大或者最小的,如果按照层序遍历的方式给节点从1开始编号,则结点之间满足如下关系

ki >= k2i                   ki <= k2i   
                    或                      (1<=i<=floor(n/2))
ki >= kv(2i+1)              ki <= kv(2i+1)

堆排序(Heap Sort)就是利用堆进行排序的算法,它的基本思想是:

  • 将待排序的序列构造成一个大顶堆(或小顶堆)

  • 此时,整个序列的最大值就是堆顶的根节点.将它移走(就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值)

  • 然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的最大值

  • 如此反复执行,便能得到一个有序序列

Code

void swap(int k[],int i,int j)
{
    int temp;
    temp = k[i];
    k[i] = k[j];
    k[j] = temp;
}

void HeapAdjust(int k[],int s,int n)
{
    int i;

    for(i=2*s;i<=n;i*=2)
    {
        if(i<n && k[i]<k[i+1])
        {
            i++;
        }

        if(temp >= k[i])
        {
            break;
        }

        k[s]= k[i];
        s =  i;
    }
    k[s] = temp;
}

//数组从1开始
void HeapSort(int k[],int n)
{
    int i;

    for(i=n/2;i>0;i--)
    {
        HeapAdjust(k,i,n);
    }

    for(i=n;i>1;i--)
    {
        (k,1,i);
        HeapAdjust(k,1,i-1);
    }
}

归并排序

归并排序的基本思想

归并排序(Merge Sort)就是利用归并的思想实现的排序方法.它的原理是假设初始记录有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到ceil(n/2)个长度为2或1的有序子序列;再两两归并,......,如此重复,直到得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序.

QQ截图20170204204516.jpg

Code

/*
递归实现
*/
#define MAXSIZE 10

//实现归并并把结果存入list1
void merging(int  *list1,int list1_size,int *list2,int list2_size)
{
    int i,j,k,m;
    int temp[MAXSIZE];

    i = j = k = 0;

    while(i<list1_size && j<list2_size)
    {
        if(list1[i]<list2[j])
        {
            temp[k++] = list1[i++];
        }
        else
        {
            temp[k++] = list2[j++];
        }
    }

    while(i<list1_size)
    {
        temp[k++] = list1[i++];
    }

    while(j<list2_size)
    {
        temp[k++] = list2_size[j++];
    }

    for(m=0;m<(list1_size + list2_size);m++)
    {
        list1[m] = temp[m];
    }
}

void MergeSort(int k[],int n)
{
    if(n>1)
    {
        int *list1 = k;
        int list1_size = n/2;
        int *list2 = k+n/2;
        int list2_size = n - list1_size;

        MergeSort(list1,list1_size);
        MergeSort(list2,list2_size);

        merging(list1,list1_size,list2,list2_size);
    }

}
/*
迭代实现
*/
void MergeSort(int k[],int n)
{
    int i,left_min,left_max,right_min,right_max;

    int *temp = (int*)malloc(n*sizeof(int));

    for(i=1;i<n;i*=2)
    {
        for(left_min=0;left_min<n-i;left_min=right_max)
        {
            right_min = left_max = left_min + i;
            right_max = left_max + i;

            if(right_max > n)
            {
                right_max = n;
            }

            int next = 0;

            while(left_min < left_max && right_min < right_max)
            {
                if(k[left_min] < k[right_min])
                {
                    temp[next++] = k[left_min];
                }
                else
                {
                    temp[next++] = k[right_min];
                }
            }

            while(left_min < left_max)
            {
                k[--right_min] = k[--left_min];
            }

            while(next > 0)
            {
                k[--right_min] = temp[--next];
            }
        }
    }

}

快速排序

Code

void swap(int k[],int low,int high)
{
    int temp;

    temp = k[low];
    k[low] = k[high];
    k[high] = temp;
}

int Partition(int k[],int low,int high)
{
    int point;

    point = k[low];

    while(low<high)
    {
        while(low<high && k[high] >= point)
        {
            high--;
        }
        swap(k,low,high);

        while(low<high && k[low] <= point)
        {
            low++;
        }
        swap(k,low,high);
    }

    return low;
}

void QSort(int k[],int low,int high)
{
    int point;

    if(low < high)
    {
        point = Partition(k,low,high);

        QSort(k,low,point-1);

        QSort(k,point+1,high);
    }
}

void QuickSort(int k[],int n)
{
    QSort(k,0,n-1);
}

快速排序的优化

优化选取基准点

选取三个元素中排在中间的元素
int m = low + (high-low)/2;

if(k[low] > k[high])
{
    swap(k,low,high);
}
if(k[m] > k[high])
{
    swap(k,m,high);
}
if(k[m] > k[low])
{
    swap(k,m,low);
}

优化不必要的交换

int Partition(int k[],int low,int high)
{
    int point;

    int m = low + (high-low)/2;

    if(k[low] > k[high])
    {
        swap(k,low,high);
    }
    if(k[m] > k[high])
    {
        swap(k,m,high);
    }
    if(k[m] > k[low])
    {
        swap(k,m,low);
    }

    point = k[low];

    while(low<high)
    {
        while(low<high && k[high] >= point)
        {
            high--;
        }
        k[low] = k[high];

        while(low<high && k[low] <= point)
        {
            low++;
        }
        k[high] = k[low];
    }

    k[low] = point;
    return low;
}

优化小数组时的排序方案

  • 小规模时使用直接插入排序
#define MAX_LENGTH_INSERT_SORT 7

void ISort(int k[],int n)
{
    int i,j,temp;

    for(i=1;i<n;i++)
    {
        if(k[i] < k[i-1])
        {
            temp = k[i];

            for(j=i-1;k[j] > temp;j--)
            {
                k[j+1] = k[j];
            }

            k[j+1] = temp;
        }
    }
}

void InsertSort(int k[],int low,int high)
{
    ISort(k+low,high-low+1);
}

void swap(int k[],int low,int high)
{
    int temp;

    temp = k[low];
    k[low] = k[high];
    k[high] = temp;
}

int Partition(int k[],int low,int high)
{
    int point;

    int m = low + (high-low)/2;

    if(k[low] > k[high])
    {
        swap(k,low,high);
    }
    if(k[m] > k[high])
    {
        swap(k,m,high);
    }
    if(k[m] > k[low])
    {
        swap(k,m,low);
    }

    point = k[low];

    while(low<high)
    {
        while(low<high && k[high] >= point)
        {
            high--;
        }
        k[low] = k[high];

        while(low<high && k[low] <= point)
        {
            low++;
        }
        k[high] = k[low];
    }

    k[low] = point;
    return low;
}

void QSort(int k[],int low,int high)
{
    int point;

    if(high - low > MAX_LENGTH_INSERT_SORT)
    {
        point = Partition(k,low,high);

        QSort(k,low,point-1);

        QSort(k,point+1,high);
    }
    else
    {
        InsertSort(k,low,high);
    }
}

void QuickSort(int k[],int n)
{
    QSort(k,0,n-1);
}

优化递归操作

  • 什么是尾递归?

如果一个函数中递归形式的调用出现在函数的末尾,我们成这个递归函数是尾递归的

当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活跃记录而不是在栈中去创建一个新的.编译器可以做到这一点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了.通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,使得实际的运行效率会变得更高.因此,只要有可能我们就需要将递归函数写成尾递归的形式

void QSort(int k[],int low,int high)
{
    int point;

    if(high - low > MAX_LENGTH_INSERT_SORT)
    {
        while(low < high)
        {
            point = Partition(k,low,high);

            if(point -low < high - point)
            {
                QSort(k,low,point-1);
                low = point + 1;
            }
            else
            {
                QSort(k,point+1,high);
                high = point - 1;
            }
        }

    }
    else
    {
        InsertSort(k,low,high);
    }
} 

总结

算法分类

QQ截图20170204222017.jpg

算法特点

  • 浅色为基本算法,深色为改进算法

QQ截图20170204222125.jpg