C++之排序算法

目录

时间复杂度比较

1. 插入排序

1.1 直接插入排序

1.2 折半插入排序

1.3 希尔排序

2. 交换排序

2.1 冒泡排序

2.2 快速排序

3. 选择排序

3.1 直接选择排序

3.2 堆排序

4. 归并排序

5. 基数排序

6. 外部排序

6.1 置换选择排序

6.2 多路归并


内部排序: 指的是待排序记录存放在计算机随机存储器中进行的排序过程。1-5

外部排序: 指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。6

时间复杂度比较

算法

最大时间

平均时间

最小时间

辅助空间代价

稳定性

直接插入排序

Θ(n2)

Θ(n2)

Θ(n)

Θ(1)

稳定

希尔排序

Θ(n3/2)

Θ(n3/2)

Θ(n3/2)

Θ(1)

不稳定

冒泡排序

Θ(n2)

Θ(n2)

Θ(n)

Θ(1)

稳定

快速排序

Θ(n2)

Θ(nlog n)

Θ(nlog n)

Θ(log n)

不稳定

选择排序

Θ(n2)

Θ(n2)

Θ(n2)

Θ(1)

不稳定

堆排序

Θ(nlog n)

Θ(nlog n)

Θ(nlog n)

Θ(1)

不稳定

归并排序

Θ(nlog n)

Θ(nlog n)

Θ(nlog n)

Θ(n)

稳定

 

基数排序

Θ(d·(n+r))

Θ(d·(n+r))

Θ(d·(n+r))

Θ(n+r)

稳定       

1. 插入排序

  • 1.1 直接插入排序

定义:将一个记录插入到已排好序的有序表中,从而得到一个新的有序表。

性质:稳定;

空间代价:Θ(1);

时间代价:

                最佳情况:n-1次比较,2(n-1)次移动,Θ(n)

                最差情况:Θ(n2)

                                  比较次数为

                                   移动次数为

                 平均情况:Θ(n2)

void Sort1(int Data[],int n)//直接插入排序
{
    int p,i;
    for(p = 1;p<n;p++)
    {
        int temp=Data[p];
        i=p-1;
        while(i>=0&&Data[i]>temp)
        {
            Data[i+1] = Data[i];
            i--;
        }
        Data[i+1] = temp;
    }
}
  • 1.2 折半插入排序

定义:在插入第i个记录时,前面的记录已经是有序的了,用二分法查找第i个记录的正确位置(把1.1的顺序查找换为二分法) 

性质:稳定

折半插入排序的时间复杂度仍为O(n2)

void Sort2(int Data[],int n)//折半插入排序
{
    int left,mid,right,p;//
    for(p=1;p<n;p++)
    {
        int temp=Data[p];//保存待插入数据
        left=0,right=p-1;
        while(left<=right)
        {
            mid=(left+right)/2;//求出中心点
            if(Data[mid] > temp)
            {
                right = mid-1;
            }
            else
            {
                left = mid+1;
            }
        }
        for(int i=p-1;i>=left;i--)
        {
            Data[i+1]=Data[i];
        }
        Data[left]=temp;//将待插入元素插入到有序表中
    }
}
  • 1.3 希尔排序

定义:

直接插入排序的两个性质: 在最好情况(序列本身已是有序的)下时间代价为Θ(n) ;对于短序列,直接插入排序比较有效

Shell排序有效地利用了直接插入排序的这两个性质

  • 先将序列转化为若干小序列,在这些小序列内进行插入排序
  • 逐渐扩大小序列的规模,而减少小序列个数,使得待排序序列逐渐处于更有序的状态
  • 最后对整个序列进行扫尾直接插入排序,从而完成排序

“增量每次除以2递减”的Shell 排序

性质:不稳定

空间代价:Θ(1)

时间代价:希尔排序的时间复杂性在O(nlog2n)和O(n2 )之间,大致为O(n1. 3)。

