排序算法分析归纳总结

 排序方法分类:

按照策略 划分内部排序方法为五大类:

插入排序选择交换归并分配排序。

下面我了归纳上述类型的排序算法和其他经典算法。

以下默认升序!!




插入排序:

直接插入排序:

排序思想:

将所有数据放入数组R[1 ... n]中,初始状态R[1]是有序区,无序区为R[2 .. n],从R[1... n]经过比较依次插入R[1]有序区中,得到一个有序序列。


那么按照定义来写:

void InsertSort(int a[],int n)
{
	int i,j,k;
	for(i = 1;i< n;i++)  //初始的有序区为a[0],所以待插入数从a[1]开始。
	{
		for(j = i-1;j >= 0;j--)       //在有序区中找到a[1]插入的位置
		{
			if(a[i]>a[j])
				break;
		}
		if(j!= i-1)                    //如果找到的位置是原位,则说明待插入数也将加入有序区。
		{
			int temp = a[i]; 
			for(k = i;k>j;k--)   //标记待插入的数的值,然后从此一直到要插入的位置进行移位
			{
				a[k] = a[k-1];
			}
			a[k+1] = temp;             //移位完成后插入数据。
		}
	}
}

代码优化:

首先,我们可以将if(a[j]< a[i])这条语句加入到第二层for循环的判断中,考虑到如果,要插入的数据比有序区的最后一位要小,是不用执行移位、插入操作的,所以得到优化过后的代码:

void InsertSort(int a[],int n)
{
	int i,j,k;
	for(i = 1;i< n;i++)
	{
		if(a[i] <a[i-1])
		{
		int temp = a[i];
		for(j = i-1;j >= 0 && a[j] > a[i];j--)
		{
			a[j+1] = a[j];
		}
		a[j+1] = temp;
		}
	}
}

算法分析:

  对于具有n个数据,要进行n-1趟排序。

  各种状态下的时间复杂度:


│ 初始状态        正序        反序      无序(平均)  


│ 第i趟的关键       1          i+1      (i-2)/2  
比较次数      


│总比较次数        n-1      (n+2)(n-1)/2     ≈n2/4    


│第i趟记录移动次数    0        i+2         (i-2)/2  
 
│总的记录移动次数     0      (n-1)(n+4)/2     ≈n2/4     


│时间复杂度        0(n)      O(n2)       O(n2)    


注意:
     按递增有序,简称"正序"。
      按递减有序,简称"反序"。 

算法的空间复杂度分析:
算法所需的辅助空间是一个监视哨,辅助空间复杂度S(n)=O(1)。是一个就地排序。

直接插入排序的稳定性:
直接插入排序是稳定的排序方法






希尔排序:

排序思想:取一个小于n 的正整数d1,然将数据分为d1组,所有相距d1距离的数据元素为同一组元素,在同一个组内进行直接插入排序,然后取第二个增量d2(小于d1)重复此操作,直至所取的增量为1,即:所有的元素放在同一组 内进行直接插入排序。

该方法的实质:分组插入排序。要知道 的是:希尔排序只会在最后一趟排序后生成有序 序列,在此之前 ,希尔排序并不会生成 有序区 ,但是每趟排序之后,都会逼近 有序序列。

code:

严格 按照定义:

void ShellSort(int a[],int n)
{
	int temp;
	int i,j;
	int gap = n/2;
	while(gap > 0)
	{
		for(i = gap;i<n;i++)
		{
			temp = a[i];
			for(j = i- gap;j>=0&&temp > a[j];)
			{
				a[j+ gap] = a[j];
				j = j- gap;
			}
			a[j+gap] = temp;
		}
		gap = gap/2;
	}
}

以n=10的一个数组49, 38, 65, 97, 26, 13, 27, 49, 55, 4为例:

第一次:gap = n/2 = 5;

49 38 65 97 26 13 27 49 55 4

1     2  3   4   5   1   2   3   4  5 

在这里相同数字标记的为同一组元素,那么久将这10个数据分为 {49, 13},{38,27},{65,49},{97,55},{26,4}这五组数据,分别在组内进行直接插入排序,   得到排序结果{13,49},{27,38},{49,65},{55,97},{4,26}.还原到原来的同一组是数据为:

13,27,49,55,4,49,38,65,97,26


 第二次:gap = gap/2 = 2;

