1. 程式人生 > >歸併排序(遞迴實現和迭代實現)

歸併排序(遞迴實現和迭代實現)

//首先是遞迴實現的方式
#include<stdio.h>

#define MAXSIZE 10

//實現歸併,並把資料都放在list1裡面
void merging(int *list1,int list1_size,int *list2,int list2_size)
{
	int i,j,k,m;
	i = j = k = 0;
	
	int temp[MAXSIZE];
	
	while(i<list1_size&&j<list2_size)
	{
		if(list1[i] < list2[j])
		{
			temp[k++] = list1[i++];
		}
		else 
		{
			temp[k++] = list2[j++];
		}
	}
	//如果有剩下的,那麼說明就是它是比前面的陣列都大的,直接加入就可以了
	while(i<list1_size)
	{
		temp[k++] = list1[i++];
	}
	while(j<list2_size)
	{
		temp[k++] =  list2[j++];
	}
	
	for(m=0; m<(list1_size + list2_size);m++)
	{
		list1[m] = temp[m];
	}
}
void MergeSort(int k[],int n)
{
	
	if(n>1)
	{
		int *list1 = k;			//定義一個指標變數,指向陣列k的地址
		int list1_size = n/2;	//陣列的長度分為本來陣列長度的一半
		int *list2 = k +n/2;	//定義另外一個指標變數,指向陣列k+n/2的地址
		int list2_size = n - list1_size;//長度為剛才總的減去剛才分去那一半
		
		MergeSort(list1,list1_size);	//呼叫陣列本身,相當與遞迴,
		MergeSort(list2,list2_size);	//呼叫陣列本身,相當與遞迴
		merging(list1,list1_size,list2,list2_size);
	}
	
	
	
}

int main(){
	int i,a[10] = {5,2,6,0,3,9,1,7,4.8};
	
	MergeSort(a,10);
	
	printf("排序後的結果是:");
	for(i=1; i<10; i++)
	{
		printf("%d",a[i]);
	}
	
	printf("\n\n");
	
	return 0;
	
}

//歸併排序複雜度分析:一趟歸併需要將待排序列中的所有記錄
//掃描一遍,因此耗費時間為O(n),而由完全二叉樹的深度可知,
//整個歸併排序需要驚醒[log2n],因此,總的時間複雜度為
//O(nlogn),而且這是歸併排序演算法中最好、最壞平均的時間效能
//空間複雜度:由於歸併過程中需要與原始記錄序列同樣數量級的
//儲存空間去存放歸併結果及遞迴深度為log2N的棧空間,因此空間
//複雜度為O(n+logN)
//也就是說,歸併排序是一種比較佔記憶體,但卻效率高且穩定的演算法

沒有最好只有更好,下面介紹一種用迭代方式實現歸併排序的演算法:

非遞迴的方法,避免了遞迴時深度為log2N的棧空間,空間只是用到歸併臨時申請的跟原來陣列一樣大小的空間,並且在時間效能上也有一定的提升,因此,使用歸併排序是,儘量考慮用非遞迴的方法。

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 10

void MergeSort(int k[],int n)
{
	int i,next,left_min,left_max,right_min,right_max;
	//開闢一個與原來陣列一樣大小的空間用來儲存用
	int *temp = (int *)malloc(n * sizeof(int));
	//逐級上升,第一次比較2個,第二次比較4個,第三次比較8個。。。
	for(i=1; i<n; i*=2)
	{
		//每次都從0開始,陣列的頭元素開始
		for(left_min=0; left_min<n-i; left_min = right_max)
		{
			right_min = left_max = left_min + i;
			right_max = left_max + i;
			//右邊的下標最大值只能為n
			if(right_max>n)
			{
				right_max = n;
			}
			//next是用來標誌temp陣列下標的,由於每次資料都有返回到K,
			//故每次開始得重新置零
			next = 0;
			//如果左邊的資料還沒達到分割線且右邊的陣列沒到達分割線,開始迴圈
			while(left_min<left_max&&right_min<right_max)
			{
				if(k[left_min] < k[right_min])
				{
					temp[next++] = k[left_min++];
					
				}
				else
				{
					temp[next++] = k[right_min++];
				}
			}
			//上面迴圈結束的條件有兩個,如果是左邊的遊標尚未到達,那麼需要把
			//陣列接回去,可能會有疑問,那如果右邊的沒到達呢,其實模擬一下就可以
			//知道,如果右邊沒到達,那麼說明右邊的資料比較大,這時也就不用移動位置了
			
			while(left_min < left_max)
			{
				//如果left_min小於left_max,說明現在左邊的資料比較大
				//直接把它們接到陣列的min之前就行
				k[--right_min] = k[--left_max];	
			}
			while(next>0)
			{
				//把排好序的那部分陣列返回該k
				k[--right_min] = temp[--next];		
			}
		}
	}
}

int main(){
	int i,a[10] = {5,2,6,0,3,9,1,7,4,8};
	
	MergeSort(a,10);
	
	printf("排序後的結果是:");
	for(i=0; i<10; i++)
	{
		printf("%d",a[i]);
	}
	
	printf("\n\n");
	
	return 0;
	
}