排序(數據結構 · 希爾排序 · 快速排序 · 堆排序 · 歸併排序)

我們在學C的時候就已經學過好幾種排序,但是數據結構還是在最後面詳細介紹了不同算法的排序方式所需要的時間和空間成本,還新增了幾個C基礎設計裏面沒有的算法,這一章很重要,因爲算法本就是數據結構的核心之一,好的程序員應該多理解不同算法和它們的時間複雜度和空間複雜度。特別想說一下堆的排序,裏面的調試板塊我都打出來了,可以直接刪掉註釋來當調試用。快速排序和希爾排序是數據結構裏的新知識點。

#include<stdio.h> 
#include<stdlib.h>
void show();
int Swap(int *a, int *b)  //交換位置函數  
{ 
	int t=*a;
	*a=*b;
	*b= t;
} 

int maopao(int *a, int n) //冒泡排序  
{ 
	int P, i;
	bool flag;
	
	for( P=n-1; P>=0; P--) 
	{  
	flag=false; //標記循環中是否發生交換,若無,則說明整個xu有序  
	for( i=0; i<P; i++ ){ //一起冒泡 
		if(a[i]>a[i+1]) 
		{   
		Swap(&a[i], &a[i+1]);  
		flag = true;//標記發生了交換  
		}  
	} 
	if(flag==false) break;//若全程無交換,則跳出循環  
	}  
	for(i=0; i<n; i++) 
	printf("%d ", a[i]); 
	printf("\n");	 
} 
 
int xuanze(int *a, int n) //選擇排序  
{ 
    int i, j, t=0; 
    for(i=0; i<n; i++) 
	scanf("%d", &a[i]);
	
	for( i=0; i<n-1; i++)  
	for(j=i+1; j<n; j++) 
    if(a[i]>a[j]) 
    Swap(&a[i], &a[j]); 
        
	for( i=0; i<n; i++) 
	printf("%d ", a[i]); 
	printf("\n");
} 

int PercDown(int *a, int P, int n) //堆排序--2 
{ 
	//將N個元素的數組中a[i]爲根的子堆調爲最大堆 
	int Parent, Child, j; 
	int X; 
	X = a[P]; //取出根結點 
	//printf("\na[(P)%d] = %d\n", P, X);  
	for( Parent=P; (Parent*2+1)<n; Parent=Child ) 
	{ 
		Child = Parent*2+1; 
		if((Child!=n-1)&&(a[Child]<a[Child+1])) 
		    Child++; //Child指向左右子較大者  
	/*for( j=0; j<n; j++ ) 
		printf("_%d_",a[j]); 
		printf("(*)\n");*/
		if(X>=a[Child]) break;  //找到了合適的位置 
		else a[Parent] = a[Child];  //下慮X 
		/*for( j=0; j<n; j++ ) 
		printf("_%d_",a[j]); 
		printf("($)\n"); */ 
		} 
	a[Parent] = X;  
	
    /*for( j=0; j<n; j++ ) 
	printf("_%d_",a[j]); 
	printf("(!)\n"); */
	
} 

int HeadSort(int *a, int n)  //堆排序 --1 
{ 
	int i; 
	for( i=n/2-1; i>=0; i--) 
    PercDown( a, i, n ); 
     
    for( i=n-1; i>0; i-- ) 
    { 
    //刪除最大項 
    //printf("^^^^\n");
    Swap(&a[0], &a[i]);  
    PercDown(a, 0, i); 
	} 
} 

void InsertionSort(int *a, int N)  //插入排序  
{ 
	int i, P;
	int t;
	
	for( P=1; P<N; P++){ 
		t = a[P]; //取出未排序序列中的第一個元素  
		for(i=P; i>0&&a[i-1]>t; i--) 
		{ 
		//printf("前:a[%d]=%d ,a[%d]=%d\n", i, a[i], i-1, a[i-1]);
		a[i] = a[i-1]; 
		//printf("後:a[%d]=%d ,a[%d]=%d\n", i, a[i], i-1, a[i-1]);
		} 
	a[i] = t;//放進合適的位置 
	//printf("位置:a[%d]=%d\n", i, a[i]); 
	} 
} 

void shellsort(int *a, int n) //希爾排序 
{ 
    int Si, D, P, i; 
	int t; 
	int SE[] = {929, 505, 209, 41, 19, 7, 5, 3, 1, 0}; 
	
	for( Si=0; SE[Si]>=n; Si++ ) 
	for( D=a[Si]; D>0; D=a[++Si]) 
	for( P=D; P<n; P++) 
	{ 
	     t = a[P]; 
		 for( i=P; i>=D&&a[i-D]>t; i-=D) 
		 a[i] = a[i-D]; 
	     a[i] = t; 
	} 
} 

int Median3(int *a, int Left, int Right) 
{ 
	int Center = (Left+Right)/2;
	if(a[Left]>a[Center]) 
	   Swap(&a[Left], &a[Center]); 
	if(a[Left]>a[Right]) 
	   Swap(&a[Left], &a[Right]); 
	if(a[Center]>a[Right]) 
	   Swap(&a[Center], &a[Right]); 
	Swap(&a[Center], &a[Right-1]);//將基準pivot藏到右邊 
	
	return a[Right-1];//返回基準 pivot  
} 