13,27,49,55,4,49,38,65,97,26

 1    2   1  2   1  2  1  2    1   2

分为两组:{13,49,4,38,97,} 和{27,55,49,65,26}这两组数组,在组内分别执行直接插入排序得到:{4,13,38,49,97},{26,27,49,55,65},还原到同一组:

4,26,13,27,38,49,49,55,97,65,


第三次排序:gap = gap/2 = 1

4,26,13,27,38,49,49,55,97,65,

1 1    1   1   1   1    1   1   1  1 

那么这就意味一所有数据处于同一组数据中,直接一趟直接插入排序得到最终结果:

得到最终数据:

4 13 26 27 38 49 49 55 65 97


算法分析:

希尔排序的时间复杂度是所取增量的序列的函数,而增量的选取是无法确定的,所以造成希尔排序的时间复杂度是难以确定的,但是一般认为希尔排序的平均时间复杂度为O(n的1.3次方);

希尔排序与直接插入排序的比较:
希尔排序的时间性能优于直接插入排序,原因:

1.数据基本有序的时候直接插入排序所需要的比较和移动次数较少

2.当n值较少时,n的平方与n 的差距也较小,所有直接插入排序算法的最好时间复杂度为O(n),最坏时间复杂度为O(n 的平方)

3.希尔排序当初始时增量大,分组较多,导致直接插入排序较快,而当增量减小时,分组较少,但是每一句都趋近与有序序列,导致直接插入  排序较快,

因此希尔排序在直接插入排序中有很大的改进,

希尔排序是不稳定的。





交换排序:

冒泡排序:

排序思想 :

总体来说:对于一组数据,先遍历一次找到最小的,放在R[0]处,然后第二次遍历找到第二小的放在R[1]处。依次类推。

细节来说:

给一组数据:,5 2 3 4 1,此时:标记 i = a[0]  = 5;

第一次排序:

5 比 2 小,则2 于 5 交换,i = a[0 = 2]得到:2 5 3 4 1 

第二次比较:

2 比3小。不交换位置

第三次比较:

2比四小,不交换位置;

第五次比较:

2 比1 小,交换位置:得到:1 5 3 4 2,

依次类推 最终得到结果:1 2 3 4  5.

即每一次排序都将一个值放在他应在的位置上去。


void BubbleSort(int a[].int n)
{
	int i,j;
	for(i = 0;i< n - 1;i++)
	{
		for(j = i+1;j<n;j++)
		{
			if(a[i] > a[j])
			{
				int temp = a[i];
				a[i] = a[j];
				a[j]  = temp;

			}
		}
	}
}

其他方式:

既然,冒泡排序算法每次将最无序区中最小的元素 上浮到 最前面,称为有序区的一部分,那么每次 比较的都是有序区以后 的部分,相当于说,我们可以 从数据末尾到无序区开始范围内 进行比较,(倒着来)

code:


void BubbleSort(int a[].int n)
{
	int i,j;
	for(i = 0;i < n - 1;i++)
	{
		for(j = n-1;j > i;j--)
		{
			if(a[i] > a[j])
			{
				int temp = a[i];
				a[i] = a[j];
				a[j] = temp;

			}
		}
	}
}

代码优化:

这样倒着来的时候,我们可以将算法进一步的优化。这是因为此时每趟排序中 都 会有 位置的交换操作发生,如果没有发生,则说明无序区的所有位置都相对是有序的,即:无序区是有序的,那么可以说明整个数据是有序的(因为有序区的最后的一个数是小于无序区最小元素的值的),那么我们可以设置一个变量来检测此行为:


void BubbleSort(int a[].int n)
{
	int i,j;
	for(i = 0;i < n - 1;i++)
	{
		bool  isOver = true;
		for(j = n-1;j > i;j--)
		{
			if(a[i] > a[j])
			{
				int temp = a[i];
				a[i] = a[j];
				a[j] = temp;
				isOver  =  false;
			}
		}
		if(isOver == true)
		{
			return ;
		}
	}
}

分析:

若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C
  
和记录移动次数M均达到最小值:
  
   
所以,冒泡排序最好的时间复杂度
   

  若初始文件是反序的,需要进行(n-1)
  
趟排序。每趟排序要进行
 (n-i) 
次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:

冒泡排序的最坏时间复杂度为
  
综上,因此冒泡排序总的平均时间复杂度为
  
稳定性:
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。



快速排序:

