八大排序算法总结

来源:互联网 发布:http 文件 json 上传 编辑:程序博客网 时间:2024/06/06 01:58

算法一:插入排序

将数据依次插入,对于每次新插入的数据,从末尾开始在已排好序的数列中找到其正确的位置,将其后数据从最后一个依次后移,最后插入。若待插入元素与有序序列中某元素相等,则插入在相等元素的后面,故稳定。

代码实现:

void insert_sort(intarray[], intn)

{

    int i, j, temp;

    for (i = 1; i < n; i++)   //i为当前要插入的数据索引,开始时均插在最后

    {

        temp= array[i];

        for (j = i; j > 0&& array[j - 1] > temp; j--) //将i前数据与i比较,后移空位置

            array[j] = array[j - 1];

        array[j] = temp;  //将i放置该放位置

    }

}

缺点:共需n趟排序,效率低,并且每次循环只能将数据移动一位

 

算法二:希尔排序

选择一个增量序列,按照增量序列进行k趟排序,没趟排序将整个待排序的记录序列分割成为m个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时(增量降为1),再对全体记录进行依次直接插入排序, 不稳定。

代码实现:

void shell_sort(intarray[], intn)

{

    int i, j, Increment,temp;

    for (Increment = n / 2; Increment >0; Increment /= 2)  //计算希尔增量,对应k趟排序

        for (i = Increment; i< n; i++) //i为要插入的数据索引,开始时均插在子序列最后

        {

            temp= array[i];

            for (j = i; j >=Increment &&array[j - Increment] > temp; j -=Increment)

                //将i在子序列中前面数据依次与i比较, 后移空出位置

                array[j] = array[j - Increment];

            array[j] = temp;

        }

}

*** 与直接插入比,多了一层增量循环,j的增值变为增量Increment,故时间复杂度无变化

 

算法三:选择排序

进行n-1趟排序,每次寻找未排序所有元素的最小值放在已排序序列的最末端,稳定。

代码实现:

void selection_sort(intarray[],int n)

{

    int i, j, min;

    for (i = 0; i < n - 1; i++)  //排序n-1趟,每次寻找i位置上的值

    {

        min= i;

        for (j = i + 1; j< n; j++)  //i后数据与min依次比,找应放在i位置上的索引给min

            if (array[j] <array[min])

                min= j;

        if (min != i)    //每个位置只交换一次

        {

            int temp = array[i];

            array[i] = array[min];

            array[min] = temp;

        }

    }

}

 

算法四:冒泡排序

每次比较相邻两元素,若错序则交换,多次遍历,直至无交换,实质上需要排序k-1趟,每次找到未排序元素中的最大值放在已排序序列最前面,稳定。

代码实现:

void bubble_sort(intarray[],int n)

{

    int i, j;

    for (i = 0; i < n - 1; i++)  //排序n-1趟,每次寻找i位置上的值

    {

        for (j = 0; j< n-i-1; j++) //i后数据与min依次比,找应放在i位置上的索引

            if (array[j] >array[j + 1]) //每趟可能需要交换多次

            {

                int temp = array[j];

                array[j] = array[j+1];

                array[j+1] = temp;

            }

    }

}

 

算法五:归并排序

采用分治思想,对任一序列,分成左右子列,子列内部排序,递归实现,然后用指针移动合并两子列实现归并操作,稳定。

代码实现:

void Merge(intarray[],int tempArr[], int startIndex,intmidIndex, int endIndex)

{

    int i = startIndex, j = midIndex + 1, k = startIndex;

    while (i != midIndex + 1 && j !=endIndex + 1) //直至有一子列全部排进合并列

    {

        if (array[i] >array[j])

            tempArr[k++] = array[j++];

        else

            tempArr[k++] = array[i++];

    }

    while (i != midIndex + 1)  //未排完子列剩余元素直接送人合并列

        tempArr[k++] = array[i++];

    while (j != endIndex + 1)

        tempArr[k++] = array[j++];

    for (i = startIndex; i <= endIndex; i++) //将合并列放入原序列应有的位置

        array[i] = tempArr[i];

}

void mergeSort(intarray[],int tempArr[], int startIndex,intendIndex)

{

int midIndex;

    if (startIndex <endIndex)

    {

        midIndex= (startIndex + endIndex) / 2;

        mergeSort(array, tempArr, startIndex, midIndex); //对左子列排序

        mergeSort(array, tempArr, midIndex + 1, endIndex); //对右子列排序,注意加1

        Merge(array, tempArr, startIndex, midIndex, endIndex); //归并操作合并两子列

    }

}

*** 需要提前申请临时空间tempArr[]存放合并列,分治思想降低了时间复杂度,但递归实现提高了空间复杂度


算法六:快速排序--目前最受推崇的排序算法

取序列中任一元素作为枢纽元,将其余元素与该枢纽元比较,分成大小两个集合,等于枢纽元的元素随机分配,递归地对两个集合快速排序。

根据细节不同,有不同的方法,标准算法取第一个数做枢纽元,这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。最好的方法是取左端,右端和中间位置上的三个元素的中值做枢纽元。取这3个值的好处是在实际问题中,出现近似顺序数据或逆序数据的概率较大,此时中间数据必然成为中值,而也是事实上的近似中值。万一遇到正好中间大两边小(或反之)的数据,取的值都接近最值,那么由于至少能将两部分分开,实际效率也会有2倍左右的增加,而且利于将数据略微打乱,破坏退化的结构。

代码实现:

int Median3(intarray[], int left, int right)

{

    int center = (left +right) / 2;

    if (array[left] >array[center])  //将左端,右端,中间三值排序

        swap(&array[left], &array[center]);

    if (array[left] >array[right])

        swap(&array[left], &array[right]);//三者中最小值放左端且无需再判断

    if (array[center] >array[right])

        swap(&array[center], &array[right]);//三者中最大值放右端且无需再判断

    swap(&array[center], &array[right-1]);//三者中中间值放倒数第二个做枢纽元

    return &array[right - 1];

}

#define cutoff 3

void QuickSort(intarray[], int left, int right)

{

    if (left +cutoff <= right)

    {

        int pivot = Median3(array,left, right);

        int i = left;

        int j = right - 1;

        while (1)

        {

            while (array[++i] < pivot) {}//从两侧分别找到错误分组的元素

            while (array[--j] > pivot) {}

            if (i < j)  //若i,j尚未交错

                swap(&array[i], &array[j]);

            else

                break; //当前分组正确

        }

        swap(array[i], array[right - 1]); //把枢纽元放到中间

        QuickSort(array, left, i - 1);

        QuickSort(array, i + 1, right);

    }

    else

        InsertionSort(array + left, right - left + 1);//对于小数组,插入排序更合适

}

 

*** 与归并相比,同样是递归,可是分成的两个子问题并不一定相等(隐患),但分成两组是在适当的位置进行且非常有效,故比递归更快。


未完待续。。。