排序算法合集

来源:互联网 发布:在线教育数据分析 编辑:程序博客网 时间:2024/04/28 06:13

排序算法有很多中,我在这里只能粗略地介绍其中的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个排序算法就大致讲完了,如果有什么错误与不足还望指正。

0 0
原创粉丝点击