MergeSort归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序核心步骤:分解+合并
归并排序的特性总结:
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
对于两端有序序列的合并,在顺序表和链表的OJ题目都学习过了。
整体思想
整体思想
- 分治的思想(分而治之)分治的思想一般都用递归的方式去完成。
- 两个有序序列合并任然是有序序列
- 一段无序序列>>>>>>有序
- 无序序列一直【递推】分割分割直到一个元素(一个元素肯定有序)
- 再【回归】的时候合并即可
- 递归的两部分:子问题&结束条件
整个流程
- 开辟一个tmp新的数组在外部(不可能每次递归都开辟一个新的数组)
- 开始分解[begin,mid] [mid+1,end]
- 分解到不能在分解了(只有一个元素肯定是顺序序列)回归begin = end
- 合并
- 两个有序序列数组和链表合并,依旧有序(取小值尾插)
- 有序序列合并(归并有序序列)
- 利用开辟一个tmp新的数组(不能放到MergeSort函数里面,不可能每次递归都开辟一个新的数组)
- 选小的尾插直到至少序列begin > end
- 若还有序列存在元素直接尾插到tmp后面
- 拷贝到a数组
- 最后记住释放free(tmp)❗
重点突破
- 递归的分解
- 有序序列的合并
- tmp数组的开辟
易错点突破
- 有序序列的合并(&&)
- 拷贝的起始位置
- 下标和元素个数和区间问题
图解分析
代码实现
void _MergeSort(int* a, int* tmp,int begin, int end) { if (begin >= end) { return; } //分割 int mid = (begin + end) / 2; //[begin,mid] [mid+1 ,end] _MergeSort(a, tmp, begin, mid); _MergeSort(a, tmp, mid + 1, end); //合并 int begin1 = begin; int end1 = mid; int begin2 = mid + 1; int end2 = end; int i = begin; //int i = 0; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] < a[begin2]) { tmp[i++] = a[begin1++]; } else//> { tmp[i++] = a[begin2++]; } } while (begin1 <= end1)// { tmp[i++] = a[begin1++]; } while (begin2 <= end2) { tmp[i++] = a[begin2++]; } //合并完成拷贝 //memcpy(a + begin, tmp, (end - begin + 1) * sizeof(int)); memcpy(a + begin, tmp+begin, (end - begin + 1) * sizeof(int)); } void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); if (tmp == NULL) { perror("malloc fail"); return; } _MergeSort(a, tmp, 0, n - 1); free(tmp); }
时间复杂度
时间复杂度O(N*logN)
递归&归并排序VS快速排序
- 快速排序的递归:前序递归
- 归并排序的递归 :后序递归
🙂感谢大家的阅读,若有错误和不足,欢迎指正。