排序算法 (-)

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

内部排序方法

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

外部排序方法

外部排序基本上由两个相互独立的阶段组成。首先,按可用内存大小,将外存上含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)。

原文地址:https://www.cnblogs.com/menghuizuotian/p/3773726.html