排序算法:实现,平均复杂度,最好(坏)情况复杂度,稳定性

1:冒泡算法
      1.1 实现

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

      1.2 平均复杂度
                 n轮循环,每一轮循平均的复杂度为n/2,故总的复杂度为:O(n * n)
      1.3 最好情况
               当输入序列已经排序好的时候,一轮冒泡下来没有交换过位置,直接退出程序,复杂度为:O(n)
      1.4 最坏情况
               当输入序列是你逆序的时候,每一次比较都要进行交换,复杂度为:O(n*n)
      1.5 稳定性
               a = b的时候,由于只有大于才做交换,故a,b的位置没有机会交换,所以,冒泡排序是稳定的

2:插入排序
      2.1 实现

void insertSort(int *a, int n)
// 该算法是最初始的实现,顺序比较前面元素,效率较低
{
            int tmp;
            int i, j, k;
            for(i = 0; i < n; ++i)
            {
                        tmp = a[i];
                        for(j = 0; j < i; ++j)
                        {
                                    if(tmp < a[j])
                                    {
                                                for(k = i; k > j; --k)
                                                {
                                                            a[k] = a[k - 1];
                                                }
                                                a[j] = tmp;
                                                break;
                                    }
                        }
            }
}

void simpleInsertSort(int *a, int n)
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)
// 该算法插入时,逆序比较前面一排序元素,效率较高
{
            int tmp;
            int i, j;
            for(i = 1; i < n; ++i)
            {
                        for(j = i, tmp = a[i]; a[j - 1] > tmp && j > 0; --j)
                        {
                                    a[j] = a[j - 1];
                        }
                        a[j] = tmp;
            }
}

      2.2 平均复杂度
               插入的次数为n,而每次插入操作需要移动的数据平均复杂度为O(n),故总的平均复杂度为:O(n*n)
      2.3 最好情况
               当输入的序列已经有序的时候,只需要进行比较,不用移动数据,故其复杂度仅仅为O(n)
      2.4 最坏情况
               当输入的序列为逆序的时候,每次插入都要移动之前所有元素,故其复杂度为O(n*n)
      2.5 稳定性
               如果a=b,则当a在前面时候,其必然比b先确定,而后面b再插入时,必然在a后面,故该算法稳定

3:shell排序
      3.1 实现
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)

void shellSort(int *a, int n)
// 该程序使用的增量为shell增量,最坏情况为O(n*n)
// 更好的效果可以选择Hibbard增量:2^k - 1
{
            int tmp;
            int gap, i, j;
            for(gap = n / 2; gap > 0; gap /= 2)
            {
                        for(i = gap; i < n; ++i)
                        {
                                    tmp = a[i];
                                    j = i;
                                    for(; j >= gap && tmp < a[j - gap]; j -= gap)
                                                a[j] = a[j - gap];
                                    a[j] = tmp;
                        }
            }
}
      3.2 平均复杂度
               由于使用的增量(程序中的gap)不同,导致的复杂度也不同,常用的有Hibbard增量,基于模拟的结果被认为是O(n^(5/4))
      3.3 最好情况
               当输入序列已经有序的时候,每一次增量向前比较都立即不满足条件而退出,故只有前面两层循环起到了作用,复杂度为O(nlogn)
      3.4 最坏情况
               使用Hibbard增量的时候,该排序算法的最坏情况复杂度为O(n^(3/2))(已经被证明)
      3.5 稳定性
               由于不同的gap间隔对应的数据是独自比较的,所以,如果a=b但是不在同一个gap间隔上,显然就会出现前后颠倒的情况,即是说,该算法不稳定

4:堆排序
      4.1 实现
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)

void percDown(int *, int , int );
void heapSort(int *a, int n)
{
            int i, j;
            // build heap
            for(i = n / 2; i >= 0; i--)
                        percDown(a, i, n);

            // delete Max
            for(j = n - 1; j > 0; j--)
            {
                        swap(a[0], a[j]);
                        percDown(a, 0, j);
            }
}


inline int leftChild(int i)
{
            return 2 * i;
}

void percDown(int *a, int i, int n)
{
            int child;
            int tmp;
            for(tmp = a[i]; leftChild(i) < n; i = child)
            {
                        child = leftChild(i);
                        if(child != n - 1 && a[child] < a[child + 1])
                                    child++;
                        if(tmp < a[child])
                                    a[i] = a[child];
                        else
                                    break;
            }
            a[i] = tmp;
}
      4.2 平均复杂度
               排序的过程主要取决于后期的调整(前期的建立堆的过程只需要O(logn * logn)),其复杂度为O(nlongn)
      4.3 最好情况
               对于堆排序,无论输入的序列是否有序或者其他的条件,建立和调整堆的复杂度都不会受到影响,故最好情况的复杂度为O(nlogn)
      4.4 最坏情况
          对于堆排序,无论输入的序列是否有序或者其他的条件,建立和调整堆的复杂度都不会受到影响,故最坏情况的复杂度为O(nlogn)
      4.5 稳定性
               不稳定,暂时没有想到好的说明例子,但是如果画出其二叉树的表示图,就可以直观地看出,a=b时,如果b后于a的插入,其将在a的后面,但是经过percDown调整后,b有可能被调到比a更高的层次(也就是a的前面了),所以,顺序可能存在调换情况。

