【C++】排序算法

/******************************************************************
** 文件名: sort.cpp
** 创建人: zzvn
** 日 期: 2012-10-25
** 修改人:
** 日 期:
** 描 述: 线性表排序
** 版 本: v1.0
******************************************************************/
#include <string.h>
#include <stdlib.h>

/*****************************************************************
** 函 数 名: instertion_sort
** 输   入: int[] arr,int size
** 输   出: int*
** 功能描述: 插入排序(非原地)
**          将一个数组的元素依次跟copy数组中的元素从右向左比较,
**          如果大于则向插入到该元素的位置,否则继续向左移动
**          时间复杂度:O(n^2)  
**          最坏情况:数组从大到小顺序排列
**          最有情况:数组从小到大顺序排列,时间复杂度为O(n)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-10-25
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
int* instertion_sort(int arr[],int size)
{
    int *arr_copy=(int*)malloc(sizeof(int)*size);
    for (int i=0;i<size;i++)
    {
        if (i==0)//如果是第一个元素,直接放入copy数组
        {
            arr_copy[0]=arr[0];
        }
        else
        {
            //从第i-1个元素向左比起,因为从i往后的元素还未赋值
            for (int j=i-1;j>=0;j--)
            {
                //copy数组元素比要插入的元素大,将copy元素向后挪
                if (arr_copy[j]>arr[i])
                {
                    arr_copy[j+1]=arr_copy[j];
                } 
                else  //copy数组元素比要插入的元素小,将copy元素放入当前位置
                {
                    arr_copy[j+1]=arr[i];
                    break;
                }

                //如果已经移到copy数组的最左边仍没有找到比其小的元素
                //则其为最小元素,将该元素放在copy数组的最左头部
                if (j==0)
                {
                     arr_copy[j]=arr[i];
                     break;
                }
            }
        }
    }
    return arr_copy;
}


/*****************************************************************
** 函 数 名: instertion_sort
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 插入排序(原地排序)
**          将一个数组从第二个元素开始(记为j),依次与其前j-1个元素进行
**          比较,如果大于则将其插入进去,如果小于则继续向左移动直到数组
**          头部
**          时间复杂度:O(n^2)  
**          最坏情况:数组从大到小顺序排列
**          最有情况:数组从小到大顺序排列,时间复杂度为O(n)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-10-25
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
void instertion_sort2(int arr[],int size)
{
    int tmp_el;
    for (int i=0;i<size;i++)
    {
        tmp_el=arr[i];
        for (int j=i;j>=0;j--)
        {
            //如果已经移动到头部或者检查到当前元素比要插入的元素小则将元素插入
            if (j==0||arr[j-1]<tmp_el)
            {
                arr[j]=tmp_el;
                break;
            } 
            else  //如果当前元素比要插入的元素大则将当前元素向右挪
            {
                 arr[j]=arr[j-1];
            }
        }
    }
}


/*****************************************************************
** 函 数 名: merge_sort
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 合并排序(递归)
**          分为三步:分解,排序,合并
**          分解排序时间复杂度为n,合并复杂度为n,n+n=2n=>O(n)
**          时间复杂度:O(n)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-10-25
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
void merge_sort(int arr[],int size)
{
    if (size==1)
    {
        return;
    } 
    else
    {
        //计算每个子数组所需的长度
        int size1=size/2;
        int size2=size-size1;

        //申请子数组内存空间
        int* arr_copy1=(int*)malloc(sizeof(int)*size1);
        int* arr_copy2=(int*)malloc(sizeof(int)*size2);

        if(arr_copy1==NULL||arr_copy2==NULL)
            throw "memory allocate faild";

        //copy数据到子数组
        memcpy(arr_copy1,arr,sizeof(int)*size1);
        memcpy(arr_copy2,arr+size1,sizeof(int)*size2);

        //递归调用,对子数组进行排序
        merge_sort(arr_copy1,size1);
        merge_sort(arr_copy2,size2);

        //合并子数组
        int pos1=0,pos2=0,pos=0;
        while ((pos1+pos2)<size) //循环两个子数组
        {
            //如果两个子数组都没有循环完毕
            if (pos1<size1&&pos2<size2)     
            {
                if (arr_copy1[pos1]<arr_copy2[pos2])
                {
                    arr[pos]=arr_copy1[pos1];
                    pos1++;
                } 
                else
                {
                    arr[pos]=arr_copy2[pos2];
                    pos2++;
                }
            }
            else //如果有一个子数组已经循环完毕
            {  
                if (pos1<size1) //arr_copy2循环完毕
                {
                    arr[pos]=arr_copy1[pos1];
                    pos1++;
                } 
                else //arr_copy1循环完毕
                {
                    arr[pos]=arr_copy2[pos2];
                    pos2++;
                }
            }
            pos++;
        }
        
        //释放内存
        free(arr_copy1);
        free(arr_copy2);
    }
}

/*****************************************************************
** 函 数 名: merge_sort2
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 合并排序(循环)
**          len_t以其2的指数级别增长,1(2^0),2(2^1),4(2^2),8(2^3),16(2^4)....
**          当合并4个元素时需循环至len_t为2,即循环2次(对应2^1=4),而2^1是<=4/2的最大数字
**          (4比较特殊,因为lg4正好是整数,理论上只需要两次,代码中实际计算了3次,不影响结果
**          其他lgN正好为整数的也会多一次,如果有必要可以在代码中对该类数字加以区分),
**          当合并19个元素时需循环至len_t为16,即循环5次(对应2^4=16),而2^3是<=19/2的最大数字,
**          当合并1000个元素时需循环至len_t为512,即循环10次(对应2^9=512),而2^8是<=1000/2的最大数字,
**          由上,可知循环次数为lg(size/2)+2
**          (所以我们使用do..while循环,区分第一次循环和最后一次循环,使多走两次)
**
**          以上所有lg(size/2)都取整,即lg(5/2)==lg(6/2)==lg(7/2)==1
**          
**          最外层的while运行次数如上,简化为logn,内两层while运行次数n(pos从0增长到n)
**          故时间复杂度为n*logn
**          时间复杂度:O(nlogn)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-10-25
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
void merge_sort2(int arr[],int size)
{        
    int is_first=1;                     //是否第一次运行
    int len_t=1,len_t1,len_t2;          //子数组长度
    int pos=0;                          //在主数组中排序的偏移
    int *arr_copy1,*arr_copy2;          //子数组指针
    int pos1,pos2;                      //子数组偏移量
    do 
    {
        if (is_first==1)
        {
            is_first=0;
        }
        else
        {
            len_t*=2;
            pos=0;
        }

        while(pos<size)
        {
            //申请子数组空间
            arr_copy1=(int*)malloc(sizeof(int)*len_t);
            arr_copy2=(int*)malloc(sizeof(int)*len_t);

            //当剩余元素不足len_t时,需重新计算每个子数组len_t的大小
            len_t1=pos+len_t>=size?size-pos:len_t;
            len_t2=pos+len_t1+len_t>=size?size-pos-len_t1:len_t;
            memcpy(arr_copy1,arr+pos,sizeof(int)*len_t1);
            memcpy(arr_copy2,arr+pos+len_t,sizeof(int)*len_t2);

            //合并
            pos1=pos2=0;
            while(pos1<len_t1||pos2<len_t2)
            {
                //如果两个子数组都没有循环完毕
                if (pos1<len_t1&&pos2<len_t2)     
                {
                    if (arr_copy1[pos1]<arr_copy2[pos2])
                    {
                        arr[pos]=arr_copy1[pos1];
                        pos1++;
                    } 
                    else
                    {
                        arr[pos]=arr_copy2[pos2];
                        pos2++;
                    }
                }
                else //如果有一个子数组已经循环完毕
                {  
                    if (pos1<len_t1) //arr_copy2循环完毕
                    {
                        arr[pos]=arr_copy1[pos1];
                        pos1++;
                    } 
                    else //arr_copy1循环完毕
                    {
                        arr[pos]=arr_copy2[pos2];
                        pos2++;
                    }
                }
                pos++;
            }
            free(arr_copy1);
            free(arr_copy2);
        }
    }while(len_t<=size/2);
}

/*****************************************************************
** 函 数 名: heap_sort
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 堆排序(摘自维基)
**          1.建堆(从叶级节点依次向堆顶建堆)
**          2.取堆顶元素与堆尾元素交换
**          3.重排堆,因堆有序,重排时只需比较被替换的节点的子树,故效率较高
**          4.重复2、3步,迭代整个数组
**           时间复杂度:O(nlogn)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-11-02
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
void sift(int d[], int ind, int len);
void heap_sort(int d[], int n)
{
    //#初始化建堆, i从最后一个非叶子节点开始#%
    for(int i = n / 2; i >= 0; i--)
        sift(d, i, n);

    for(int i = 0; i < n; i++)
    {
        //#交换#%
        int t = d[0];
        d[0] = d[n - i - 1];
        d[n - i - 1] = t;

        //#筛选编号为0 #%
        sift(d, 0, n - i - 1);

    }
}
void sift(int d[], int ind, int len)
{
    //#置i为要筛选的节点#%
    int i = ind;

    //#c中保存i节点的左孩子#%
    int c = i * 2 + 1; //#+1的目的就是为了解决节点从0开始而他的左孩子一直为0的问题#%

    while(c < len)//#未筛选到叶子节点#%
    {
        //#如果要筛选的节点既有左孩子又有右孩子并且左孩子值小于右孩子#%
        //#从二者中选出较大的并记录#%
        if(c + 1 < len && d[c] < d[c + 1])
            c++;
        //#如果要筛选的节点中的值大于左右孩子的较大者则退出#%
        if(d[i] > d[c]) break;
        else
        {
            //#交换#%
            int t = d[c];
            d[c] = d[i];
            d[i] = t;
            //
            //#重置要筛选的节点和要筛选的左孩子#%
            i = c;
            c = 2 * i + 1;
        }
    }

    return;
}

/*****************************************************************
** 函 数 名: quick_sort
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 快速排序
**           取数组最后一个数字作为主元数字,与数组中每一个数字进行比较,
**           使数组中位于主元数字前的数字都小于主元数字,位于主元数字之后
**           的数字都大于主元数字
**           平均时间复杂度:O(nlogn)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-11-02
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
void quick_sort(int arr[],int size)
{
    if (size<=1)
    {
        return;
    }
    int j=0,m=size-1,tmp;
    for (int i=0;i<size-1;i++)
    {
        if (arr[i]<arr[m])
        {
            tmp=arr[j];
            arr[j]=arr[i];
            arr[i]=tmp;
            j++;
        }
    }
    tmp=arr[j];
    arr[j]=arr[m];
    arr[m]=tmp;
    j++;
    quick_sort(arr,j-1);
    quick_sort(arr+j,size-j);
}


/*****************************************************************
** 函 数 名: counter_sort
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 计数排序(线性排序)
**           直接统计数组中每一个数组应该放的位置下标,将其放在正确位置
**           时间复杂度:O(n)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-11-02
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
#define FLAG 0x80
#define INT_FLAG 0x80808080
void counter_sort(int arr[],int size)
{
    if (size<=0)
    {
        return;
    }
    int *arr_copy=(int*)malloc(sizeof(int)*size);
    memset(arr_copy,FLAG,size*sizeof(int));

    int max,min;
    max=min=arr[0];
    for (int i=1;i<size;i++)
    {
        if (min>arr[i])
            min=arr[i];
        if (max<arr[i])
            max=arr[i];
    }
    max=max+1;
    int size_pos=max-min;

    int *pos=(int*)malloc(sizeof(int)*size_pos);
    memset(pos,0,size_pos*sizeof(int));

    for (int i=0;i<size;i++)
    {
        pos[arr[i]-min]++;
    }

    for (int i=1;i<size_pos;i++)
    {
        pos[i]+=pos[i-1];
    }
    //为保证计数排序的稳定性,逆序遍历整个数组
    //因为输入数组中第一个遇到的数字在输出数组中是放到同数字组中最后面的
    for (int i=size-1;i>=0;i--)
    {
        int p=pos[arr[i]-min]-1;
        while (arr_copy[p]!=INT_FLAG&&p>0)
            p--;
        arr_copy[p]=arr[i];
    }

    memcpy(arr,arr_copy,size*sizeof(int));
    free(pos);
    free(arr_copy);
}

/*****************************************************************
** 函 数 名: bucked_sort
** 输   入: int[] arr,int size
** 输   出: void
** 功能描述: 桶排序(线性排序)
**          将整个序列划分为n个桶,然后遍历序列中的数字,使每个数字落在
**          相应范围的桶内,对桶内数据进行排序,依次获取桶中元素,即为排序
**          好的序列
**          期望时间复杂度:O(n)
** 全局变量:
** 调用模块:
** 作   者: zzvn
** 日   期: 2012-11-02
** 修   改:
** 日   期:
** 版    本: v1.0
****************************************************************/
#define BUCKED_LEN 100
typedef struct node  
{
    int num;
    struct node* next;
} Node;
void bucked_sort(int arr[],int size)
{
    if (size<=0)
    {
        return;
    }

    //统计输入序列的最大,最小值
    int max,min;
    max=min=arr[0];
    for (int i=1;i<size;i++)
    {
        if (min>arr[i])
            min=arr[i];
        if (max<arr[i])
            max=arr[i];
    }

    int bucked_size=(max-min)/BUCKED_LEN+1;//划分为BUCKED_LEN个桶,每个桶的宽度为bucked_size,深度为链表结构
    Node *bucked[BUCKED_LEN];//桶数组
    memset(bucked,0,BUCKED_LEN*sizeof(Node*));

    min=-min;
    //将数组元素放入相应桶
    for (int i=0;i<size;i++)
    {
       //为了处理负数,如果min<0,在计算桶位置时,统一将序列整个右移|min|个位置
       //为了处理区间数(如:200~300),如果min>0,在计算桶位置时,统一将序列整个左移min个位置
       int p = (arr[i]+min)/bucked_size;
       if (bucked[p]==NULL)
       {
           Node* cnode=(Node*)malloc(sizeof(Node));
           cnode->num=arr[i];
           cnode->next=NULL;
           bucked[p]=cnode;
       }
       else
       {
           Node* fnode=bucked[p];
           while (fnode->next!=NULL)
               fnode=fnode->next;
           Node* cnode=(Node*)malloc(sizeof(Node));
           cnode->next=NULL;
           cnode->num=arr[i];
           fnode->next=cnode;
       }
    }

    //将桶中元素依次放回原数组
    int pos=0;//原数组偏移量
    for (int i=0;i<BUCKED_LEN;i++)
    {
        Node *nnode,*cnode = bucked[i];
        if(cnode==NULL)
            continue;
        
        //对链表进行遍历,确定桶中元素的个数
        int sub_size=1;
        while (cnode->next!=NULL)
        {
            sub_size++;
            cnode=cnode->next;
        }

        if (sub_size==1)//桶中只有一个元素
        {
            arr[pos]=cnode->num;
            free(cnode);
            pos++;
        } 
        else//桶中有超过一个元素
        {
            //为了利用已有的排序程序,这里将链表复制到一个数组中
            int* sub_arr=(int*)malloc(sizeof(int)*sub_size);
            cnode = bucked[i];
            int tmp_pos=0;
            while (cnode!=NULL)
            {
                 sub_arr[tmp_pos++]=cnode->num;
                 nnode=cnode->next;
                 free(cnode);
                 cnode=nnode;
            }

            //调用计数排序对子数组进行排序
            counter_sort(sub_arr,sub_size);
            memcpy(arr+pos,sub_arr,sub_size*sizeof(int));
            pos+=sub_size;
        }
    }
}


