排序算法合集

排序算法有很多中,我在这里只能粗略地介绍其中的6种,有什么不当之处还望读者多多指教。
为了方便起见,在这里假设排序为升序排列,用C++实现。

  • 选择排序

    选择排序是较为简单的一种排序方式,特点是在当前所指的元素前找比当前元素小的元素,如果找到就交换位置,是一种不稳定的排序方式,平均时间复杂度为O(n^2)。
    具体实现:

for (int i = 1; i < n; i++) {//设定list[i]为被指向的数
        for (int j = 0; j < i; j++) {//查找到被指向的元素为止
            if (list[i] < list[j])//若后面的数比前面小,则交换位置
                Swap(list[i], list[j]);
        }
}
  • 冒泡排序

    冒泡排序也是一种较为简单的排序方式,特点是用每次用相邻两个元素比较,大的放在后面,然后右移指针。每一次可以选出一个最大的放在最后。这是一种稳定的排序方式,平均时间复杂度为O(n^2)。
    优化思路:如果检测到序列已经排好序了,就不需要再次进行循环了,而是可以直接跳出。可以通过增加一个bool值来描述是否已经排好序。
    但是,就算已经排好序,也需要经过一轮检测来得知是排好序的,所以无论如何都会经过一次遍历。
    具体实现:

bool sorted = true;//先假设是排好序的
for (int i = n-1; i >=0 ; i--) {//要排序的元素数递减
    for (int j = 0; j < i; j++) {
        if (list[j] > list[j + 1]) {
            //当一趟里前面的元素比后面的元素大时,交换
            Swap(list[j], list[j + 1]);
            sorted = false;//这时认为没有排好序
        }
    }
    if (sorted) break;//如果已经排好序,跳出
}
  • 插入排序

    插入排序的特点是每次取一个元素插入之前已经排好序的序列中,如果前面的元素比较大,就后移一位,直到找到比要插入的元素小的元素或找到数组0号元素为止。第一次插入时将第一个元素自身视为有序序列。这是一种稳定的排序方式,平均时间复杂度为O(n^2)。
    具体实现:

for (int i = 1; i < n; i++) {
    int temp = a[i];//将当前元素存到临时元素中
    int ti = i;//将当前位置也保存到临时位置
    while (ti > 0 && a[ti - 1] > temp)
        a[ti] = a[ti-- - 1];//当数组中元素比要插入的元素大时,后移一位
    a[ti] = temp;//在满足条件的位置插入临时元素
}
  • 基数排序

    基数排序的特点是设置一个基数(假设为10),然后创建10个桶,每个桶最多有n(元素总个数)个元素,然后通过取模来按照余数的大小进行排序,先对个位排序,并把排序后的元素放回原数组中,再对十位排序……直到所有位都排序结束后,原数组就变为了有序数组。
    基数排序
    这种排序方式是稳定的,平均时间复杂度是O(d(r+n)),其中r代表基数,d代表长度(共有几位),n代表元素个数。
    具体实现:

    T* bins[10];//10个桶
    int p[10] = { 0 };//每个桶中放入了几个数
    bool next = true;//是否要进行下一轮
    int digit = 1;//每轮的位数
    int remainder = 0;//每次的余数
    for (int i = 0; i < 10; i++) {//初始化
        bins[i] = new T[n];
    }
    do {
        for (int i = 0; i < n; i++) {//把元素放入桶中
            remainder = list[i] % (digit*10) / digit;
            //当还有任意元素有余数时,循环要继续
            if (remainder) next = true;
            else next = false;
            bins[remainder][p[remainder]++] = list[i];
            //将元素放到余数桶的最后一个数的后面
        }
        int k = 0;//放置元素的位置
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < p[i]; j++) {
                //把每个桶中的元素重新装到list中
                list[k++] = bins[i][j];             
            }
        }
        for (int i = 0; i < 10; i++) //重置桶中放入的元素数
            p[i] = 0;
        digit *= 10;
    } while (next);
    for (int i = 0; i < 10; i++) {//释放空间
        delete [] bins[i];
    }
  • 归并排序
    归并排序是一种较为复杂的排序方法,特点是分而治之,将原数组先分成一个个小数组,再对每个小数组进行归并,形成一个排好序的大数组,再下一步归并,直到原数组整个被排好序为止。这是一种稳定的排序方式,时间复杂度为O(nlogn),需要一个存放临时数据的数组。我以前写过递归实现的伪代码,这里就再写一个非递归实现的代码吧。
    具体实现:

template<class T>
void Merge(T* c, T* d, int l, int m, int r)
{
    int i = l,      //第一个块的指针
        j = m + 1,  //第二个块的指针
        k = l;      //结果的指针

    //把小的先放进去,直到有一个块放完
    while ((i <= m) && (j <= r)) { 
        if (c[i] < c[j]) d[k++] = c[i++];
        else d[k++] = c[j++];
    }

    //把还没有放完的数据放进d数组
    if (i > m)
        for (int q = j; q <= r; q++)
            d[k++] = c[q];
    else
        for (int q = i; q <= m; q++)
            d[k++] = c[q];
}

template<class T>
void MergePass(T* a, T* b, int s, int n)
{
    int i = 0;//从头开始进行
    while (i <= n - 2 * s) {//能够分出长度为s的两段时,直接对两段归并
        Merge(a, b, i, i + s - 1, i + 2 * s - 1);
        i = i + 2 * s; //在数组中后移两个段的距离
    }

    //不能分成恰好的两段时,有两种情况:
    //如果能分成两段,则分成一段长度为s的,一段包含剩下元素的,进行归并;
    //不能分成两段,则直接拷贝到b数组中
    if (i + s < n)
        Merge(a, b, i, i + s - 1, n - 1);
    else
        for (int j = i; j <= n - 1; j++)
            b[j] = a[j];
}

template<class T>
void MergeSort(T * a, int n)
{
    T* b = new T[n]; //存放过程中数据的数组
    int s = 1; //段长度
    while (s < n) {
        MergePass(a, b, s, n);//从a到b归并
        s += s;               //长度加倍
        MergePass(b, a, s, n);//从b到a归并
        s += s;
    }
    delete[] b;
}
  • 快速排序

    快速排序是一种较复杂的排序方式,但是性能十分优秀。它的特点是取一个数作为参照数,从左找比该数大的数,找到后再从右找比该数小的数,如果找到了这样一对数,就将它们交换位置,直到左边指针大于等于右边指针,这时把参照数和右指针指着的数交换位置,然后对该位置左边的数据和右边的数据分别进行归并排序,直到进入方法时的左指针就大于等于右指针时,直接返回。这是一种不稳定的排序方式,最坏时间复杂度为O(n^2),在数组本身就有序时出现,平均时间复杂度为O(nlogn)。
    具体实现:

template<class T>
void quickSort(T* a, int left, int right,int n)
{
     //当左指针大于等于右指针时,不需要排序,直接返回
    if (left >= right) return;
    int i = left,       //从左到右的指针
        j = right + 1;  //从右到左的指针
    T pivot = a[left]; //参照数

    while (true) {//从左找大于参照数的数,从右找小于参照数的数
        do {
            i++;
        } while (a[i] < pivot);//小于参照数,继续查找下一个
        do {
            j--;
        } while (a[j] > pivot);//大于参照数,继续查找下一个
        if (i >= j) break; //找不到要交换的对
        Swap(a[i], a[j]);
    }
    //参照数与右指针所指的数交换位置
    a[left] = a[j];
    a[j] = pivot;
    quickSort(a, left, j - 1,n);//对左半边排序
    quickSort(a, j + 1, right,n);//对右半边排序
}

这样6个排序算法就大致讲完了,如果有什么错误与不足还望指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值