排序总结

来源:互联网 发布:c语言大小写字母互换 编辑:程序博客网 时间:2024/06/10 05:54

1. 直接插入排序

//直接插入排序void insert_sort(int a[], int n){    int i, j, temp;    for (i = 1; i < n; i++)    {        //暂存下标为i的数        temp = a[i];        //从后往前找要插入的位置        for (j = i - 1; j >= 0 && a[j]>temp; j--) {             a[j + 1] = a[j];        }        a[j + 1] = temp;    }}

时间复杂度,在最好的情况下是:O(n),最坏情况下是O(n2),平均时间复杂度是O(n2)
空间复杂度:O(1)
它是稳定排序(相等元素的相对次序在排序后不会发生改变)

2. 希尔排序

eg:
这里写图片描述

void shell_sort(int a[],int n){    int h, i, j, temp;//h是增量    for (h = n / 2; h > 0; h = h / 2)    {    //下面是一个插入排序,0~h-1时在各自组的第一位,认为是最好的排序        for (i = h; i < n; i++)        {            temp = a[i];            for (j = i - h; j >= 0 && a[j] > temp; j -= h)                a[j + h] = a[j];            a[j + h] = temp;        }    }}

希尔排序的实质是分组插入排序,由于相等的元素可能分在不同的组,导致他们的相对次序发生改变,所以希尔排序不稳定
希尔排序优于直接插入排序,因为:
(1)当增量h较大时,一组内的元素个数n较小,此时用直接插入排序,O(n)O(n2)相差不大。
(2)当增量h减小,每组元素的个数不断增多,但经过以前的处理,数组较接近有序状态,当数组初始状态基本有序时,直接插入排序所需的比较和移动次数都较小。
一般情况下,时间复杂度优于O(n2)
空间复杂度:O(1)

3. 冒泡排序

void bubble_sort(int a[], int n){    int i, j, temp, exchange = 0;    for (i = 0; i < n; i++) {        exchange = 0;        for (j = n-1;j>i; j--)//从后往前交换,使得最轻的气泡在最前面        {            if (a[j] < a[j - 1])            {                temp = a[j];                a[j] = a[j - 1];                a[j - 1] = temp;                exchange = 1;            }        }        //如果此趟扫描没有交换,说明已经是有序的了        if (exchange != 1)            return;    }}

冒泡排序是稳定排序
时间复杂度,在最好的情况下是:O(n),最坏情况下是O(n2),平均时间复杂度是O(n2)
空间复杂度:O(1)

4. 快速排序

思想:分治法(Divide-and-Conquer),划分交换排序
首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面。
eg:
这里写图片描述

void quick_sort(int a[], int low, int high){    int i, j, pivot;    if (low < high)    {        pivot = a[low];        i = low;        j = high;        while (i < j)        {            //从后往前搜索(j--),找到第一个小于pivot的a[j],交换a[i]与a[j];            while (i < j&&a[j] >= pivot)                j--;            if (i < j)                a[i++] = a[j];//将比pivot小的元素移到低端            //从前往后找(i++),找到第一个大于pivot的a[i],交换a[i]与a[j]            while (i < j && a[i] <= pivot)                i++;            if (i < j)                a[j--] = a[i];//将比pivot大的元素移到高端        }        a[i] = pivot;        quick_sort(a, low, i - 1);        quick_sort(a, i + 1, high);    }}

快速排序在最坏情况下的时间复杂度:O(n2),平均时间复杂度:O(nlgn)
空间复杂度:O(1)
快速排序是不稳定的,因为相等元素在分组交换时不能保证相对位置不变。

5. 直接选择排序

思想:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,….,第i次从R[i-1]~R[n-1]中选取最小值,与R[i-1]交换,…..,第n-1次从R[n-2]~R[n-1]中选取最小值,与R[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列·

void select_sort(int a[], int n){    int i, j, x, l;    for (i = 0; i < n - 1; i++)    {        x = a[i];        l = i;        for (j = i; j < n; j++)        {            if (a[j] < x)            {                x = a[j];                l = j;            }        }        a[l] = a[i];//将最小元素与a[i]交换        //这种不相邻元素的互换,所以不是稳定的排序        a[i] = x;    }  }

直接选择排序不是一种稳定的排序
平均时间复杂度:O(n2),空间复杂度:O(1)

6. 堆排序

思想:
(1)先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区;
(2)再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key;
(3)由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
eg:
这里写图片描述

//以下开始是堆排序int heap_size = 0;int Left(int idx) { return ((idx<<1)+1); }int Right(int idx) { return ((idx<<1)+2); }//a[idx]与其左右子树进行递归对比,用最大值替换a[idx]void maxHeapify(int a[], int idx){    int largest = 0;    int left = Left(idx), right = Right(idx);    if ((left <= heap_size) && (a[left] > a[idx]))        largest = left;    else largest = idx;    if ((right <= heap_size) && (a[right] > a[largest]))        largest = right;    //此时largest为堆顶、左子节点、右子节点中的最大者    if (largest != idx)    {        swap(a[largest], a[idx]);        maxHeapify(a, largest);    }}//初始化堆,将数组中的每一个元素放到适当的位置//完成之后,堆顶的元素为数组的最大值void buildMaxHeap(int a[], int n){    int i=0;    heap_size = n ; //堆大小为数组长度    for (i = (n >> 1); i >= 0; i--)        maxHeapify(a, i);}void heap_sort(int a[], int n){    int i=0;    //初始化堆    buildMaxHeap( a, (n-1));//O(n)    for (i = (n - 1); i > 0; i--)    {        //堆顶元素a[0](数组的最大值)被置换到数组的尾部a[i]        swap(a[0], a[i]);        heap_size--;//从堆中移除该元素        maxHeapify(a, 0);//重建堆,O(lgn)    }}

堆排序不稳定
时间复杂度:建最大堆过程buildMaxHeap是O(n),维持堆的性质maxHeapify是O(lgn),堆排序的时间复杂度:O(nlgn)。空间复杂度: O(1)

7. 归并排序

两路归并思想:将两个已排好序的子文件合并。
自顶向下的算法设计:分解->求解->合并。

//下面是归并排序void merge(int a[], int temp[], int lpos, int rpos, int rend){    int i, lend, numEles, tmpPos;    lend = rpos - 1;    tmpPos = lpos;    numEles = rend - lpos + 1;    while (lpos <= lend && rpos <= rend)    {        if (a[lpos] < a[rpos])            temp[tmpPos++] = a[lpos++];        else            temp[tmpPos++] = a[rpos++];    }    while (lpos <= lend)        temp[tmpPos++] = a[lpos++];    while (rpos <= rend)        temp[tmpPos++] = a[rpos++];    //把临时数组拷贝到原始数组    for (i = 0; i < numEles; i++, rend--)        a[rend] = temp[rend];}void msort(int a[], int tmp[], int low, int high){    if (low >= high) return;    int mid = (low + high) / 2;    msort(a, tmp, low, mid);    msort(a, tmp, mid + 1, high);    merge(a, tmp, low, mid + 1, high);}void merge_sort(int a[], int n){    int *tmp = NULL;    tmp = new int[n];    if (tmp != NULL) {        msort(a, tmp, 0, n - 1);        delete[]tmp;    }}

**最好、最坏和平均时间复杂度:O(nlgn),空间复杂度:O(n)
归并排序是稳定的**。

8. 基数排序

桶排序:设置若干箱子,依次扫描,按关键字等于k装入第k个箱子,然后依次按序号将各非空的箱子首尾连接起来。
有最低位优先(LSD)和最高位优先(MSD )。LSD:从低位到高位依次对数据进行箱排序。在第i趟排序中,所需的箱子数就是基数。

以下是对正整数的排序:

//下面是基数排序int find_max(int a[], int n){    int max = a[0];    for (int i = 1; i < n; i++)        if (max < a[i])            max = a[i];    return max;}int digit_num(int num){    int digit = 0;    do {        num /= 10;        digit++;    } while (num != 0);        return digit;}int kth_digit(int num, int k)//第k位的数字{    num /= pow(10, k);    return num % 10;}void radix_sort(int a[], int n){    int *temp[10];//指针数组,每一个指针表示一个箱子    int count[10] = { 0,0,0,0,0,0,0,0,0,0 };    int max = find_max(a, n);    int maxDigit = digit_num(max);    int i, j, k;    for (i = 0; i < 10; i++)    {        temp[i] = new int[n];        memset(temp[i], 0, sizeof(int) * n);    }    for (i = 0; i < maxDigit; i++)    {        memset(count, 0, sizeof(int) * 10);        for (j = 0; j < n; j++)        {            int xx = kth_digit(a[j], i);            temp[xx][count[xx]] = a[j];            count[xx]++;        }        int index = 0;        //把数据从暂存数组中返回到原始数组        for (j = 0; j < 10; j++)            for (k = 0; k < count[j]; k++)                a[index++] = temp[j][k];    }}

基数排序是稳定的。
时间复杂度:假设有n个元素,每个元素有m个关键字(即这里的最大位数maxDigit),此时为O(nm).空间复杂度: O(n)


9. 小结

方法 平均时间复杂度 空间复杂度 稳定性 直接插入排序 O(n2) O(1) 稳定 希尔排序 一般优于O(n2) O(1) 不稳定 冒泡排序 O(n2) O(1) 稳定 快速排序 O(nlgn) O(1) 不稳定 直接选择排序 O(n2) O(1) 不稳定 堆排序 O(nlgn) O(1) 不稳定 归并排序 O(nlgn) O(n) 稳定 基数排序 O(nm) O(n) 稳定

参考:
希尔排序_百度百科
冒泡排序_百度百科
快速排序_百度百科
直接选择排序_百度百科
堆排序_百度百科
归并排序_百度百科
基数排序_百度百科

0 0
原创粉丝点击