Test:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <Windows.h>
#include <cmath>

using namespace std;

extern void print(int [],int,bool);
extern void random(int arr[],int size);
extern void test_sort(int arr[],int size);

extern int* instertion_sort(int[],int);//插入排序
extern void instertion_sort2(int[],int);//插入排序
extern void merge_sort(int[],int);//合并排序(递归)
extern void merge_sort2(int arr[],int size);//合并排序(循环)
extern void heap_sort(int arr[],int size);//堆排序
extern void quick_sort(int arr[],int size);//快速排序

extern void counter_sort(int arr[],int size);//计数排序
extern void bucked_sort(int arr[],int size);//桶排序

int main(void)
{
    //初始化数组
    int size=19;
    int *arr=(int*)malloc(sizeof(int)*size);
    random(arr,size);
    print(arr,size,false);
        
    //插入排序1
    //clock_t t=clock();
    int *arr_sort=instertion_sort(arr,size);
    //cout<<(clock()-t)/1000.0<<endl;
    print(arr_sort,size,true);
    //test_sort(arr_sort,size);
    
    //插入排序2
    //instertion_sort2(arr,size);
    //print(arr,size,true);

    //合并排序
    //merge_sort2(arr,size);
    //print(arr,size,true);
    
    //堆排序
    //t=clock();
    //heap_sort(arr,size);
    //cout<<(clock()-t)/1000.0<<endl;
    //print(arr,size,true);
    //test_sort(arr,size);

    //快速排序
    //quick_sort(arr,size);
    //print(arr,size,true);

    //计数排序
    //counter_sort(arr,size);
    //print(arr,size,true);

    //桶排序
    bucked_sort(arr,size);
    print(arr,size,true);

    //清理
    free(arr_sort);
    free(arr);
    system("pause");
}

void random(int arr[],int size)
{
    srand((unsigned int)time(NULL));
    for (int i=0;i<size;i++)
    {
        arr[i]=rand()%200+7200;
    }
}

void test_sort(int arr[],int size)
{
    for (int i=0;i<size-1;i++)
    {
        if (arr[i+1]-arr[i]<0)
        {
            cout<<":::"<<i<<':'<<arr[i]<<','<<i+1<<':'<<arr[i+1];
            break;
        }
    }
    cout<<endl;
}

void print(int arr[],int size,bool isTest)
{
    for (int i=0;i<size;i++)
    {
        cout<<arr[i]<<',';
    }
    if (isTest)
    {
         test_sort(arr,size);
    } 
    else
    {
        cout<<endl;
    }
   
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值