排序算法归纳

排序算法分类:

1、基于比较的排序算法

         (1)交换排序

                冒泡排序、快速排序

         (2)插入排序

                直接插入排序、二分插入排序、Shell排序

         (3)选择排序

                简单选择排序、堆排序

         (4)合并排序

2、基于数字和地址计算的排序方法

        计数排序、桶排序、基数排序

冒泡排序 O(n2)

第一个数和第二个数进行比较,若逆序则交换,然后比较第二个和第三个....直至n-1个数和第n个数比较使最大的安放在最后一个位置(第一趟)。接着对n-1个数(从第一个到n-1,第n大的数已在第一轮被确定)进行第二趟冒泡排序,使最大的安放在第n-1个记录位置。重复上述过程排剩下n-2个数。

 时间复杂度:

        正序情况:

                比较次数: \sum_{i=1}^{n-1}(n-i) = \frac{1}2{\left ( n^{2}-n \right )} 

                交换次数:0

        逆序情况:

                比较次数:  \sum_{i=1}^{n-1}(n-i) = \frac{1}2{\left ( n^{2}-n \right )}         

                交换次数: \sum_{i=1}^{n-1}(n-i) = \frac{1}2{\left ( n^{2}-n \right )}

 代码如下: 

void bubble_modified(int *a, int n)
{
    for(int i = 0; i < n - 1; i++) // n-1趟排序
    for(int j = 0; j < n - i - 1; j++)
    {
        if(a[j + 1] < a[j]) swap(a[j], a[j + 1]);
    }
}

优化版冒泡排序O(n2)

思路:当某一趟遍历时,没有发生任何数的交换,说明此时数组已经有序,排序可结束。

           例如: 4  3  1  2  3

                        第一次执行后数组为  3 1 2 3 4

                        第二次执行后数组为  1 2 3 3 4 

                        第三轮执行后数组为  1 2 3 3 4

                        循环不用执行n-1次,当执行第三次时,发现没有任何数进行了交换(数组已有                                  

                        序)则可停止。

             复杂度仍为O(n2) 但该做法在正序情况下可减少很多时间。

 时间复杂度:

        正序情况:

                比较次数: n-1 

                交换次数:0

        逆序情况:

                比较次数:  \sum_{i=1}^{n-1}(n-i) = \frac{1}2{\left ( n^{2}-n \right )}         

                交换次数: \sum_{i=1}^{n-1}(n-i) = \frac{1}2{\left ( n^{2}-n \right )} 

void Bubble_Modified(int *a, int n)
{
    int flag = 1;
    for(int i = 0; i < n - 1 && flag; i++)
    {
        flag = 0; //每一次循环需要重新置0
        for(int j = 0; j < n - i - 1; j++)
        {
            if(a[j] > a[j + 1])
            {
                flag = 1;//需要再进行下一轮
                swap(a[j], a[j + 1]);
            }
        }
    }
}

插入排序O(n2)

思想:将第1个记录看成有序子列,然后从第2个记录开始,逐个插入,直至整个序列有序。

            整个过程为n-1趟插入操作。

时间复杂度:

         正序情况:

                比较次数:n-1

                交换次数:0

        逆序情况:

                比较次数:   \sum_{i=1}^{n-1}i=\frac{n*(n-1)}{2}        

                交换次数:  \sum_{i=1}^{n-1}i=\frac{n*(n-1)}{2}

 代码如下:

void insertion(int *a, int n)
{
    for(int i = 1; i < n; i++)
    {
        int tmp = a[i];
        int j = i - 1;
        while(tmp < a[j] && j >= 0)
        {
            a[j + 1] = a[j];
            j--;
        }
        a[j + 1] = tmp;
    }
}

二分插入排序

基本思想:用二分查找确定插入位置

 时间复杂度:

正序

void Binary_InsertionSort(int *a, int n)
{
    int mid, l, r;
    for(int i = 1; i < n; i++)
    {
        l = 0, r = i - 1;
        mid = r + l >> 1;
        int x = a[i];
        while(l <= r)
        {
            mid = l + r >> 1;
            if(x < a[mid]) r = mid - 1;
            else l = mid + 1;
        }
        for(int k = i - 1; k >= l; k--)
        a[k + 1] = a[k];
        a[l] = x;
    }
}

希尔排序(缩小增量法)

思想:先取一个正整数d1<n,把所有相隔d1的数放一组,组内进行直接插入排序

           然后取d2<d1,重复上述分组和排序操作

           直至di=1,即所有记录放进一个组中排序为止。

        (为了区间不重叠,d1、d2....di应该互素,例如若d1取4,而d2取2,重复率太高)

提高速度原因:分组后n值减小,n2更小,而T(n) = O(n2),所以T(n)总体上是减小了。

                           在进行最后一趟增量为1的插入排序时,序列已基本有序

 时间复杂度约为O(n1.3)

选择排序

思想:通过n-1次比较,从n个记录中找出最小的数,将它与第一个数做交换。

           再通过n-2次比较,从剩余n-1个记录中找出关键词次小的数,与第二个数做交换        

           重复上述操作,共进行n-1趟排序后,排序结束。

void selection(int *a, int n)
{
    for(int i = 0; i < n - 1; i++)
    for(int j = i; j < n - 1; j++)
    {
        if(a[j + 1] < a[j]) swap(a[j], a[j + 1]);
    }
}

        

void selection_standard(int *a, int n)
{
    for(int i = 0; i < n - 1; i++)
    {
        int j, k;
        for(k = i, j = i + 1; j < n; j++)
        if(a[k] > a[j])  k = j;
        
        if(k != i) swap(a[i], a[k]);
    }
}

选择算法改进版

int Max(int *a, int n, int &m)
{
    // 确定a[0:n-1]中最大元素的下标
    int pos = 0;
    for(int i = 1; i < n; i++)
    if(a[pos] < a[i])
    {
        pos = i;
        m ++;
    }
    return pos;
}
void Selection_Modified(int *a, int n)
{
    for(int size = n; size > 1; size --)
    {
        int m = 0;
        int j = Max(a, size, m);
        swap(a[j], a[size - 1]);
        if(m == size - 1) break;
    }
}

递归版的合并排序算法

思想:平衡的二分分治

          将待排序元素序列简单德分成大小大致相等的左右两段,对这两段子序列递归地进行合并排序,然后利用两段子序列已得到的有序性,将它们有序地合并到一个工作区,最后用工作区中排好序的全序列更新原待排序的元素序列。

void merge_sort(int *a, int l, int r)
{
    if(l >= r) return;
    int mid = r + l >> 1;
    
    merge_sort(a, l, mid), merge_sort(a, mid + 1, r);
    
    int i = l, j = mid + 1, k = 0;
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j]) tmp[k++] = a[i++];
        else tmp[k++] = a[j++];
    }
    while(i <= mid) tmp[k++] = a[i++];
    while(j <= r) tmp[k++] = a[j++];
    
    for(int i = l, j = 0; i <= r; i++, j++) a[i] = tmp[j];
}

合并排序算法非递归版

思想:将数组a中相邻元素两两配对,用Merge算法将它们合并,得到n/2个长度为2的排好序的数组段,然后再将它们Merge成长度为4的排好序的数组段....如此下去,直至排好整个数组。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值