5:归并排序
      5.1 实现
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)

void merge(int *, int *, int , int , int );
void mergeSort(int *, int *, int , int );
void simpleMergeSort(int *a, int n)
{
            int *b = new int[n];
            mergeSort(a, b, 0, n - 1);
            delete b;
}

void mergeSort(int *a, int *tmp, int left, int right)
{
            if(left < right)
            {
                        int center = (left + right) / 2;
                        mergeSort(a, tmp, left, center);
                        mergeSort(a, tmp, center + 1, right);
                        merge(a, tmp, left, center + 1, right);
            }
}
void merge(int *a, int *tmp, int leftPos, int rightPos, int rightEnd)
{
            int leftEnd = rightPos - 1;
            int tmpPos = leftPos;
            int numElements = rightEnd - leftPos + 1;

            while(leftPos <= leftEnd && rightPos <= rightEnd)
            {
                        if(a[leftPos] < a[rightPos])
                                    tmp[tmpPos++] = a[leftPos++];
                        else
                                    tmp[tmpPos++] = a[rightPos++];
            }

            while(leftPos <= leftEnd)
                        tmp[tmpPos++] = a[leftPos++];

            while(rightPos <= rightEnd)
                        tmp[tmpPos++] = a[rightPos++];

            for(int i = 0; i < numElements; i++, rightEnd--)
                        a[rightEnd] = tmp[rightEnd];
}

      5.2 平均复杂度
               对于各种输入序列,归并排序的处理过程都是一样的,T(1) = 1, T(N) = T(N/2) + N,不难求得最后的复杂度T(N)为O(NlogN)
      5.3 最好情况
               与平均情况一致
      5.4 最坏情况
               与平均情况一致
      5.5 稳定性
               由于没有发生数据交换,所有当a=b的时候,a一开始如果在b前面,则其每一次合并后仍然在b前面,故该排序算法是稳定的

6:快速排序
      6.1 实现
PS: 以下代码引自《数据结构与算法分析:C++描述》(第3版)

int median3(int *, int , int);
void quickSort(int *, int , int);
void simpleQuickSort(int *a, int n)
{
            quickSort(a, 0, n - 1);
}

void quickSort(int *a, int left, int right)
{
// if number of elements less than 10, used insertSort
            if(left + 10 > right)
                        insertSort(a, right - left + 1);
            else
            {
                        int pivot = median3(a, left, right);
                        int i = left, j = right;
                        for(;;)
                        {
                                    while(a[++i] < pivot);
                                    while(a[--j] > pivot);
                                    if(i < j)
                                                swap(a[i], a[j]);
                                    else
                                                break;
                        }
                        // reset pivot
                        swap(a[i], a[right - 1]);
                        quickSort(a, left, i - 1);
                        quickSort(a, i + 1, right);
            }
}

// find out median of three elements and set pivot
int median3(int *a, int left, int right)
{
            int center = (left + right) / 2;
            if(a[center] < a[left])
                        swap(a[left], a[center]);
            if(a[right] > a[left])
                        swap(a[left], a[right]);
            if(a[right] < a[center])
                        swap(a[center], a[right]);

            // place pivot at position right - 1
            swap(a[center], a[right - 1]);
            return a[right - 1];
}

      6.2 平均复杂度
                 平均复杂度为O(NlogN)
      6.3 最好情况
                 每次都刚好选到了一个区间段的中位数,相当于归并排序的过程,复杂度为O(NlongN)
      6.4 最坏情况
                 每次都刚好选到了最小的元素作为主元,导致极度的不平衡,退化为插入排序的最坏情况了,复杂度为O(N^2)
      6.5 稳定性
                 当a=b>pivot且a在b前面的时候,由于从后面开始遍历,故b会先于a被替换到pivot的前面,这样,b就变成了在a的前面,也就是说,ab位置对调,故该排序算法不稳定
7:直接选择排序
      7.1 实现

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

      7.2 平均复杂度
               每次循环需要访问n-i个元素得到最小值,故其总的比较次数为(N+1)*N/2,故复杂度为O(N^2)
      7.3 最好情况
               与平均情况一致
      7.4 最坏情况
               与平均情况一致
      7.5 稳定性
               假设a=b,并且a在b的前面,而在某轮循环中最小值在b的后面,而次最小值需要跟a交换,那么该轮过后,b就在a前面了,所以,该排序算法不稳定
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值