一.前提
1.为简单起见,讨论从小到大的整数排序
2.只讨论基于比较的排序(< = >有定义)
3.只讨论内部排序
4.稳定性:任意两个相等的数据,排序前后相对位置不发生变化
二.归并排序原理
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序
三.分类
1.算法1-用递归
(1)实现
//合并时用到的临时数组
static int[] temp;
//array:要排序的数组
//begin:要排序的数组在在array中的起始位置
//end:要排序的数组在在array中的末尾位置
//把数组分为两半,排好左边的,再排好右边的,合并
static void Merge_Sort(int[] array, int begin, int end){
//递归结束条件
if(begin >= end) return;
//如果temp=null,表示是第一次进行分割,初始化temp。temp只初始化一次
//即只会有一个临时数组
if(null == temp) temp = new int[array.length];
//递归排好左右两边,合并
int middle = (end - begin)/2;
Merge_Sort(array, begin, begin+middle);
Merge_Sort(array, begin+middle+1, end);
merge(array, begin,end);
}
//lbegin:合并的数组在array数组中的最左位置
//end:合并的数组在array数组中的最右位置
static void merge(int[] array, int lbegin, int end){
//i:temp数组的下标
int i = 0;
//rbegin:合并要合并两个数组,rbegin是右边数组的第一个元素的位置
int rbegin = (lbegin+end)/2 + 1;
int middle = rbegin;
//合并到临时数组里
while(lbegin < middle && rbegin <= end){
if(array[lbegin] <= array[rbegin])
temp[i++] = array[lbegin++];
else temp[i++] = array[rbegin++];
}
//如果左右数组元素个数不相等,则把剩余的元素直接按顺序放入temp数组里
while(lbegin < middle){
temp[i++] = array[lbegin++];
}
while(rbegin <= end){
temp[i++] = array[rbegin++];
}
//把temp数组中的元素放回array数组中
//即表示array数组这部分合并完成
while(i > 0){
array[end--] = temp[i-1];
i--;
}
}
(2)时间复杂度
//递归了log(n)次
Merge_Sort(array, begin, begin+middle);
Merge_Sort(array, begin+middle+1, end);
//合并的时间复杂度为O(n)
merge(array, begin,end);
- 总的时间复杂度:T(n) = O(nlogn)
- 没有最好最坏,固定的nlogn
(3)空间复杂度
temp = new int[array.length];
- 额外且只创建了一个新的数组:S1(n) = O(n);
- 递归时压入栈的数据占用的空间:S2(n) = O(logn);
- 空间复杂度:两者相加,取最高次的。S(n) = O(n)
2.算法2-用非递归
思路:
1.最开始把每个元素看作一个有序的子序列,然后把相邻的两个子序列归并
2.注意:可以只开一个数组,然后两个数组重复存数据