void Sort3(int Data[],int n)//希尔排序
{
    int d=n/2;
    while(d>=1)
    {
        for(int k=0;k<d;k++)
        {
            for(int i=k+d;i<n;i+=d)//直接插入排序
            {
                int temp=Data[i];
                int j=i-d;
                while(j>=k&&Data[j]>temp)
                {
                    Data[j+d]=Data[j];
                    j-=d;
                }
                Data[j+d]=temp;
            }
        }
        d=d/2;
    }
}
#include<iostream>
using namespace std;
int main()
{
    int a[5]={11,24,65,35,91};
    int b[5]={2,65,98,74,15};
    int c[7]={65,34,25,87,12,38,56};
    Sort1(a,5);
    for(int i=0;i<5;i++)
    {
        cout<<a[i]<<"  ";
    }
    cout<<endl;
    Sort2(b,5);
    for(int j=0;j<5;j++)
    {
        cout<<b[j]<<"  ";
    }
    cout<<endl;
    Sort3(c,7);
    for(int k=0;k<7;k++)
    {
        cout<<c[k]<<"  ";
    }
    return 0;
}

2. 交换排序

  • 2.1 冒泡排序

定义:不停地比较相邻的记录,如果不满足排序要求,就交换相邻记录,直到所有的记录都已经排好序。

注意:避免不必要的比较。检查每次冒泡过程中是否发生过交换,如果没有,则表明整个数组已经排好序了,排序结束。

性质:稳定性:稳定

空间代价:Θ(1)

时间代价分析:

比较次数 : 最少:Θ(n)     最多:

交换次数:  最多:Θ(n2)    最少:0      平均:Θ(n2)

void BubbleSort(int Data[],int n)
{
    int flag=0;
    int i,j;
    for(i=0;i<n;i++)
    {
        flag=0;
        for(j=1;j<n-i;j++)
        {
            if(Data[j]<Data[j-1])
            {
                flag=1;
                int temp=Data[j];
                Data[j]=Data[j-1];
                Data[j-1]=temp;
            }
        }
        if(flag==0)
        {
            return ;
        }
    }
}
  • 2.2 快速排序

定义:

  • 选择轴值(pivot)
  • 将序列划分为两个子序列L和R,使得L中所有记录都小于或等于轴值,R中记录都大于轴值
  • 对子序列L和R递归进行快速排序

主要思想:分——划分子问题;治——求解子问题(子问题不重叠);合——综合解

轴值选择:尽可能使L,R长度相等

                  选择策略: 选择最左边记录(第一个记录);随机选择;选择平均值

分割过程:轴值位于正确位置,分割后使得 L中所有记录小于轴值 R中记录大于轴值

性质:不稳定

最佳情况: 时间代价:Θ(nlog n)     空间代价:Θ(log n)

最差情况: 时间代价: Θ(n2)           空间代价: Θ(n)

平均情况: 时间代价:Θ(nlog n)     空间代价:Θ(log n)

最好的情形(左、右子区间的长度大致相等)

最坏的情形(每次能划分成两个子区间,但其中一个是空 或 要排序的数据已基本有序)

可能优化: 轴值选择、小子串不递归、消除递归

快速排序的过程:

第一趟排序后    13    38    27        49        76    97    65    52

int Partition(int Data[],int left,int right)//快速排序分割策略一
{
    //实现对Date[left]到date[right]的分割操作 并返回划分后周元素对应的位置
    int pivot = Data[left];//选择最左边的为轴元素
    while(left<right)
    {
        while(left<right &&Data[right]>pivot)//控制right指针的移动
        {
            right--;
        }
        Data[left] = Data[right];//将指针指向的数据移动到left位置
        while(left<right &&Data[left]<=pivot)//控制left指针移动
        {
            left++;
        }
        Data[right] = Data[left];//将left指向的数据移动到right位置
    }
    Data[left] = pivot;//将轴元素放到left位置
    return  left;      //返回轴元素的新位置  实现分治
}
void QuickSort(int Data[],int left,int right)
{
    //用分治法实现快速排序算法
    if(left<right)
    {
        int p=Partition(Data,left,right);//实现分割并找到分离的位置
        QuickSort(Data,left,p-1);
        QuickSort(Data,p+1,right);
    }
}

