归并排序,选择排序,和它们的组合
归并排序,选择排序,和它们的组合
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版