• 各类排序算法


    堆排序

    堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。

      (1)用大根堆排序的基本思想

      ① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区

      ② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key

      ③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。

      ……

      直到无序区只有一个元素为止。

      (2)大根堆排序算法的基本操作:

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

      ② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。

      注意:

      ①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。

      ②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止

    特点

       堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择关键字最大(或最小)的记录

    堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。

    初始建堆的时间约为n/2*logn, 最初从n/2即倒数第二层开始调整,使得当前节点为孩子和自己中的最小(大)值。那么调整到第一层时,根节点为最小值。

    不稳定的排序算法

    代码

    View Code
     1 void adjust(int i,int n)//调整为小顶堆
     2 {
     3     int j = i,k;
     4     a[0] = a[i];
     5     j = 2*i;
     6     while(j<=n)
     7     {
     8         if(j < n&&a[j]>a[j+1])//左右孩子结点的最小值
     9             j++;
    10         if(a[0]>a[j])//如果比最小值大
    11         {
    12             a[i] = a[j];//当前最小值孩子结点上移 i变为当前j值 继续与下面比较
    13             i = j;
    14             j = 2*j;
    15         }
    16         else
    17             break;
    18     }
    19     a[i] = a[0];//放在合适位置
    20 }
    21 void heapsort(int n)
    22 {
    23     int i,t;
    24     for(i = n/2 ; i>0 ; i--)//最初调整为堆
    25         adjust(i,n);
    26     for(i = n ; i>1 ; i--)//依次交换根节点和尾结点
    27     {
    28         t = a[1];
    29         a[1] = a[i];
    30         a[i] = t;
    31         adjust(1,i-1);
    32     }
    33 }

     快速排序

    设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

    最坏 时间复杂度为o(n2)。

    最佳 T(n)=θ(nlogn)

    代码

    View Code
     1 /*
     2  一趟快速排序的算法是:   
     3  找一个记录,以它的关键字作为“枢轴”,
     4  凡其关键字小于枢轴的记录均移动至该记录之前,
     5  反之,凡关键字大于枢轴的记录均移动至该记录之后。
     6  A[0] A[1] A[2] A[3] A[4] A[5] A[6]:   
     7  49   38    65   97   76   13   27   
    
     8  进行第一次交换后:
     9  27 38 65 97 76 13 49   
    10  进行第二次交换后:
    11  27 38 49 97 76 13 65   
    12  进行第三次交换后:27 38 13 97 76 49 65    
    13  进行第四次交换后:27 38 13 49 76 97 65 
    14  */
    15  int fqsort(int low,int high,int a[])
    16  {
    17      int i = low,j = high,k = a[low];//找一个比较的点
    18      while(i<j)
    19      {        
    20          while(i<j&&a[j]<=k)//从右向左移直至找到比k大的数
    21              j--;
    22          a[i] = a[j];//比这个数大的放左边
    23          while(i<j&&a[i]>=k)//从左向右移直至找到比k小的数
    24              i++;
    25          a[j] = a[i];//比这个数大的放右边
    26      }
    27      a[i] = k;
    28      return j;//j左右两边已经被k分割开 再把j+1当作右边一组的low j-1当作左边一组的high进行下一次的快排
    29  }
    30  void qsort(int low,int high,int a[])
    31  {
    32      int q;
    33      if(low<high)
    34      {        
    35          q = fqsort(low,high,a);//找到分割点
    36          qsort(low,q-1,a);//左右两边进行快排
    37          qsort(q+1,high,a);
    38      }
    39  }

     归并排序

    归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

    将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

    归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1)
    2(3) 2(4) 3(2) 5(5) 中的2 和 2
    是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要.这也是它比快速排序优势的地方.

     代码

    View Code
     1 void msort(int low,int mid,int high)//将两部分归并
     2 {
     3     int i,j,n1 = mid-low+1,n2 = high-mid,x[10001],y[10001];
     4     for(i = 1 ; i <= n1 ; i++)//存于两个数组中
     5         x[i] = a[low+i-1];
     6     for(i = 1 ; i <= n2 ; i++)
     7         y[i] = a[mid+i];
     8     int k = low;
     9     i = 1;
    10     j = 1;
    11     while(i<=n1&&j<=n2)//排序
    12     {
    13         if(x[i]<y[j])
    14             a[k] = x[i++];
    15         else
    16             a[k] = y[j++];
    17         k++;
    18     }
    19     while(i<=n1)
    20         a[k++] = x[i++];
    21     while(j<=n2)
    22         a[k++] = y[j++];
    23 }
    24 void merge(int low,int high)//二分递归合并
    25 {
    26     int mid;
    27     if(low<high)
    28     {
    29         mid = (low+high)/2;
    30         merge(low,mid);
    31         merge(mid+1,high);
    32         msort(low,mid ,high);
    33     }
    34 }

     拓扑排序

    拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。

    在现实生活中,也会有不少应用例子,比如学校课程布置图,要先修完一些基础课,才可以继续修专业课。
    一个简单的求拓扑排序的算法:首先要找到任意入度为0的一个顶点,删除它及所有相邻的边,再找入度为0的顶点,以此类推,直到删除所有顶点。顶点的删除顺序即为拓扑排序。
     对于有n个顶点和e条边的有向图而言,for循环中建立入度为0的顶点栈时间为On);若在拓扑排序过程中不出现有向环,则每个顶点出栈、入栈和入度减1的操作在while循环语句中均执行e次,因此拓扑排序总的时间花费为O (n+e)
    代码

     http://acm.hdu.edu.cn/showproblem.php?pid=1285

    以这道题为例

    这题说明了 不会出现环 就没判断

    非递归版

    View Code
     1 #include<stdio.h>
     2 #include<string.h>
     3 int g[501][501],c[501],topo[501],de[501];
     4 void toposort(int n)
     5 {
     6     int i,j,k;
     7     for(i = 1 ;i <= n ;i++)//n次查找
     8     {
     9         for(j = 1 ; j <= n ; j++)
    10         {
    11             if(de[j]==0)//寻找入度为0的节点 并将其删除
    12             {
    13                 de[j]--;
    14                 topo[i] = j;
    15                 for(k = 1 ;k <= n ; k++)//同时更新以它为前驱节点的入度 
    16                     if(g[j][k])
    17                         de[k]--;
    18                 break;
    19             }
    20         }
    21     }
    22 }
    23 int main()
    24 {
    25     int n,m,i,j,a,b;
    26     while(scanf("%d%d", &n,&m)!=EOF)
    27     {
    28         memset(g,0,sizeof(g));
    29         memset(de,0,sizeof(de));
    30         for(i = 1 ;i <= m ; i++)
    31         {
    32             scanf("%d%d",&a,&b);
    33             if(!g[a][b])
    34             {
    35                 g[a][b] = 1;
    36                 de[b]++;
    37             }
    38         }
    39         toposort(n);
    40         for(i = 1 ;i < n ; i++)
    41         printf("%d ",topo[i]);
    42         printf("%d\n",topo[n]);
    43     }
    44     return 0;
    45 }

     dfs 递归回溯版

    View Code
     1 int dfs(int u,int n)
     2 {
     3     int v;
     4     c[u] = -1;
     5     for(v = 1 ; v <= n ; v++)
     6         if(g[u][v])
     7         {
     8             if(c[v]<0)
     9             {
    10                 return 0;
    11             }
    12             else
    13                 if(!c[v]&&!dfs(v,n))
    14                     return 0;
    15         }
    16     c[u] = 1;
    17     return 1;
    18 }
    19 int toposort(int n)
    20 {
    21     int u;
    22     memset(c,0,sizeof(c));
    23     for(u = 1 ;u <= n ;u++)
    24         if(!c[u])
    25         {
    26             if(!dfs(u,n))
    27                 return 0;
    28         }
    29     return 1;
    30 }
  • 相关阅读:
    关于产品那些事
    关于“编程的本质”的探讨
    分享一款在线贝塞尔曲线调试器
    HTML、CSS、JS对unicode字符的不同处理
    HTTP Content-Disposition Explanation [ from MDN ]
    认证 (authentication) 和授权 (authorization) 的区别
    事件驱动引擎会取代多线程编程吗
    你所不知道的JSON
    都有哪些特殊而实用的的搜索引擎?
    巨头们的GitHub仓库整理
  • 原文地址:https://www.cnblogs.com/shangyu/p/2601996.html
Copyright © 2020-2023  润新知