数据结构算法(3)--排序

数据结构算法(3)--排序

总结并记录常见的几种排序算法.


稳定排序算法

Note:
稳定性是指当数组中有两个相同值的元素p和q ,其排序完成后q依旧在p右边。

(1)插入排序

说明: 插入排序在n较小且原始数组基本有序的情况下效果最佳。

//插入排序,稳定排序
void InsertSort(int a[], int n)
{
    int temp;
    for (int i = 1; i < n; ++i)
    {
        int j = i - 1;
        temp = a[i];
        while (j >= 0 && temp < a[j])//逆序
        {
            a[j + 1] = a[j];//后移
            --j;
        }
        a[j + 1] = temp;
    }
}
(2)冒泡排序

优化:在比较的过程中如果没有发现交换情况则提前终止

void BuddleSort(int a[], int n)
{
    bool NoSwap;
    int i, j;
    for (i = 0; i < n - 1; ++i)
    {
        NoSwap = true;
        for (j = n - 1; j > i; --j)
        {
            if (a[j] < a[j - 1])
            {
                swap(a, j, j - 1);
                NoSwap = false;
            }
        }
        if (NoSwap)
            return;
    }
}
(3)归并排序

说明:
基于分治思想的排序算法,其基本思想为:

  • 将序列划分为两个子序列;
  • 分别对每个子序列进行归并排序;
  • 有序子序列合并
    其算法框架为:
void MergeSort(int a[], int tempa[], int left, int right)
{
    int middle;
    if (left < right)
    {
        middle = (left + right) / 2;
        MergeSort(a, tempa, left, middle);//左子序列
        MergeSort(a, tempa, middle + 1, right);//右子序列
        Merge(a, tempa, left, right, middle);
    }
}

归并算法为:

//从左往右扫描两个有序子序列,将其归并到新数组
void Merge(int a[], int tempa[], int left, int right, int middle)
{
    int i, j, index1, index2;
    for (j = left; j <= right; ++j)//暂存
        tempa[j] = a[j];
    index1 = left;//L子序列起点
    index2 = middle + 1;//R子序列起点
    i = left;//从左开始归并
    while (index1 <= middle && index2 <= right)
    {
        if (tempa[index1] <= tempa[index2])
            a[i++] = tempa[index1++];
        else
            a[i++] = tempa[index2++];
    }
    while (index1 <= middle)//整理余下的L序列
        a[i++] = tempa[index1++];
    while (index2 <= right)//整理余下的R序列
        a[i++] = tempa[index2++];
}

对归并排序的优化

由于上面的Merge函数中存在大量的比较操作从而使得代码效率较低,为了解决这一问题,故采用以下优化方式:

  • 对基本有序的序列直接进行插入排序;
  • 采用R.Sedgewick优化:归并时从两端开始处理,向中间推进。具体做法为:将R子序列进行倒置后与L序列进行归并。
    代码如下:
void Merge(int a[], int tempa[], int left, int right, int middle)
{
    int i, j,k, index1, index2;
    for(i = left;i<=middle;++i)
        tempa[i] = a[i];
    for (j = 1; j <= right-middle; ++j)//逆序存储R序列
        tempa[right-j+1] = a[j+middle];
    for(index1 = left,index2 = right,k=left;k<=right;++k)
    {
        if(tempa[index1]<=tempa[index2])
            a[k]=tempa[index1++];
        else
            a[k]=tempa[index2--];
    }
    
}
(4)桶排序

其主要特点为:

  • 非比较排序:将具有相同值的记录分配于同一桶内,然后依次按照编号从桶中取出;
  • 需要事先直到序列中的记录都位于某个小区间[0,m)内;
  • 适用于m(元素值范围)较小的情况。
    其中代码实现为:
//桶排序
void BucketSort(int a[], int n, int max)
{
    int *tempa = new int[max], *count = new int[max];
    int i;
    for (i = 0; i < n; ++i)
        tempa[i] = a[i];
    for (i = 0; i < n; ++i)
        count[i] = 0;
    for (i = 0; i < n; ++i)//统计每个值出现次数
        count[a[i]]++;
    for (i = 0; i < max; ++i)//统计前i桶总元素个数
        count[i] = count[i - 1] + count[i];
    for (i = n - 1; i >= 0; --i)
        a[--count[tempa[i]]] = tempa[i];
    delete[] tempa;
    delete[] count;
}

非稳定排序算法

(1)shell排序(以shell(2)为例)

说明:shell为插入排序的变形,其基本思想为:

  • 先将序列转化为若干小序列,在这些小序列内进行插入排序;
  • 逐渐增加小序列的规模,同时减少小序列个数。使之更有序;
  • 最后对整个序列进行排序:扫尾直接进行插入排序。

但shell(2)的选取的增量(2的指数)间不互质,导致重复排序。常见的解决办法是使用shell(3)或Hibbard增量排序。
Hibbard增量:{2^k-1,2^(k-1)-1,...,7,3,1}

void ModInsSort(int array[], int n, int delta)
{
    int i, j;
    for (i = delta; i < n; i += delta)
        for (j = i; j >= delta; j -= delta)//已delta为步长向前寻找逆置位,对其进行调整
        {
            if (array[i] < array[j - delta])
                swap(array, j, j - delta);
            else
                break;
        }
}

//shell(2)排序
void shellSort(int a[], int n)
{
    int i, delta;
    for (delta = n / 2; delta > 0; delta /= 2)
        for (i = 0; i < delta; ++i)
            ModInsSort(&a[i], n - i, delta);
    ModInsSort(a, n, 1);//如果不能保证最后一个delta的间距为1,可加入
}
(2)直接选择排序

没什么需要说的,哈哈

void SelectSort(int a[], int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        int min = i;
        for (int j = i + 1; j < n; ++j)
            if (a[j] < a[min])
                min = j;
        swap(a, i, min);
    }
}
(3)快速排序

说明:
基于分治思想的排序算法,其基本思想为:

  • 选择轴值(pivot):
  • 将序列划分为两个子序列L和R,使得L中所有记录小于等于轴值,R中的所有记录都大于轴值;
  • 对L和R递归进行快速排序(分治)。

轴值选择:尽可能使得L和R长度相等。

int SelectPivot(int left, int right)
{
    return (left + right) / 2;//中间记录作为轴值
}

快速排序算法框架:

void QuickSort(int a[], int left, int right)
{
    if (right <= left)
        return;
    int pivot = SelectPivot(left, right);//选择轴值
    swap(a, pivot, right);//将轴值放在序列末端
    pivot = Partition(a, left, right);//序列整理
    QuickSort(a, left, pivot - 1);//对L进行递归排序
    QuickSort(a, pivot + 1, right);//对R进行递归排序
}

一次序列整理过程:

  • 选择轴值并存储;
  • 将序列最后一个元素放在轴值位置(swap(a,pivot,right));
  • 初始化下标i,j使其分别指向头和尾;
  • i递增直到遇到比轴值大的元素,将此元素放到j位置,之后j递减直到遇到比轴值小的元素,将该元素放到i的位置;
  • 重复上步,直到i==j,最后将轴值放到i的位置。

其代码如下:

int Partition(int a[], int left, int right)
{
    int l = left, r = right;
    int temp = a[right];//存储轴值
    while (l != r)
    {
        while (a[l] <= temp && l < r)//右移
            ++l;
        if (l < r)
        {
            a[r] = a[l];
            --r;
        }
        while (a[r] >= temp && l < r)//左移
            --r;
        if (l < r)
        {
            a[l] = a[r];
            ++l;
        }
    }
    a[l] = temp;//轴值填到l位置
    return l;//返回pivot位置
}

快排的可能优化为:

  • 将left和right的间距小于阈值(28?)时,改用简单排序算法代替;
  • 用栈来减少递归操作;
(4)堆排序

堆排序是一种不稳定排序,其时间复杂度为O(nlogn)。

转载于:https://www.cnblogs.com/willingtosmile/p/10587504.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值