int Partition1(int Data[],int start,int end)//快速排序分割策略二
{
    //实现对Data[start]到date[end]的分割操作,并返回划分后轴元素对应的位置
    int pivot = Data[start];
    int left = start+1,right = end;//初始化 left right
    int temp;//交换变量
    while(left<=right)
    {
        while(left<=right &&Data[left]<=pivot)//控制left指针的移动
        {
            left++;
        }
        while(left<=right && Data[right]>pivot)//控制right指针的移动
        {
            right--;
        }
        if( left<right )
        {
            //swap(Data[right],Data[left]);//交换Data[right]和Date[left]
            temp=Data[right];
            Data[right]=Data[left];
            Data[left]=temp;
            left++;
            right--;
        }
        //swap(Data[start],Data[right]);//交换Data[right]和轴元素Data[start]
    }
            temp=Data[right];
            Data[right]=Data[start];
            Data[start]=temp;
        return right;//返回轴元素的新位置  实现分治
}
void QuickSort1(int Data[],int left,int right)
{
    //用分治法实现快速排序算法
    if(left<right)
    {
        int p=Partition1(Data,left,right);//实现分割并找到分离的位置
        QuickSort1(Data,left,p-1);
        QuickSort1(Data,p+1,right);
    }
}
#include<iostream>
using namespace std;
int main()
{
    int a[8]={45,32,61,98,74,17,22,53};
    int b[8],c[8];
    for(int j=0;j<8;j++)
    {
        b[j]=a[j];
        c[j]=a[j];
    }

    BubbleSort(a,8);
    cout<<"冒泡排序 ";
    for(int i=0;i<8;i++)
    {
        cout<<a[i]<<"   ";
    }

    cout<<endl;
    cout<<"快排1:  ";
    QuickSort(b,0,7);
    for(int i=0;i<8;i++)
    {
        cout<<b[i]<<"   ";
    }

    cout<<endl;
    cout<<"快排2:  ";
    QuickSort1(c,0,7);
    for(int i=0;i<8;i++)
    {
        cout<<c[i]<<"   ";
    }

    return 0;
}

3. 选择排序

  • 3.1 直接选择排序

选出剩下的未排序记录中的最小记录,然后直接与数组中第i个记录交换

性质:不稳定

空间代价:  Θ(1)

时间代价 : 比较次数:Θ(n2)    交换次数:n-1     总时间代价:Θ(n2)

void SelectionSort(int Data[],int n)
{
    for(int i=1;i<n;i++)
    {
        int k=i-1;//假定第i-1个为序列中最小的元素
        for(int j=i;j<n;j++)
        {
            if(Data[j]<Data[k])//记录最小元素的下标
                k=j;
        }
        if(k!=i-1)//如果k改变
        {
            int t=Data[k];
            Data[k]=Data[i-1];
            Data[i-1]=t;
        }
    }
}
  • 3.2 堆排序

基于最大值堆来实现

  • 性质:不稳定
  • 建堆:Θ(n) 删除堆顶重新建:Θ(log n)
  • 一次建堆,n次删除堆顶
  • 总时间代价为Θ(nlog n) 空间代价为Θ(1)
template <class Record>
void sort(Record Array[], int n)  { 
	int i;
	MaxHeap<Record> max_heap = MaxHeap<Record>(Array,n,n); // 建堆
	// 算法操作n-1次,最小元素不需要出堆
	for (i = 0; i < n-1; i++)		 	
		// 依次找出剩余记录中的最大记录,即堆顶
		max_heap. RemoveMax();		     
}
/*最大堆的相关代码*/
/*定义堆,封装初始化,插入,删除栈顶元素的操作*/
#include<iostream>
using namespace std;
class Maxheap
{
private:
    int* heap;
    int n;
    int cs;
public:
    Maxheap()
    {
        heap=NULL;
        n=0;
        cs=0;
    }
    Maxheap(int maxsize=100)
    {
        heap=new int[maxsize];
        n=maxsize;
        cs=0;
    }
    bool Insert(int x)
    {
        cs++;
        heap[cs-1]=x;
        int i=cs-1;
        shiftup(i);
    }
    void shiftup(int i)
    {
        int parent,temp;
        while(i>0)
        {
            parent=(i-1)/2;
            if(heap[i]>heap[parent])
            {
                temp=heap[i];
                heap[i]=heap[parent];
                heap[parent]=temp;
                i=parent;
            }
            else
                break;
        }
    }
    void initialize(int a[],int na)
    {
        for(int i=0;i<na;i++)
            heap[i]=a[i];
        cs=na;
        for(int j=(cs-2)/2;j>=0;j--)
            shiftdown(j);
    }
    void shiftdown(int i)
    {
        int lchild,rchild,temp;
         while(1)
         {
             lchild=2*i+1;
             rchild=2*i+2;
             if(lchild<cs)
             {
                 if(rchild<cs&&heap[lchild]<heap[rchild])
                 {
                     if(heap[i]<heap[rchild])
                     {
                         temp=heap[i];
                         heap[i]=heap[rchild];
                         heap[rchild]=temp;
                         i=rchild;
                     }
                     else
                        break;
                 }
                 else
                 {
                     if(heap[i]<heap[lchild])
                     {
                         temp=heap[i];
                         heap[i]=heap[lchild];
                         heap[lchild]=temp;
                         i=lchild;
                     }
                     else
                        break;
                 }
             }
            else
                break;
         }
    }
    void Delete(int temp)
    {
        int m,p;
        int item;
        m=cs-1;
        for(int i=0; i<cs; i++)
            if(temp==heap[i])
            {
                p=i;
                break;
            }
        item=heap[p];
        heap[p]=heap[m];
        heap[m]=item;
        cs--;
        shiftdown(p);
    }
    void display()
    {
        cout<<"堆内元素为:"<<endl;
        for(int i=0;i<cs;i++)
            cout<<heap[i]<<endl;
    }
};
int main()
{
    Maxheap heap(100);
    int *a,num,temp,x;
    cout<<"输入堆元素个数"<<endl;
    cin>>num;
    a=new int[num];
    cout<<"输入堆元素"<<endl;
    for(int i=0;i<num;i++)
     {
         cin>>temp;
         a[i]=temp;
     }
    heap.initialize(a,num);
    heap.display();
    cout<<"输入要插入的元素"<<endl;
    cin>>x;
    heap.Insert(x);
    heap.display();
    cout<<"输入要删除的元素"<<endl;
    cin>>x;
    heap.Delete(x);
    heap.display();
}

