排序算法总结(二)

归并排序算法思想是分而治之。下面将对分而治之算法排序做一个简单描述。

//归并排序之分而治之
 void sort(E, n)
 {
	 //对E中的n个元素排序
	 if(n >= k)
	 {
	     i = n / k;
	     j = n - I;
         令A由E的前i个元素组成;
         令B由E的后j个元素组成;
	     sort(A, i);
	     sort(B, j);
	     merge(A, B, E, i, j);//把A和B合并到E
	 }
	  else
	     对E插入排序
}

    当K=2的时候,分而治之的排序算法称之为归并排序,严格的来说是二路归并排序,它的空间复杂度是O(n),时间复杂度为O(nlogn)。

    归并排序函数用一个数组a来存储元素序列E,并用a饭后排序后的序列。当序列E被划为2个子序列时,不必把他们分别复制到A和B中,只需要简单的记录他们在序列E中的左右边界。然后将拍熏后的子序列归并到一个新的数组b中去,最后再将它们赋值回a中。简明的算法如下:、

void mergeSort(T *a, int left, int right)
 {
	 //对数组a[left,right-1]排序
	 if (left < right)
	 {
		 //至少有两个元素
		 int middle = (left + right) / 2;
		 mergeSort(a, left, middle);
		 mergeSort(middle + 1, right);
		 merge(a, b, left, middle, right); //从a到b归并
		 copy(b, a, left, right);//将排序结果复制到a
	 }
}

    仔细观察这个归并程序会发现递归就是不断的将序列进行划分,直到序列的长度变为1,这时在进行归并,长度为1的子序列被归并成长度为2的子序列,长度为2的子序列被归并成长度为4的子序列,如此反复,直到只剩下一个有序序列,例子如下;

初始段  [8][4][5][1][3][6][2][7]
 归并到 b [4 8] [1 5] [3 6] [2 7]
 复制到 a [4 8] [1 5] [3 6] [2 7]
 归并到 b [1 4 5 8] [2 3 6 7]
 复制到 b [1 4 5 8] [2 3 6 7]
 归并到 b [1 2 3 4 5 6 7 8]

    轮流的从a归并到b,从b归并到a,实际上消除了从b复制到a的过程。

用归并排序对数组a[0,n-1]进行排序的最终算法如下:

 template<class T>
 void mergeSort(T a[], int n)
 {
	 T *b = new T[n];
	 int segmentSize = 1;                //记录子序列的长度
	 while (segmentSize < n)
	 {
		 mergePass(a, b, n, segmentSize); //从a到b的归并
		 segmentSize += segmentSize;
		 mergePass(a, b, n, segmentSize); //从b到a的归并
		 segmentSize += segmentSize;
	 }
	 delete[]b;
 }
 //mergePass函数的作用是确定归并子序列的左右边界,真实的归并是在merge中完成
 template <class T>
 void mergePass(T x[], T y[], int n, int segmentSize)
 {
	 int i = 0;
	 while (i <= n - 2 * segmentSize)//i+ 2 * segmentSize<n
	 {
		 //从x到y归并相邻的数据段
		 merge(x, y, i, i + segmentSize - 1, i + 2 * segmentSize - 1);
		 i += 2 * segmentSize;
	 }
	 //少于两个慢数据段时
	 if (i + segmentSize < n)
		 //剩余两个数据段
		 merge(x, y, i, i + segmentSize - 1, n - 1);
	 else
		 //剩余一个数据段
	 for (int j = i; j < n; j++)
	 {
		 y[j] = x[i];
	 }
 }

 template <class T>
 void merge(T c[], T d[], int startOfFirst, int endOfFirst, int endOfSencond)
 {
	 //把相邻的数据段从c归并到d
	 int first = startOfFirst;//第一个数据段的索引
	 int sencond = endOfFirst + 1;//第二个数据段的索引
	 result = startOfFirst;//归并数据段d的索引
	 while (first <= endOfFirst&&sencond <= endOfSencond)
	 {
		 if (c[first] <= c[sencond])
			 d[result++] = c[first++];
		 else
			 d[resulst++] = c[sencond++];
	 }
	 //归并剩余元素
	 if (first>endOfFirst)
	 for (int q = sencond; q <= endOfSencond; q++)
		 d[result++] = c[q];  //当归并序列的左边全不大于右边
	 else
	 for (int q = first; q < endOfFirst; q++)
	 {
		 d[result++] = c[q];  //当归并序列的左边全不小于右边
	 }
 }


//法二
template<class T>
void merge(T data[], T result[],int start, int mid, int end)
 {
	 int i, j, k;
	 i = start;
	 j = mid + 1;                        //避免重复比较data[mid]
	 k = 0;
	 while (i <= mid && j <= end)        //数组data[start,mid]与数组(mid,end]均没有全部归入数组result中去
	 {
		 if (data[i] <= data[j])         //如果data[i]小于等于data[j]
			 result[k++] = data[i++];    //则将data[i]的值赋给result[k],之后i,k各加一,表示后移一位
		 else
			 result[k++] = data[j++];    //否则,将data[j]的值赋给result[k],j,k各加一
	 }
	 while (i <= mid)                    //表示数组data(mid,end]已经全部归入result数组中去了,而数组data[start,mid]还有剩余
		 result[k++] = data[i++];        //将数组data[start,mid]剩下的值,逐一归入数组result
	 while (j <= end)                    //表示数组data[start,mid]已经全部归入到result数组中去了,而数组(mid,high]还有剩余
		 result[k++] = data[j++];        //将数组a[mid,high]剩下的值,逐一归入数组result

	 for (i = 0; i < k; i++)             //将归并后的数组的值逐一赋给数组data[start,end]
		 data[start + i] = result[i];    //注意,应从data[start+i]开始赋值
 }
template<class T>
void merge_sort(T data[], T result[], int start, int end)
 {
	 if (start < end)
	 {
		 int mid = (start + end) / 2;
		 merge_sort(data, result, start, mid);                    //对左边进行排序
		 merge_sort(data, result,mid + 1, end);                  //对右边进行排序
		 merge(data, result,start, mid, end);                    //把排序好的数据合并
	 }
 }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值