排序思想:
快速排序的实质思想是分治法。
1.找出一个元素作为基准点(一般都是以第一个元素为基准点)
2.分区中 将比基准点小的元素转到左边,比基准点大的数调整到右边,而基准点调整到排序完成后的正确位置,
3.递归执行2操作,直到将其他n-1个元素调整到它应该在的正确位置上去,最终完成快速排序行程有序序列。
(快速排序的重点是调整基准点的位置和操作分区。)


为了能让快速排序的过程看起来简单易懂,我在这里举一个例子
现在有5颗树,由于先天原因,树长的高度参差不齐,本宝宝于是看不下去了,要把树挖了重新栽树,让它们从低到高排列。
算法模拟实现:
给出树的高度:22 4 96  30 13,米。这7颗树给下标0,1,2,3,4,基准点为 第一颗树:

初始时i指向第一棵树,j指向最后一棵树,X记录第一棵树的高度并将第一棵树挖出来:

   0    

   1  

     2   

     3   

     4   

   22

   4 

    96

    30

    13


此时: i= 0;j= 4 ; X = a[i] = 22;
首先从j开始从后到前找到一个比基准点X低的树,即:当j= 4时符合要求,那么将第5棵树挖出来栽在第一个坑里:

   0    

   1  

     2   

     3   

     4   

   13

   4 

    96

    30

    13



此时:a[0] = a[4];  i++;  (i= 1)
这样第一个坑种上了树,但是最后一个位置是没有树的,那么我们从i的位置,即第二棵树开始找到比X高的树:当i= 2时满足,于是把第二颗树挖出来,种到最后一个位置,

   0    

   1  

     2   

     3   

     4   

   13

   4 

    96

    30

    96



此时:a[4] =a[2] ;j--;(j = 3)现在第三个位置是空的,那么从j往前找比22低的树,发现找到临界点i== j,则将基准点归位于a[2] = 22;

   0    

   1  

     2   

     3   

     4   

   13

   4 

    22

    30

    96



得到数据: 13 4 22 30 96 ,会发现基准点22 的位置就是排序终结的正确位置,下一步操作是对a[0...1] 与 a[3...4 ]这两个子区间重复此操作,如果还没有得到最后的排序结果, 递归此操作。

总结:
1. i = l,j = r,基准数为首元素,记录X首元素,
2.j --从后往前找比X小的数,赋值给a[i];
3.i ++从前往后找比X大的数,赋值给a[j],
重复2,3步,直到 i== j ;让a[i] = X.即:基准数归位。
最后递归完成排序操作。
说了这么久了:
code:

void QuickSort(int a[], int l, int r)
{
	if (l < r){
		int i = l;
		int j = r;
		int X = a[l];
		while (i < j)
		{
			while (i < j && a[j] >= X)
				j--;
			if (i < j)
			{
				a[i++] = a[j];
			}
			while (i < j && a[i] < X)
			{
				i++;
			}
			if (i < j)
			{
				a[j--] = a[i];
			}
		}
		a[i] = X;
		QuickSort(a, l, i - 1);
		QuickSort(a, i + 1, r);
	}
}

有的书上介绍的时候采取的是中间点为基准点:只需要在代码中加入一个数据转换操作即可:

void QuickSort(int a[], int l, int r)
{
	if (l < r){
		int i = l;
		int j = r;
		//Swap(a[l], a[(l + r) / 2]); //首元素和中间元素调换位置
		int X = a[l];
		while (i < j)
		{
			while (i < j && a[j] >= X)
				j--;
			if (i < j)
			{
				a[i++] = a[j];
			}
			while (i < j && a[i] < X)
			{
				i++;
			}
			if (i < j)
			{
				a[j--] = a[i];
			}
		}
		a[i] = X;
		QuickSort(a, l, i - 1);
		QuickSort(a, i + 1, r);
	}
}

算法分析:
快速排序-时空复杂度:
快速排序每次将待排序数组分为两个部分,在理想状况下,每一次都将待排序数组划分成等长两个部分,则需要logn次划分。
而在最坏情况下,即数组已经有序或大致有序的情况下,每次划分只能减少一个元素,快速排序将不幸退化为冒泡排序,所以快速排序时间复杂度下界为O(nlogn),最坏情况为O(n^2)。在实际应用中,快速排序的平均时间复杂度为O(nlogn)。
快速排序在对序列的操作过程中只需花费常数级的空间。空间复杂度S(1)。
但需要注意递归栈上需要花费最少logn最多n的空间。





