• 排序算法 (-)


    以下算法都编译通过,具体原理相信大家都懂,直接上代码。

    内部排序方法

    若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。
    内排序的方法有许多种,按所用策略不同,可归纳为五类:插入排序选择排序交换排序归并排序和基数排序。
    其中,插入排序主要包括直接插入排序希尔排序两种;选择排序主要包括直接选择排序堆排序交换排序主要包括气(冒)泡排序和快速排序

    外部排序方法

    外部排序基本上由两个相互独立的阶段组成。首先,按可用内存大小,将外存上含n个记录的文件分成若干长度为k的子文件或段(segment),依次读入内存并利用有效的内部排序方法对它们进行排序,并将排序后得到的有序子文件重新写入外存。通常称这些有序子文件为归并段或顺串;然后,对这些归并段进行逐趟归并,使归并段(有序子文件)逐渐由小到大,直至得到整个有序文件为止。

    插入排序

    1.直接插入排序

    typedef int eleType;
    typedef struct{
       eleType key[MAXSIZE],
           int length;
    }listSeq;
    
     void InsertSort(listSeq  &L)
     {
       int j;
       for (int i=1;i<=L.length;i++)
       {
           L.key[0]=L.key[i];
           j=i-1;
          while((L.key[0]<L.key[j])&&j>0)
          {
              L.key[j+1]=L.key[j];
              j--;
          }
           L.key[j+1]=L.key[0];
       }
     }

    复杂度分析:

    插入排序在最好的情况下(序列已经排好)有最少的比较次数 ,但是它在元素移动方面效率非常低下,最糟糕的情况下是逆序(比较次数达到(n+2)(n-1)/2,移动次数为 (n+4)(n-1)/2,因此时间复杂度为O(n2),附加存储空间为O(1))因为它只与毗邻的元素进行比较,效率比较低。

    2.希尔排序算法

    void shellInsert(listSeq &L,int d)
    {
      int temp,j;
      for (int i=d;i<L.length;i++)    //理解这句话
      {
         temp=L.key[i];
         j=i-d;
         while (temp<L.key[j]&&j>=0)
         {
             L.key[j+d]=L.key[j];
             j-=d;
         }
         L.key[j+d]=temp;
      }
    }
    
    void shellSort(listSeq &L)
    {
        int d;
        d=L.length/2;
        while (d>1)
        {
            shellInsert(L,d);  //这个地方不需用引用
            d=d/2;
        }
        
    }

    复杂度分析:

    希尔排序实际上是预处理阶段优化后的插入排序,一般而言,在 比较大时,希尔排序要明显优于插入排序。

    希尔排序的时间耗费与索取的“增量”序列的函数有关,到目前为止,尚未有人求出一个最好的增量序列是希尔时间性能达到最好。经过大量研究,普遍认为希尔的时间复杂度为O(n3/2)。空间上只需要一个元素辅助空间。

    交换排序

    1.冒泡排序

      每一轮选一个最大的元素。

    void bubbleSort(listSeq &L)
    {
        int temp;
        int flag=1;
        for (int i=0;i<(L.length-1)&&flag;i++)  //当不改变的时候就终止!
        {
            flag=0;
            for (int j=0;j<L.length-i-1;j++)
            {
                if (L.key[j+1]<L.key[j])
                {
                    temp=L.key[j];
                    L.key[j]=L.key[j+1];
                    L.key[j+1]=temp;
                    flag=1;
                }
            }
        }
    
    }

    复杂度分析:

    冒泡排序在最优情况下只需要经过n-1次比较即可得出结果(即对于完全正序的表,此时循环一次就达到了终止条件),最坏情况下也要进行 次比较。也就是O(n2)

    附加存储空间为O(1)。

    2.快排 --冒泡排序的改进

    采用递归的方式

    int partQuickSort(listSeq &L,int left,int right)  //注意返回中间值
    {
       int temp;
       int low=left,high=right;
       temp=L.key[low];
       while (low<high)
       {
           while((L.key[high]>=temp)&&low<high)
               high--;
               L.key[low]=L.key[high];  //此时high被悬空
           
           while((L.key[low]<temp)&&low<high)
               low++;
    
               L.key[high]=L.key[low];  //此时low被悬空
    
       }
       L.key[low]=temp;         //这个不要忘记了;
       return low;
    }
    void QuickSort(listSeq &L,int low,int high)
    {
       int mid;
       if (low<high)
       {
         mid=partQuickSort(L,low,high);
         QuickSort(L,low,mid-1);
         QuickSort(L,mid+1,high);
       }
    }

    复杂度分析:

    快速排序采用的“大事化小,小事化了”的思想,用递归的方法,将原问题分解成若干规模较小但与原问题相似的子问题进行求解。快速算法的平均时间复杂度为O(nlogn) ,平均而言,快速排序是基于关键字比较的内部排序算法中速度最快者;但是由于快速排序采用的是递归的方法,因此当序列的长度比较大时,对系统栈占用会比较多。快速算法尤其适用于随机序列的排序。

    最坏情况,当序列为正序的时候,要进行 次排序。理想情况下每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n 趟划分,便可以得到理想长度为1的子表。整个算法时间复杂度为O(nlog2n)(因为每次划分就要进行n次比较)。

    空间复杂度上,尽管快排只需要一个元素的辅助空间,但快排需要一个栈空间来实现递归。最坏情况下,栈的最大深度为n。总得评卷时间复杂度为O(log2n)。

    归并排序

    //将有二个有序数列a[first...mid]和a[mid...last]合并。
    void mergearray(int a[], int first, int mid, int last, int temp[])
    {
        int i = first, j = mid + 1;
        int m = mid,   n = last;
        int k = 0;
        
        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[first + i] = temp[i];
    }
    void mergesort(int a[], int first, int last, int temp[])
    {
        if (first < last)
        {
            int mid = (first + last) / 2;
            mergesort(a, first, mid, temp);    //左边有序
            mergesort(a, mid + 1, last, temp); //右边有序
            mergearray(a, first, mid, last, temp); //再将二个有序数列合并  
        }
    }
    
    void MergeSort(int a[], int n)  
    {
        int *p = new int[n];    //temp
        mergesort(a, 0, n - 1, p);
        delete[] p;
    }
    
    int main(int argc, char* argv[])
    {
        int a[10];
        int t;
        cout<<"请输入长度:"<<endl;
        cin>>t;
        cout<<"请输入数组:"<<endl;
        for (int i=0;i<t;i++)
        cin>>a[i];
        MergeSort(a,t);
        for (int j=0;j<t;j++)
        cout<<a[j]<<" ";
        cout<<endl;
        return 0;
    }

    复杂度分析:

    对长度为n的数据表进行排序,必须要进行log2n趟排序,每一趟归并军队数据表中的n个元素做一次操作,时间复杂度为O(n),总得时间复杂度为O(nlog2n)。

    归并排序最大特点就是它是稳定的排序方法。通常情况下实验归并排序需要与待排序数据表等大小的辅助空间,所以空间复杂度为O(n)。

  • 相关阅读:
    springboot2.0整合logback日志(详细)
    关于Logstash中grok插件的正则表达式例子
    feign多文件上传
    HBase API(新版本)
    HBase基础知识
    Hive数据操作和数据查询
    Hive数据定义
    Hive基础知识
    Hive安装
    Spark词频统计,求TOP值,文件排序,二次排序
  • 原文地址:https://www.cnblogs.com/menghuizuotian/p/3773726.html
Copyright © 2020-2023  润新知