void Qsort(int *a, int Left, int Right) 
{ 
	int pivot, cutoff, Low, Hight;
	
	if(cutoff<=Right-Left){ //如果序列元素充分過多,進入快排 
	pivot = Median3(a, Left, Right);//選基準 
	Low=Left; Hight=Right-1;
	while(1){ //將序列中比基準小的移到基準左邊,打的移動到右邊  
		while( a[++Low]  <pivot ); 
		while( a[--Hight]>pivot ); 
		if(Low>Hight) Swap(&a[Low], &a[Hight]); 
		else break; 
	} 
	Swap( &a[Low], &a[Right-1] ); 
	Qsort(a, Left,  Low-1); 
	Qsort(a, Low+1, Right); 
    } 
    else InsertionSort(a+Left, Right-Left+1); //元素太少,用簡單排序  
} 
///////////////////////////////////////////////////////////////////////////////一下爲歸併排序代碼  
void Merge( int *A, int *TmpA, int L, int R, int RightEnd )
{
	int LeftEnd, NumElements, Tmp; 
	int i; 
	
	LeftEnd = R-1;
	NumElements = RightEnd-L+1;
	Tmp = L;
	
	while( L<=LeftEnd&&R<=RightEnd ){ 
		if( A[L]<=A[R] ) TmpA[Tmp++] = A[L++]; //將左邊的元素複製到TmpA  
		else TmpA[Tmp++] = A[R++]; //將右邊的元素複製到TmpA  
	} 
	while( L<=LeftEnd ) //複製剩下的元素 
	TmpA[Tmp++] = A[L++]; 
	while( R<=RightEnd ) 
	TmpA[Tmp++] = A[R++]; 

    for( i=0; i<NumElements; i++, RightEnd-- )
    A[RightEnd] = TmpA[RightEnd]; //將有序的TmpA[]複製到A[]中  
    
}

void Msort( int *a, int *TmpA, int L, int RightEnd ) 
{ 
	int Center; 
	
	if( L<RightEnd ) 
	{ 
		Center = (L+RightEnd)/2; 
		Msort( a, TmpA, L, Center);//遞歸解決左邊  
		Msort( a, TmpA, Center+1, RightEnd);//遞歸解決右邊  
		Merge( a, TmpA, L, Center+1, RightEnd);//合併兩段有序序列 
	} 
} 

void MergeSort( int *A, int N ) 
{ 
	int *TmpA; 
	TmpA = (int *)malloc(N*sizeof(int)); 	
	if( TmpA != NULL ) 
	{ 
		Msort(A, TmpA, 0, N-1 ); 
		free(TmpA); 
	}else printf("空間不足!"); 
	
} 
////////////////////////////////////////////////////////////////////////////////以上爲歸併排序代碼          
int main() 
{  
	int i, n, m=-1; 
    show();
	while( m!=0 ) 
	{ 
	scanf("%d", &m); 
	switch(m) 
	{ 
		case 1:{ 
			printf("輸入問題規模:"); 
			scanf("%d", &n); 
			int a[n]; 
			for( i=0; i<n; i++)
			scanf("%d", &a[i]);
			maopao( a, n ); 
			break; 
		} 
		case 2:{ 
			printf("輸入問題規模:"); 
			scanf("%d", &n); 
			int a[n]; 
			xuanze(a, n); 
			break; 
		}  
		case 3:{ 
			printf("輸入問題規模:");
			scanf("%d", &n);
			int a[n];
			
			for( i=0; i<n; i++)
			scanf("%d", &a[i]);
			
			HeadSort( a, n);
			
			for( i=0; i<n; i++)
			printf("%d ",a[i]);
			printf("\n");
			
			break;
		} 
		case 4:{
			printf("輸入問題規模:");
			scanf("%d", &n);
			int a[n];
            for( i=0; i<n; i++)
			scanf("%d", &a[i]);
			shellsort( a, n );
			for( i=0; i<n; i++)
			printf("%d ",a[i]); 
			break;
		}
		case 5:{
			printf("輸入問題規模:");
			scanf("%d", &n);
			int a[n];
			for( i=0; i<n; i++)
			scanf("%d", &a[i]);
			InsertionSort( &*a, n);
			for( i=0; i<n; i++) 
			printf("%d ", a[i]); 
			printf("\n"); 
			break;
		}
		case 6:{ 
			printf("輸入問題規模:"); 
			scanf("%d", &n); 
			int a[n]; 
			
			for( i=0; i<n; i++) 
			scanf("%d", &a[i]);
			
			Qsort( a, 0, n-1); 
			
			for( i=0; i<n; i++ ) 
			printf("%d ", a[i]); 
			printf("\n"); 
			
			break; 
		} 
		case 7:{
			printf("輸入問題規模:");
			scanf("%d", &n);
			int a[n];
			for( i=0; i<n; i++)
			scanf("%d", &a[i]);
			MergeSort(a, n); 
			for( i=0; i<n; i++)
			printf("%d ",a[i]);
			printf("\n");
			break;
		}
	} 
	printf("\n請選擇菜單:"); 
	} 
	return 0; 
} 

void show() 
{ 
	printf("              |.......選擇你需要的排序方法......|\n"); 
	printf("              |          1.冒泡排序             |\n"); 
	printf("              |          2.選擇排序             |\n"); 
	printf("              |          3.堆排序               |\n"); 
	printf("              |          4.希爾排序             |\n"); 
	printf("              |          5.插入排序             |\n"); 
	printf("              |          6.快速排序             |\n"); 
	printf("              |          7.歸併排序             |\n"); 
	printf("              |          8.基數排序             |\n");
	printf("              |          0.結束                 |\n");
	printf("              |.................................|\n"); 
	printf("選擇菜單:"); 
} 
//5 4 3 2 1 10 9 8 7 6

給一下堆排序在刪掉註釋後的截圖:







你可以根據圖中序號的選擇方式,結合完全二叉樹來觀察排序方式。

下面的演示:



編譯器:DEV C++

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章