归并排序,选择排序,和它们的组合

归并排序,选择排序,和它们的组合

1.选择排序

 1 void Selection(int *a, int left, int right)//输入数组a,要排序的左端点left和右端点right
 2 {
 3     int temp;//用于交换数组元素的temp
 4     for (int i = left; i <= right-1; i++)//遍历左端点left到右端点right-1的元素
 5     {
 6         for (int j = i+1; j <= right; j++)//将最小值j与第i个元素交换
 7         {
 8             if (a[i]>a[j])//如果第i个元素大于第j个元素,交换两元素
 9             {
10                 //交换
11                 temp = a[i];
12                 a[i] = a[j];
13                 a[j] = temp;
14             }
15             
16         }
17     }
18 }

 

2.归并排序

2.1.1 什么是归并排序
用下面一张图能清晰的描述归并排序

2.2.1 递归部分
void mergeSort(int *a, int left, int right) //输入数组a,要排序的左端点left和右端点right
{
    if (left<right) //若左端点left小于右端点right,则需要继续排序
    {
        int mid = (left+right)/2; //找出左右端点的中点
        mergeSort(a, left, mid); //对左端点到中点的部分递归
        mergeSort(a, mid+1, right); //对中点到右端点的部分递归,记得中点应加一防止排序部分重复
        merge(a, left, mid, right); //对左端点到右端点的部分排序,排序部分在下文2.2.2
    }
}
2.2.2 排序部分
void merge(int *a, int left, int mid, int right)//输入数组a,要排序的左端点left和右端点right,中点mid
{
    //建立两个数组储存终点两端的元素
    int nL = mid - left + 1; //左端的长度
    int nR = right - mid; //右端的长度
    int aL[nL+1]; //建立左端数组
    int aR[nR+1]; //建立右端数组
    for (int i = 0; i < nL; i++)//将数组a左端的元素赋给左端数组
    {
        aL[i] = a[left + i];
    }
    for (int i = 0; i < nR; i++)//将数组a右端的元素赋给右端数组
    {
        aR[i] = a[mid + i + 1];
    }
    aL[nL] = INF;//设置左端数组的最后一个元素为哨兵
    aR[nR] = INF;//设置右端数组的最后一个元素为哨兵

    //将左端数组和右端数组的元素排序填入数组a
    int i = 0;
    int j = 0;
    for (int k = left; k < right + 1; k++)//遍历数组a左端点left到右端点right+1,用于填入元素
    {
        
        if ( aL[i] <= aR[j] )//若左端数组的元素小于右端数组的元素,将左端数组的第i个元素填入数组a
        {
            a[k] = aL[i];
            i++;
        }
        else                //若左端数组的元素大于右端数组的元素,将右端数组的第j个元素填入数组a
        {
            a[k] = aR[j];
            j++;
        }
        /*
        若任意一端数组元素到最后一个,
        该数组的最后一个元素哨兵将恒大于另一数组最后一个之前的元素
        使另一数组的元素陆续填入数组a
        */
    }
    
}

 

3归并排序与选择排序的组合

// 我们用 mergeToSelectionNum 来确定何时将归并排序转到选择排序
void mergeSelectionSort(int *a, int left, int right, int mergeToSelectionNum)
{
    //主要修改这一部分
    if ( left < right )//若左端点left小于右端点right,则需要继续排序
    {
        if ( right - left > mergeToSelectionNum )//当左右端点中元素的数量大于mergeToSelectionNum时,进行归并排序
        {
            int mid = (left+right)/2;//找出左右端点的中点
            mergeSelectionSort(a, left, mid, mergeToSelectionNum);//对左端点到中点的部分递归
            mergeSelectionSort(a, mid+1, right, mergeToSelectionNum);//对中点到右端点的部分递归,记得中点应加一防止排序部分重复
            merge(a, left, mid, right);//对左端点到右端点的部分进行归并排序
        }
        else                                //否则,进行选择排序
        {
            Selection(a, left, right);//对左端点到右端点的部分进行选择排序
        }
    }
}

下面是完整代码

#include <stdio.h>
#define MAX 10000  //数组元素最大值
#define INF 2147483647  //哨兵,不超过int范围

