归并排序:
1.分治思想 :
归并排序用到了一种称为分治法的设计方法,分治法的思想就是将原问题的分解成为几个规模小的但是类似于原问题的子问题,递归的求解子问题,之后再合并子问题的解,来组成原问题的解。
2.原理 :
根据分治方法的思想,归并排序算法的基本操作分为三大步:
(1):分解
(2):解决问题
(3):合并结果。
以一个需要排序的数组为例,分解也就是不停地递归分解问题,直到问题的规模降为1,然后开始合并。
3.C语言实现 :
void merge(int from, int mid, int to, int* a)
{
//新建了一个辅助的数组,用来暂时寄存merge时的数组
int left_top, right_top, index, count, *t;
index = 0;
//左半边的等待merge数组的顶端
left_top = from;
//右半边的等待merge数组的顶端
right_top = mid + 1;
//整个merge后的数组长度
count = to - from;
t = (int*)malloc( (count + 1) * 2);
//不断的从左右两个需要merge的子数组中取出顶端较小的数字
//直到其中一个数组取尽
while ((left_top <= mid) && (right_top <= to))
{
if (a[left_top] < a[right_top])
{
t[index++] = a[left_top++];
}
else
{
t[index++] = a[right_top++];
}
}
//下面两个while循环
//作用是将还没有取尽的数组元素放入merge时的暂存数组中
while (left_top <= mid)
{
t[index++] = a[left_top++];
}
while (right_top <= to)
{
t[index++] = a[right_top++];
}
//将merge暂存数组的结果给原数组
for (int i = 0; i <= count; i++)
{
a[from++] = t[i];
}
}
void merge_sort(int from, int to, int* a)
{
if (from < to)
{
//分解问题
int mid = (from + to) / 2;
merge_sort(from, mid, a);
merge_sort(mid + 1, to, a);
//开始合并
merge(from, mid, to, a);
}
}
void main()
{
int count, *p;
printf("please input the count :");
scanf_s("%d", &count);
p = (int *)malloc(count * 2);
printf("\nplease input the number to be sorted : \n");
for (int i = 0; i < count; i++)
{
scanf_s("%d", p+i);
}
merge_sort(0, count-1, p);
for (int i = 0; i < count; i++)
{
printf("%d ", p[i]);
}
system("pause");
}
4.分析:
上面实现的MERGE_SORT算法在输入的数字数量为奇数和偶数的时候都可以正常的工作,但是这里我们为了方便分析效率,将问题的规模假设为2的幂,在每次分解问题的时候都可以分成n/2。我们按照三个步骤来分析效率:
分解:
分解步骤只需要找到数组的中间位置,因此时间是常数O(1)。
解决:
分解之后,需要递归的求解两个规模为n/2的子问题,花费时间为2T(n/2)。
合并:
将两个长度为n/2是的数组合并成一个长度为n的数组,相当于将n个元素都遍历了一遍,时间复杂度为O(n)。
因此我们可以写出T(n)和T(n/2)的关系表达式,T(n) = 2T(n/2) + cn(c为常数);后面会分析可以从这个式子导出T(n) = O(nlgn)。