4. 归并排序

简单地将原始序列划分为两个子序列

分别对每个子序列递归排序

最后将排好序的子序列合并为一个有序序列,即归并过程

性质:稳定性:稳定     

算法最坏情况的复杂度为

算法需要的辅助空间 因为需要一个与原始序列同样大小的辅助序列。这是此算法的缺点。        

#include<iostream>
using namespace std;
void Merge(int Data[],int start,int end,int mid)
{
    int len1=mid-start+1;
    int len2=end-mid;
    int i,j,k;
    int *left=new int[len1];
    int *right=new int[len2];
    for(i=0;i<len1;i++)
        left[i]=Data[i+start];
    for(i=0;i<len2;i++)
        right[i]=Data[i+mid+1];
    i=0,j=0;
    for(k=start;k<end;k++)
    {
        if(i==len1||j==len2)
            break;
        if(left[i]<=right[j])
            Data[k]=left[i++];
        else
            Data[k]=right[j++];
    }
    while(i<len1)
        Data[k++]=left[i++];
    while(j<len2)
        Data[k++]=left[j++];
    delete []left;
    delete []right;
}
void MergeSort(int Data[],int start,int end)
{
    if(start<end)
    {
        int mid=(start+end)/2;
        MergeSort(Data,start,mid);
        MergeSort(Data,mid+1,end);
        Merge(Data,start,end,mid);
    }
}
int main()
{
    int b[6]={8,4,5,6,2,1};
    cout<<"归并排序:"<<endl;
    MergeSort(b,0,5);
    for(int i=0;i<6;i++)
        cout<<b[i]<<" ";
    return 0;
}

5. 基数排序

假设长度为n的序列         R={ r0,r1,…,rn-1 }     

记录的排序码K包含d个子排序码         K=(kd-1,kd-2,…,k1,k0 )

R对排序码(kd-1,kd-2,…,k1,k0 )有序

对于任意两个记录R i,R j (0 ≤ i< j ≤ n-1),都满足(k i ,d-1,k i ,d-2, …,k i ,1,k i,0 ) ≤    (k j ,d-1,k j, d-2,…,k j,1,k j,0 )

其中k d-1称为最高排序码 k 0称为最低排序码

基数排序分为两类:高位优先法MSD,低位优先法LSD

低位优先法LSD:

从最低位k0开始排序 对于排好的序列再用次低位k1排序;

依次重复,直至对最高位kd-1排好序后,整个序列成为有序的

这是一个分、收;分、收;…;分、收的过程 比较简单,计算机常用

性质:稳定性:稳定(一直前后有序)

特点:不用比较和移动,改用分配和收集,时间效率高!

时间复杂度为:O (mn) 空间复杂度:O(n)

6. 外部排序

  • 6.1 置换选择排序

  • 6.2 多路归并

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值