void sort(int*, int, int);//排序初始化
void mergeSelectionSort(int*, int, int, int);//归并排序与选择排序的组合
void merge(int*, int, int, int);//归并排序
void Selection(int*, int, int);//选择排序
void pri(int*,int);//输出数组

int main()
{    
    //建立数组a,判断何时将归并排序转到选择排序的mergeToSelectionNum
    int a[MAX];
    int mergeToSelectionNum;
    int n,i;
    //输入数组a,mergeToSelectionNum
    scanf("%d",&n);
    for (i=0;i<n;i++) scanf("%d",&a[i]);
    scanf("%d",&mergeToSelectionNum);
    //排序
    sort(a, n, mergeToSelectionNum);
    //输出
    pri(a, n);

    return 0;
}

void pri(int *a, int len)//输出
{
    for (int i = 0; i < len; i++)
    {
        printf("%d\t",a[i]);
        if ( i != 0 && (i+1)%10 == 0)printf("\n");//每输出5个元素换行
    }
    
}

void Selection(int *a, int left, int right)//输入数组a,要排序的左端点left和右端点right
{
    int temp;//用于交换数组元素的temp
    for (int i = left; i <= right-1; i++)//遍历左端点left到右端点right-1的元素
    {
        for (int j = i+1; j <= right; j++)//将最小值j与第i个元素交换
        {
            if (a[i]>a[j])//如果第i个元素大于第j个元素,交换两元素
            {
                //交换
                temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
            
        }
    }
}

void merge(int *a, int left, int mid, int right)//输入数组a,要排序的左端点left和右端点right,中点mid
{
    //建立两个数组储存终点两端的元素
    int nL = mid - left + 1; //左端的长度
    int nR = right - mid; //右端的长度
    int aL[nL+1]; //建立左端数组
    int aR[nR+1]; //建立右端数组
    for (int i = 0; i < nL; i++)//将数组a左端的元素赋给左端数组
    {
        aL[i] = a[left + i];
    }
    for (int i = 0; i < nR; i++)//将数组a右端的元素赋给右端数组
    {
        aR[i] = a[mid + i + 1];
    }
    aL[nL] = INF;//设置左端数组的最后一个元素为哨兵
    aR[nR] = INF;//设置右端数组的最后一个元素为哨兵

    //将左端数组和右端数组的元素排序填入数组a
    int i = 0;
    int j = 0;
    for (int k = left; k < right + 1; k++)//遍历数组a左端点left到右端点right+1,用于填入元素
    {
        
        if ( aL[i] <= aR[j] )//若左端数组的元素小于右端数组的元素,将左端数组的第i个元素填入数组a
        {
            a[k] = aL[i];
            i++;
        }
        else                //若左端数组的元素大于右端数组的元素,将右端数组的第j个元素填入数组a
        {
            a[k] = aR[j];
            j++;
        }
        /*
        若任意一端数组元素到最后一个,
        该数组的最后一个元素哨兵将恒大于另一数组最后一个之前的元素
        使另一数组的元素陆续填入数组a
        */
    }
    
}

// 我们用 mergeToSelectionNum 来确定何时将归并排序转到选择排序
void mergeSelectionSort(int *a, int left, int right, int mergeToSelectionNum)
{
    //主要修改这一部分
    if ( left < right )//若左端点left小于右端点right,则需要继续排序
    {
        if ( right - left > mergeToSelectionNum )//当左右端点中元素的数量大于mergeToSelectionNum时,进行归并排序
        {
            int mid = (left+right)/2;//找出左右端点的中点
            mergeSelectionSort(a, left, mid, mergeToSelectionNum);//对左端点到中点的部分递归
            mergeSelectionSort(a, mid+1, right, mergeToSelectionNum);//对中点到右端点的部分递归,记得中点应加一防止排序部分重复
            merge(a, left, mid, right);//对左端点到右端点的部分进行归并排序
        }
        else                                //否则,进行选择排序
        {
            Selection(a, left, right);//对左端点到右端点的部分进行选择排序
        }
    }
}

void sort(int *a, int len,int mergeToSelectionNum)
{
    mergeSelectionSort(a, 0, len-1, mergeToSelectionNum);
}
 
参考文献 算法导论_原书第3版

 

posted @ 2021-11-20 11:13  Spoiler_蓝灯  阅读(66)  评论(0编辑  收藏  举报