• 经典排序算法分析(附源码)


    No.1 冒泡排序

    关于冒泡排序相信大家学排序算法的时候,老师肯定会讲这一个,因为这个便于理解。记得当时我的老师说,冒泡排序就像是从水底涌出的气泡,慢慢地向上升,越来越大。也就是说最大的元素往后排列。感觉这个比喻有点冷啊!

    假设数组的长度为N,可以用下面的步骤实现冒泡排序:

    1. 比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交 换。

    2. 这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就上升到数组第N-1个位置。

    3. N=N-1,如果N不为0就重复前面二步,否则排序完成。

    下面给出源码实现:

    void bubbleSort(int A[],int len)
    {
    for(int i = 0;i < len;i++)
    for(int j = 0;j < len-i-1;j++)
    {
    if(A[j]>A[j+1])
    swap(A[j],A[j+1]);
    }
    }

    No.2 直接插入排序

    直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成 为止。 设数组为 a[0…n-1]。

    1. 初始时, a[0]自成 1 个有序区,无序区为 a[1..n-1]。 令 i=1

    2. 将 a[i]并入当前的有序区 a[0…i-1]中形成 a[0…i]的有序区间。

    3. i++并重复第二步直到 i==n-1。排序完成。

    void insertSort(int Array[],int len)
    {
    for(int i = 1;i < len;i++)
    {
    int j = i - 1;
    int key = Array[i];
    while(j >= 0 && Array[j] > key)
    {
    Array[j+1] = Array[j];
    j--;
    }
    Array[j+1] = key;
    }
    }

    No.3 希尔排序

    该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序, 待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插 入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是 很高的,因此希尔排序在时间效率上比前两种方法有较大提高。

    void shellSort(int A[],int n)
    {
    int grap,i,j;
    for(grap = n/2;grap > 0;grap /=2)
    for(i = grap;i < n;i++)
    for(j = i-grap;j>=0 && A[j]>A[j+grap];j -= grap)
    swap(A[j+grap],A[j]);
    
    }

    No.4  直接选择排序

    直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是 直接插入排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有 序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。

    设数组为 a[0…n-1]。

    1. 初始时, 数组全为无序区为 a[0..n-1]。 令 i=0

    2. 在无序区 a[i…n-1]中选取一个最小的元素,将其与 a[i]交换。交换之后 a[0…i] 就形成了一个有序区。

    3. i++并重复第二步直到 i==n-1。排序完成。

    void selectSort(int A[],int len)
    {
    for(int i = 0;i < len;i++)
    {
    int min = i;
    for(int j = i+1;j<len;j++)
    if (A[min] > A[j])
    {
    min = j;
    }
    swap(A[min],A[i]);
    }
    }

    No.5  归并排序

    归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的 第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较, 如果有数列为空,那直接将另一个数列的数据依次取出即可。

    void merge(int A[],int p,int mid,int q)
    {
    int i = p, j = mid + 1;
    int m = mid, n = q;
    int k = 0;
    int *temp = new int[q-p+1];
    
    while (i <= m && j <= n)
    {
    if (A[i] <= A[j])
    temp[k++] = A[i++];
    else
    temp[k++] = A[j++];
    }
    
    while (i <= m)
    temp[k++] = A[i++];
    
    while (j <= n)
    temp[k++] = A[j++];
    
    for (i = 0; i < k; i++)
    A[p + i] = temp[i];
    delete []temp;
    }
    void mergeSort(int A[],int p,int q)
    {
    if(p<q)
    {
    int m = (p+q)/2;
    mergeSort(A,p,m);
    mergeSort(A,m+1,q);
    merge(A,p,m,q);
    }
    }

    No.6 快速排序

    它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

    int partition(int A[],int p,int q)
    {
    int key = A[p];
    while(p<q)
    {
    while (p<q && A[q]>=key)
    q--;
    A[p] = A[q];
    while(p<q && A[p]<=key)
    p++;
    A[q] = A[p];
    }
    A[p] = key;
    return p;
    }
    void quickSort(int A[],int p,int q)
    {
    int pos;
    if(p<q)
    {
    pos = partition(A,p,q);
    quickSort(A,p,pos);
    quickSort(A,pos+1,q);
    }
    }

    No.7 堆排序

    先介绍一下堆的定义:

    堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:

    Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]

    即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

    堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。

    堆排序的思想

    利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

    其基本思想为(大顶堆):

    1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;

    2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];

    3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

    操作过程如下:

    1)初始化堆:将R[1..n]构造为堆;

    2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。

    因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。

    void HeapAdjust(int A[],int i,int len)
    {
    int lchild = 2*i+1;
    int rchild = 2*i+2;
    int parentPos = i;//临时变量存放当前父节点
    if(i<=(len-1)/2)//如果i不是叶节点就不用进行调整
    {
    if(lchild<=len-1 && A[lchild]>A[parentPos])
    parentPos = lchild;
    if(rchild<=len-1 && A[rchild]>A[parentPos])
    parentPos = rchild;
    if(parentPos!=i)
    {
    swap(A[i],A[parentPos]);
    HeapAdjust(A,parentPos,len);//避免调整之后parentPos为父节点的子树不是堆
    }
    }
    
    }
    
    void buildHeap(int A[],int len)//建立堆
    {
    int i = (len-1)/2;
    while(i>=0)
    {
    HeapAdjust(A,i,len);
    i--;
    }
    }
    
    void heapSort(int A[],int len)
    {
    int i;
    buildHeap(A,len);
    for(i = len;i>=1;i--)//调整排序,即每次将剩余元素中最大的元素放到最后面
    {
    HeapAdjust(A,0,i);
    swap(A[0],A[i-1]);
    }
    }

    以上是七种基本的排序算法,另外还有基数排序,计数排序,桶排序等排序算法,就不做介绍了。

    基础最重要,不要轻视基础,要重视它,打牢它,才能取得更大的成绩。

  • 相关阅读:
    firefox打开链接自动跳转至新页面设置
    sql server 查询不为空的字段
    C# 判断ip地址是否正确
    Win7自带功能,刻录光盘遇到的问题
    【一起学源码-微服务】Nexflix Eureka 源码六:在眼花缭乱的代码中,EurekaClient是如何注册的?
    【一起学源码-微服务】Nexflix Eureka 源码五:EurekaClient启动要经历哪些艰难险阻?
    【一起学源码-微服务】Nexflix Eureka 源码四:EurekaServer启动之完成上下文构建及EurekaServer总结
    【一起学源码-微服务】Nexflix Eureka 源码三:EurekaServer启动之EurekaServer上下文EurekaClient创建
    【一起学源码-微服务】Nexflix Eureka 源码二:EurekaServer启动之配置文件加载以及面向接口的配置项读取
    【一起学源码-微服务】Netflix Eureka 源码一:Netflix Eureka 源码初探,我们为什么要读源码?
  • 原文地址:https://www.cnblogs.com/tgycoder/p/4570199.html
Copyright © 2020-2023  润新知