归并排序
时间复杂度:O(nlogn)
空间复杂度:O(n)
算法思想:
将要排序的数组进行不断切分,切分到最后再逐步进行归并操作。
将数组不断切分为2份,层数:log n,如果每层的merge操作都是n,时间复杂度就是O(nlogn)。
归并过程:
开辟一个辅助空间,建立3个索引,一个是当前merge的位置,一个是头一个有序数组操作到的位置,一个是第二个有序数组操作到的位置。
比较两个有序数组第一个位置的两个数,1小,将1放到数组中,第二个有序数组位置+1,
2和4比较,4小,那么2赋值到第二个位置
实现:
#include <iostream>
#include <algorithm>
using namespace std;
//对arr[l,mid]和arr[mid+1,r]两部分做归并排序
void merge(int arr[], int l,int mid,int r){
//辅助空间,存储两部分值
int tmp[r-l+1];
for(int i=0; i<r-l+1; i++)
tmp[i] = arr[l+i];
int i=l,j=mid+1;
for(int k=l; k<=r; k++)
{
if(i>mid) //当i超过mid,第一个数组没有值
{
arr[k] = tmp[j-l];
j++;
}
else if(j>r)//当i超过r,第二个数组没有值
{
arr[k] = tmp[i-l];
i++;
}
else if(tmp[i-l]<tmp[j-l]) //第一部分值小于第二部分值
{
arr[k] = tmp[i-l];
i++;
}
else //第二部分值小于第一部分值
{
arr[k] = tmp[j-l];
j++;
}
}
}
//对arr[l,r]之间的数进行归并排序
void mergeSort(int arr[], int l,int r){
if(l >= r)
return;
int mid = (l+r)/2;
mergeSort(arr,l,mid);
mergeSort(arr,mid+1,r);
merge(arr,l,mid,r); // 归并
}
在两种特殊的情况下:
1.近乎有序的数组进行排序
2.有序的数组进行排序
以上两种情况下,插入排序算法的性能比归并排序算法的性能还要好
优化
1.对于数组近乎有序的情况
如果前面有序数组中所有的数都小于后面有序数组中的所有数,说明整个数组是有序的,就不需要进行归并
这点改进后,对于近乎有序的数组
改进前:
改进后:
2.当归并排序递归到一定层数的时候,可以转而使用插入排序来提高一些性能。
原因在于:1.当元素数据比较小的时候,数组近乎有序的概率会比较大,此时使用插入排序,就会有优势。2.在时间复杂度前面有一个常数级别,插入排序比归并排序小。当n小于一定程度时,插入排序会比归并排序快一些。
自底向上的归并排序算法
思想:
两个元素一个小段进行归并
四个元素一个小段进行归并
8个元素一起归并
N=8
sz=1 |
i=0 |
Merge(0,1) |
|
i=2 |
Merge(2,3) |
|
i=4 |
Merge(4,5) |
|
i=6 |
Merge(6,7) |
sz=2 |
i=0 |
Merge(0,1)(2,3) |
|
i=4 |
Merge(4,5)(6,7) |
sz=4 |
i=0 |
Merge(0,3)(4,7) |
越界问题:i+size-1可能会越界
i+2size-1可能会越界
可以使用这个算法对链表进行O(nlogn)的排序