排序算法学习

排序算法有:冒泡排序,快速排序,选择排序,堆排序,插入排序,合并排序,希尔排序,计数排序,基数排序,桶排序。


交换排序: 冒泡排序、快速排序

选择排序:直接选择排序、树形选择排序、堆排序

插入排序:    直接插入排序、希尔排序、合并排序

分配排序:    桶排序、基数排序、计数排序


交换排序

1、冒泡排序:

对待排序元素的关键字从后往前进行多遍扫描,遇到相邻两个关键字次序与排序规则不符时,就将这两个元素进行交换。这样关键字较小的那个元素就像一个泡泡一样,从最后面冒到最前面来。


#include <iostream>  
using namespace std;  
  
void BubbleSort(int a[], int n) {  
    for (int i = 0; i < n; i++) //遍历n次  
        for (int j = n-1; j > i; j--) {  
            if (a[j] < a[j-1]) { //当前比较前面键值,使当前总为最小的  
                swap(a[j-1], a[j]);//交换  
            }  
        }  
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    cout << "冒泡排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    BubbleSort(num, 6);  
    cout << "冒泡排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  

冒泡的时间复杂度为O(n2),空间复杂度O(1)。稳定。


2、快速排序

快速排序采用了分治算法策略,它是冒泡排序的一种改进。

基本思路是:把待排列的数据分为两个子列,从数列中挑出一个数作为基准,遍历其他数据,把小于它的放前面,大的放在基准的后面。之后,通过递归,将各个子序列划分为更小的序列,直到把小于基准值元素的子数列和大于基准值元素的字数列排序。


#include <iostream>  
using namespace std;  
  
//拆分为两个子列  
int Partition(int a[], int left, int right) {  
    int base = a[left];  
    while (left < right) {  
        while (left < right && a[right]>base) //从右往左找出第一个比基准小的数据  
            --right;  
        a[left] = a[right]; //将这个数放到基准的左边  
        while (left < right && a[left]<base) //从左往右找出第一个比基准大的数据  
            ++left;  
        a[right] = a[left]; //放到右边  
    }  
    a[left] = base;  
    return left; //返回基准的位置  
}  
void  QuickSort(int a[], int left, int right) {  
    int i;  
    if (left < right) {  
        i = Partition(a, left, right);  
        QuickSort(a, left, i-1);  
        QuickSort(a, i+1, right);  
    }  
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    cout << "排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    QuickSort(num, 0, 5);  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  
</pre><pre name="code" class="cpp">
<strong><span style="font-size:32px;">选择排序</span></strong>
<strong><span style="font-size:32px;">
</span></strong>
<span style="font-size:32px;"></span><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">1,直接选择排序:</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">对待排序的序列,选出关键字最小的数据,将它和第一个位置的数据交换,接着,选出关键字次小的数据,将它与第二个位置上的数据交换。以此类推,直到完成整个过程。</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">所以如果有n个数据,那个需要遍历n-1遍。其实现代码如下:</p><strong>
</strong>
#include <iostream>  
using namespace std;  
  
void SelectSort(int a[], int n) {  
    int i, j, small;  
    for (i = 0; i < n-1; ++i) {  
        small = i;  
        for (j = i+1; j < n; ++j) {  
            if (a[small] > a[j]) small = j;  
        }  
        if (small != i)  
            swap(a[small], a[i]);  
    }  
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    cout << "排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    SelectSort(num, 6);  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  
直接选择排序的时间复杂度为O(n2)。同样适合对链式结构进行排序


2、堆排序


#include <iostream>  
  
using namespace std;  
  
//用数组二叉树  
void HeapAdjust(int a[], int s, int n)//构成堆  
{  
    int j;  
    while(2*s + 1 < n) //第s个结点有右子树   
    {  
        j=2*s + 1 ;  
        if((j+1) < n)  
        {              
            if(a[j] < a[j+1])//右左子树小于右子树,则需要比较右子树  
                j++; //序号增加1,指向右子树   
        }  
        if(a[s] < a[j])//比较s与j为序号的数据  
        {              
            swap(a[s], a[j]);             
            s = j ;//堆被破坏,需要重新调整  
        }  
        else //比较左右孩子均大则堆未破坏,不再需要调整  
            break;  
    }  
}  
void HeapSort(int a[],int n)//堆排序  
{  
    int t, i;  
    int j;  
    for(i = n/2 - 1; i >= 0; i--)    //将a[0,n-1]建成大根堆  
        HeapAdjust(a, i, n);  
    for(i = n-1; i > 0; i--)  
    {  
        swap(a[0], a[i]);  
        HeapAdjust(a, 0, i);        //将a[0]至a[i]重新调整为堆  
    }    
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
  
    cout << "排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
  
    HeapSort(num, 6);  
  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  

堆排序的时间复杂度为O(nlog2n),空间复杂度为O(1)。不稳定。


插入排序


1,直接插入排序

直接插入排序是一种简单直观的排序算法,它的工作原理是通过建有序序列,对于没有排序的数据,在已排序序列中从后往前扫描,找到相应位置,并插入。故,如果是数组这样的连续空间的数据序列,那就每次插入都要将其位置的后面数据都向后移动,相对比较麻烦。


#include <iostream>  
using namespace std;  
  
void InsertSort(int a[], int n) {  
    int i, j, temp;  
    for (i = 1; i < n; ++i) {  
        temp = a[i]; //先保存当前值  
        for (j = i-1; j >= 0 && temp < a[j]; --j) //从后往前移,直到找到适合位置  
            a[j+1] = a[j]; //往后移一位,腾出位置  
        a[j+1] = temp; //将值放入已找出的适当位置  
    }  
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    cout << "排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    InsertSort(num, 6);  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  

这样的直接插入排序空间复杂度O(1),但是时间复杂度为O(n2)


2、希尔排序

希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。

它的具体做法是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成(n除以d1)个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。


#include <iostream>  
using namespace std;  
  
void ShellSort(int a[], int n) {  
    int d, i, j, temp;  
    d = n/2; //分成n/2组  
    while (d >= 1) {   
        for (i = d; i < n; ++i) { //对每组进行直接插入排序  
            temp = a[i];  
            j = i - d;  
            while (j >= 0 && a[j] > temp) {  
                a[j+d] = a[j];  
                j -= d;  
            }  
            a[j+d] = temp;  
        }  
        d /= 2;  
    }  
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    cout << "排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    ShellSort()  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  
希尔排序时间复杂度为O(n4/3),空间复杂度为O(1),但是不适合在链表结构上使用。

3、合并排序(归并排序)

 合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。合并排序也叫归并排序。


<pre name="code" class="cpp">#include <iostream>  
using namespace std;  
  
//2-路合并  
void Merge(int a[], int r[], int left, int mid, int n) {//a[]归并到r[]  
    int s1 = left, s2 = mid+1, s3 = left;  
    while (s1 <= mid && s2 <= n)//比较较小的数据填充到a[]  
        if (a[s1] <= a[s2])   
            r[s3++] = a[s1++];  
        else   
            r[s3++] = a[s2++];  
    while (s1 <= mid) r[s3++] = a[s1++];//将未填充的数补全  
    while (s2 <= n) r[s3++] = a[s2++];//将未填充的数补全  
}  
//2-路归并排序  
void MergePass(int a[], int r[], int n, int len) {  
    int beg = 0, end;  
    while (beg + len < n) {  
        end = beg + 2*len - 1;  
        if (end >= n)//最后一个可能少于len个  
            end = n - 1;  
        Merge(a, r, beg, beg+len-1, end);//合并  
        beg = end + 1;//提供给下次开始  
    }  
    if (beg < n)  
        while (beg < n) {  
            r[beg] = a[beg];  
            beg++;  
        }  
}  
void MergeSort(int a[], int n) {  
    int len = 1;//当前进行归并有序数列的长度  
    int f = 0;  
    int *p = (int *)malloc(sizeof(int)*n);  
    while(len < n) {  
        if (f) MergePass(p, a, n, len);//交替归并到p和a  
        else MergePass(a, p, n, len);  
        len *= 2;  
        f = 1 - f;  
    }  
    if (f) //排序后是归并到p的情况,复制回到a  
        for (f = 0; f < n; f++)  
            a[f] = p[f];  
    free(p);  
}  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    cout << "排序前:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    MergeSort(num, 6);  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << num[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  


分配排序


桶排序:

假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)。

他的排序过程如图:




桶排序只是用于关键字取值范围较小的情况,否则会因为所需箱子的数目太多而导致资源的浪费。这种排序的使用价值不大,他一般用于基数排序的一个中间过程。


2,基数排序:

基数排序是桶排序的一种改进和推广。它的基本思想是,先设立r个队列,队列编号分别为0~r-1,(r为关键字的基数),然后按照下面的规则对关键字进行“分配”和“收集”。

1, 按照最低有效位的值,把n个关键字分配到上述的r个队列里,然后从小到达将各队列中关键字收集起来。

2, 再按低次有效位的值把刚刚收集起来的关键字分配到r个队列中,重复收集工作。

3, 重复上述分配和收集工作,直到最高的有效位。(也就是说,如果数位为d,则需要重复进行d次。d由所有元素中最长的一个元素的位数计量。)

图示如下:




上图过程,就是先按个位分配然后收集,再按十位,百位分配和收集,最后就得出排序结果。

在C++中可以使用库函数lexicongraphical_compare()进行字典次序比较。

每一趟分配的时间是O(n),所以总时间的开销为O(d(n+r)) = O(n),通常d,r为常数。空间负复杂度为O(n+r)。基数排序使用于采用链式结构存储结构的排序。

3,计数排序:

 计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。这个算法于1954年由 Harold H. Seward 提出。

计数排序算法的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。

#include <iostream>  
  
using namespace std;  
//n为个数,k为最大值,b为输出  
void CountingSort(int a[], int b[], int n, int k)  
{  
    int* c = new int[k+1];  
    memset(c, 0, (k+1) * sizeof(int));  
    for (int j = 0; j < n; j++) c[a[j]]++;//保存每个下标的值的个数  
    for (int j = 1; j <= k; j++) c[j] += c[j - 1];//从前到后累计的位置  
    for (int j = n - 1; j >= 0; j--)  
    {  
        b[c[a[j]] - 1] = a[j];  
        c[a[j]]--;  
  
    }  
    delete []c;  
}  
  
int main()  
{  
    int num[6] = {23,45,13,2,99,78};  
    int out[6];  
    cout << "排序前:" << endl;  
    int max = 0;  
    for (int i = 0; i < 6; i++) {  
        if (max < num[i]) max = num[i];  
        cout << num[i] << " ";  
    }  
    cout << endl;  
  
    CountingSort(num, out, 6, max);  
  
    cout << "排序后:" << endl;  
    for (int i = 0; i < 6; i++) {  
        cout << out[i] << " ";  
    }  
    cout << endl;  
    return 0;  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值