归并排序是一种性能比较高的排序算法,不论是之前学习的堆排序、插入排序、shell排序时间复杂度上来看都是最低的。达到了O(logN)反观之前的堆排序,虽然非常好的利用的二叉堆的性质,如果不重复利用空间的话,可能带来的局面时空间换取时间,当然最好的做法肯定是创建一个大根堆这里很重要的下沉操作,不论是构建大根堆还是后面的deletemax的操作都是其实是对perdown下沉操作的递归调用,堆的底层其实就是数组,只需要在deletemax的时候将size这个有效数据给减一,其实就能保证数组的重复利用,删除掉的再放入原来的数组之中。其中用到了宏定义的函数其实很简单就是leftchild(i)=(2*(i)+1)在调用的时候替换就可以。下沉操作一定要深入理解!
void perdown(int arr[],int i,int n)
{
int child;
int tmp;
for(tmp=arr[i];leftchild(i)<n;i=child)
{
child=leftchild(i);
if(child!=n-1&&arr[child+1]>arr[child])
child++;
if(tmp<arr[child])
arr[i]=arr[child];
else
break;
}
arr[i]=tmp;
}
再来复习一下插入排序,插入排序就不用说了虽然很容易实现也很常用但是性能不高,时间复杂度已经到了O(N^2),依次的拿到了角标1,2,3的数字比较后插入保证了排序后前免得肯定是有序的。
void insertionSort(int arr[], int n)
{
int i;
int p;
int tep;
for (p = 1; p < n; p++)
{
tep = arr[p];
for (i = p; i > 0 && arr[i - 1] > tep; i--)
{
arr[i] = arr[i - 1];
}
arr[i] = tep;
}
}
这里还有一个序偶的概念,其实就是可以估算算法的下界有多少个逆序就代表着插入排序需要执行的交换次数。这样来看的话时间复杂度O(I+N),I其实是原始数组里面的逆序数,那插入排序也可以按照线性时间来执行。再看看shell排序其实我觉得shell排序可以看作插入排序的一种方法,只不过它优化了该方法,因为它添入了一个增量序列,其实就是隔了多少位进行排序,但是最后一定要回到增量为1其实就是最后要执行一次插入排序,效率高是因为最后一次插入排序的时候前面的排序已经让逆序的情况比较少了!可以了解一下通过数学方法可以知道最好的序列是{1,5,19,41,109…},shell排序的性能对数以万计的N完全可以接受。这是shellSort的核心代码。
void shellSort(int arr[], int n)
{
int i, j, increment;
int tep;
for (increment = n / 2; increment > 0; increment /= 2)
{
for (i = increment; i < n; i++)
{
tep = arr[i];
for (j = i; j >= increment; j -= increment)
{
if (tep < arr[j - increment])
arr[j] = arr[j - increment];
else
break;
}
arr[j] = tep;
}
}
}
最后来看看今天学习的归并排序,首先归并排序的思想就很重要,为什么可以时间复杂度性能比较好?因为采用了分治算法,分而治之,数组中下标在比较中间的位置开始往左边分成一个数组,然后往右边再到末尾组成一个数组,三个计数器对应数组的开始端,将分开的两个数组中较小的拷贝到新的数组里面。递归的应用一定要理解递归在其中的深奥!就是在不停的化成更小的数组对半分对半分类比一下折半查找法。
void MSort(int arr[], int temparr[], int left, int right)
{
int center;
if (left < right)
{
center = (left + right) / 2; //4 2 1 0
//递归调用一直在化小 更小的数组
MSort(arr, temparr, left, center);
MSort(arr, temparr, center+1, right);
Merge(arr, temparr, left, center + 1, right);
}
}
void Merge(int arr[], int temparr[], int Lpos, int Rpos, int Rightend)
{
int i, leftend, numelements, tmppos;
leftend = Rpos - 1;
tmppos = Lpos;
numelements = Rightend - Lpos + 1;
while (Lpos <= leftend && Rpos <= Rightend)
{
if (arr[Lpos] <= arr[Rpos])
temparr[tmppos++] = arr[Lpos++];
else
temparr[tmppos++] = arr[Rpos++];
}
while (Lpos <= leftend)
temparr[tmppos++] = arr[Lpos++];
while (Rpos <= Rightend)
temparr[tmppos++] = arr[Rpos++];
for (i = 0; i < numelements; i++,Rightend--)
{
arr[Rightend] = temparr[Rightend];
}
}