zoukankan      html  css  js  c++  java
  • 各类排序算法

    堆排序

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

      (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 }
  • 相关阅读:
    learnyou 相关网站
    hdu 3038 How Many Answers Are Wrong
    hdu 3047 Zjnu Stadium 并查集高级应用
    poj 1703 Find them, Catch them
    poj 1182 食物链 (带关系的并查集)
    hdu 1233 还是畅通工程
    hdu 1325 Is It A Tree?
    hdu 1856 More is better
    hdu 1272 小希的迷宫
    POJ – 2524 Ubiquitous Religions
  • 原文地址:https://www.cnblogs.com/shangyu/p/2601996.html
Copyright © 2011-2022 走看看