选择排序:

直接选择排序:

排序思想:
n个数据经过n-1次排序成为有序序列,
第一趟排序:数据分为有序区(开始时为空),和无序区R[0……n-1];从无序区中找到最小的数据元素与无序区的第一位交换,构成有序区R[0...0];无序区:R[1...n-1];
第i趟排序:有序区:R[0...i-1],无序区R[i。。n-1],那么该趟排序从无序中找到最小的元素与无序区的第一位交换位置,并将无序区的第一个元素划分到有序区。
这样,n个数据通过n-1趟排序得到有序序列。

void SelectSort(int a[], int n)
{
	int i, j, k;
	for (i = 0; i < n -1;i++)
	{
		k = i;
		for (j = i + 1; j < n; j++)
		{
			if (a[j] < a[k])
			{
				k = j;
			}
		}
		int temp = a[i];
		a[i] = a[k];
		a[k] = temp;
	}
}


算法分析:
在直接选择排序中,共需要进行n-1次选择和交换,每次选择需要进行 n-i 次比较 (1<=i<=n-1),而每次交换最多需要3次移动,因此,总的比较次数C=(n*n - n)/2,
总的移动次数 3(n-1).由此可知,直接选择排序的时间复杂度为 O(n2) (n的平方),所以当记录占用字节数较多时,通常比直接插入排序的执行速度快些。
由于在直接选择排序中存在着不相邻元素之间的互换,因此,直接选择排序是一种不稳定的排序方法。




堆排序:


堆有好几种堆形式,但是由于二叉堆在运用时很常见,一般将二叉堆简称为堆。
堆排序定义:

n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):  (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1。

堆的性质:

由定义可以得出,堆的父节点总是大于等于或者小于等于它的子节点;每个节点的左儿子和有儿子又都是一个二叉堆;

二叉堆分为两种:

1.最大堆:父节点的值大于等于子任何一个子节点的值时的二叉堆

2.最小堆:父节点的值小于等于任何一个子节点的值时的二叉堆。

最小堆和最小堆的示例:



下面以最大堆为例子:

最大堆对应 的就是挑选 最大元素,将数组看为一颗完全二叉树,那么此时我们 可以利用完全二叉树的父亲与孩子节点的关系来选择最大元素。

首先我们 把数据放在R[1...n]中(为了与二叉树的顺序储存结构相一致,堆排序的开始下边标从1开始),把每个数据看做一个我节点,那么首元素R[1]则为完全二叉树的根,以下元素依次每层从左到右排列在数组 中,R[i]的左孩子是R[2i],右节点R[2i+1];

堆排序的首要任务是 建堆,假设现在完全二叉树中的某个节点i,(左子树2i,右子树2i +1),我们需要将它的左子树和右子树和此i节点比较选取最大者当做这三个数的父节点,当然,这样比较后交换位置后可能会造成下一级的堆被破坏,所以我们需要接着按照上述方法进行下一级的建堆,直到完全二叉树中的节点i构成堆为止。对于任意一颗完全二叉树,i取[n/2]~1,反复利用此方法建堆。大数上调,小数下降,

调整堆的方法:

void Sift(int a[], int low, int high)
{
	int i = low;              
	int temp = a[i];
	int j = 2 * i;
	while (j <= high)
	{
		if (j < high &&a[j] < a[j + 1])   //判断两个孩子节点哪个大(要注意此时判断的j是不能加上=号的。因为j代表左孩子节点,理论上还有j+1)
		{
			j++;
		}
		if (a[j] > temp)                  //如果最大的孩子节点比父节点大的话,
		{
			a[i] = a[j];                  //<span style="font-family: Arial, Helvetica, sans-serif;">让其成为父节点</span>
			i = j;                         //接着该节点往下进行建堆
			j = 2 * i;
		}
		else
			break;
	}
	a[i] = temp;
}

在初始化堆构造后,最大的数一定位于根节点,将其放在序列的最后,也就是第一个数和最后一个数进行交换,由于最大的元素已经归位,所以待排序的数据中就减少了一位。但由于根节点的改变,这n-1个节点不一定还是堆,所以要再次初始化建堆,初始化建堆之后其根节点为次大的数据,将它放在序列的倒数第二位。如此反复的进行操作,知道完全二叉树只剩下一个根为止。

实现堆排序的算法如下:

void HeapSort(int a[], int n)
{
	int i;
	for (i = n / 2; i >= 1; i--)
	{
		Sift(a, i, n);
	}
	for (i = n; i >= 2; i--)
	{
		int temp = a[1];
		a[1] = a[i];
		a[i] = temp;
		Sift(a, 1, i - 1);
	}
}

算法模拟:

数据: 72 73 71 23 94 16 5 68

对应的完全二叉树1.:


模拟堆排:

初始时 i= n/2 = 4;j = i*2;那么68比13小,交换位置,j > n结束。


 i 执行i--;i = 3;j = i*2 = 6,那么16 和5都比71小,直到j > n都未发生交换,结束,


4.i 执行 i--;i = 2;j = i*2 =4;那么 94比68和73大,执行73与94的交换,直到j>n;结束。

 

执行i--;i = 1;j = i*2 = 2;那么94比71 和72 大,成为父节点,直到j >n结束:



那么该建堆就结束了,可以看到最大的值是在根节点,我们将94与13交换,然后截断94的连接点,那么94就相当于已经归位,我们需要做的是将切断后的数据重新建堆,

找出次大的数。一直重复此操作,就可以得到有序序列。

完整代码:

void Sift(int a[], int low, int high)
{
	int i = low;
	int temp = a[i];
	int j = 2 * i;
	while (j <= high)
	{
		if (j < high &&a[j] < a[j + 1])
		{
			j++;
		}
		if (a[j] > temp)
		{
			a[i] = a[j];
			i = j;
			j = 2 * i;
		}
		else
			break;
	}
	a[i] = temp;
}
void HeapSort(int a[], int n)     //使用时调用此函数即可。
{
	int i;
	for (i = n / 2; i >= 1; i--)
	{
		Sift(a, i, n);
	}
	for (i = n; i >= 2; i--)
	{
		int temp = a[1];
		a[1] = a[i];
		a[i] = temp;
		Sift(a, 1, i - 1);
	}
}

算法分析:

直接选择 排序中,为了从R[1..n]中选出最大的数据,必须进行n-1次比较,然后在R[2..n]中选出最大的数据需要执行n-2次比较,事实上,这些比较中有很多都是在重复比较。有很多比较可能已已经在n-1次比较中进行了,但由于未保留比较后的结果,所以执行了重复性的比较工作,而堆排序会对比较过后的结果进行保留,(根据上面的图例就可以看出来),减少了一些重复的比较。

时间复杂度:

堆排序的运行时间打过消耗在初始化建堆和重建堆的反复筛选中。二叉树从最底层右边的非终结点开始建堆,将其与其孩子进行比较和可能的交换,对于每个非终结点点来谈,最多进行量次比较和交换数据操作。在排序时(此时已经初始化建堆完成了),第i次去最大值记录重建堆需要O(logi)时间,并且需要取n-1次堆顶记录,所以重建堆的时间复杂度为O(nlogn)。

总体说:堆排序的时间复杂度为O(nlogn);




归并排序:

归并排序是建立在归并操作上的一种排序算法,该算法采用了分治法,是分治法的一种典型的例子。

排序思想:

将已经有序的子序列进行合并,得到完全有序的序列。即对于一组数据,我们先分为若干个子序列,然后让子序列有序,有序后合并子序列形成新的子序列,我们再对新的子序列进行操作使其有序。那么当所有的子序列经过有序合并后最终会形成一组有序序列。


那么什么是归并操作?

归并操作也叫归并算法,指的是将两个顺序序列合并成一个有序序列的方法。

首先我们先说如何实现归并操作:

1.我们先申请一个空间(可以容纳两个已经有序序列总和),用来存在最终结果。

2.然后设置连个标记分别标记两个有序序列的下标,通过这两个标记来比较较小的数据元素放入已经申请好的空间内,并移动下标

3.重复操作操作某一个下标超出序列,然后将另一个序列的剩下元素直接复制到最终空间内。

为了传入参数的简化,我们将两个有序的序列存入一个数组内相邻位置。

code:

void Marge(int a[], int low, int mid, int high)
{
	int i = low, j = mid + 1, k = 0;
	int *r;
	r = (int *)malloc(sizeof(high - low - 1) *sizeof(int));  //申请空间
	while (i <= mid && j <= high)  //如果第一个数组或者第二个数组没有达到尾,继续比较
	{
		if (a[i] <= a[j])
		{
			r[k++] = a[i++];
		}
		else
		{
			r[k++] = a[j++];
		}
	}
	while (i <= mid)  //如果是i标记的数数据元素没有复制完
	{
		r[k++] = a[i++];
	}
	while (j <= high)  //如果是j标记的数据元素没有复制完
	{
		r[k++] = a[j++];
	}
	for (k = 0, i = low; i <= high; k++, i++)  //将临时空间里的数据传给a数组
	{
		a[i] = r[k];
	}
	free(r);
}
那么经过这样的归并操作,得到的最终数据将是有序数据。

那么问题来了:如果我们要对一组无序的数据进行归并排序怎么弄呢?

这里要要用到分治的思想。

分治法:通俗的说,分治法就是将一块领土分解,分解为若干个小块部分,然后将一块块领土占领,被分解的可以是不同的政治派别或者是其他什么的,然后让他们彼此异化。


分治法精髓:

分- 将问题分解为规模更小的子问题。

治- 将这些规模更小的子问题逐个解决。

合- 将已经解决的问题进行合并,最终得出母问题的解。

针对归并排序,我们先来一组数据模拟一下你就能用理解如果用分治法实现的归并排序了。


针对这一问题,我们想将 18 2 20 34 12 32 6 16 通过归并排序使其成为一个有序数组:

初始: (18 2) ( 20 34) (12 32)  (6 14)

第一趟归并:(2 18  20 14) (12 32 6 16)

第2趟归并:(2 18 20 34 6 12 16 32)

第三趟归并: 2 6 12 16 18 20 32 34(得到有序序列)

Marge实现了一次归并,那么现在我们 需要利用Marge解决一趟归并,在某趟归并中,假设各子表的长度为length,(最后一个字表的长度可能 小于length,n/length不一定不剩余),则归并前a[0...n-1]中共有[n/length]个子表,调用Marge函数将相邻一对子表 进行归并时必须要判断子表的个数的奇偶,以及最后一个字表的长度小于length这两种情况,如果子表为奇数,则最后一个字表此次归并轮空,如果是偶数则注意到最后一个子表的上界是 n-1。

code:

void MergePass(int a[], int length, int n)
{
	int i;
	for (i = 0; i + length * 2 - 1 < n; i = i + 2 * length)
	{
		Marge(a, i, i + length - 1, i + 2 * length - 1);
	}
	if (i + length - 1 < n)
	{
		Marge(a, i,i + length - 1, n - 1);
	}
}


在进行二路归并排序的第一趟 ,将待排序的表看作是n个 长度为1的有序序列 ,将这些表两两归并,如果n为偶数,得到n/2个长度为2的子序列,如果为奇数,则最后一个 子表轮空,本趟结束后,最后一个字表的长度 为 1。第二趟排序,将第一趟排序所得的[n/2]个有序的子表两两归并,反复操作,直到得到一个长度为n的有序表。

对应的二路归并算法:

void MergeSort(int a[], int n)
{
	int length;
	for (length = 1; length < n; length = 2 * length)
	{
		MergePass(a, length, n);
	}
}

完整算法:

void Marge(int a[], int low, int mid, int high)
{
	int i = low, j = mid + 1, k = 0;
	int *r;
	r = (int *)malloc((high - low + 1) *sizeof(int)); 
	while (i <= mid && j <= high)  
	{
		if (a[i] <= a[j])
		{
			r[k++] = a[i++];
		}
		else
		{
			r[k++] = a[j++];
		}
	}
	while (i <= mid)  
	{
		r[k++] = a[i++];
	}
	while (j <= high)  
	{
		r[k++] = a[j++];
	}
	for (k = 0, i = low; i <= high; k++, i++)  
	{
		a[i] = r[k];
	}
	free(r);
}
void MergePass(int a[], int length, int n)
{
	int i;
	for (i = 0; i + length * 2 - 1 < n; i = i + 2 * length)
	{
		Marge(a, i, i + length - 1, i + 2 * length - 1);
	}
	if (i + length - 1 < n)
	{
		Marge(a, i,i + length - 1, n - 1);
	}
}
void MergeSort(int a[], int n)
{
	int length;
	for (length = 1; length < n; length = 2 * length)
	{
		MergePass(a, length, n);
	}
}









未完,明天接着写。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值