1. 程式人生 > 其它 >C語言氣泡排序、選擇排序和快速排序

C語言氣泡排序、選擇排序和快速排序

@

目錄

前言

本文簡單介紹了C語言的氣泡排序、選擇排序、快速排序,結合本人的理解與使用做一下記錄。


一、氣泡排序

思想:(以小到大排序為例)假設一個數組有n個數,第一輪遍歷這個陣列的前(n-1)個數,遍歷的時候比較當前位置和下一個位置的數,如果當前位置的數比較大,就跟下一個數交換位置;所以第一輪結束之後,就會把最大的數“沉”到陣列的末尾位置;第二輪遍歷這個陣列的前(n-2)個數,按照前面的方法把第二大的數“沉”到陣列的倒數第二個位置;依此類推,(n-1)輪之後每一個較大的數都按順序沉到了陣列的相應位置。

二、選擇排序

思想:使用for迴圈對陣列進行排序;每迴圈一次,選擇出一個較大的數,然後與
陣列中的第n個位置交換資料,n從0開始。假設這個陣列有n個數據,那麼迴圈(n-1)次即可完成排序。

三、 快速排序

思想:

  1. 首先確定一個target,找到這個target在陣列中的位置;它會把這個陣列分割成兩部分(你可以認為是兩個待排序的區域),並且左邊的數都比它小,右邊的數都比它大;
  2. 這兩個區域都設定了新的target,然後通過遞迴確定左邊這個區域的新target的位置和右邊這個區域的新target的位置;繼續分割繼續遞迴直到區域的資料只有一個為止;
  3. 最終可以實現陣列中的每一個數據的左邊的數都比它小且右邊的數都比它大,從而完成排序。

四、程式碼設計與實現

程式碼設計

編寫一個程式,隨機生成10個整數並賦值給陣列;然後分別使用氣泡排序、選擇排序和快速排序對這個陣列進行排序並打印出排序後的結果。

程式碼實現

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>


void BubbleSort(int *a, int length) ;
void select_sort(int *array, int size);
void quick_sort(int *array, int low, int high);

#define SIZE 10
//#define SELECT_SORT
//#define BUBBLE_SORT
#define QUICK_SORT

int main(void)
{
	int array[SIZE];
	int i;
	
	srand(time(0));	//設定隨機數種子
	//使用隨機數初始化陣列
	for(i = 0; i < SIZE; i++)
	{
		array[i] = rand() % (50-3+1)+3;	//生成[3,50]範圍內的隨機整數
	}
	
	printf("排序之前,陣列為:");
	for(i = 0; i < SIZE; i++)
	{
		printf("%d\t", array[i]);
	}
	printf("\n");

#ifdef SELECT_SORT
	//使用選擇排序對陣列從大到小排序
	select_sort(array, SIZE);
#elif defined BUBBLE_SORT
	//選擇氣泡排序對陣列從小到大排序
	BubbleSort(array, SIZE);
#else
	//使用快速排序對陣列從小到大排序
	quick_sort(array, 0, SIZE-1);
#endif
	printf("排序之後,陣列變為:");
	for(i = 0; i < SIZE; i++)
	{
		printf("%d\t", array[i]);
	}
	printf("\n");



	return 0;
}

//氣泡排序
void BubbleSort(int *a, int length) 
{
	int temp;
	for (int i = 0; i < length - 1; i++)
	{
		for (int j = 0; j < length - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
}

//選擇排序
void select_sort(int *array, int size)
{
	int max;
	int pos;
	int i,j;
	int tmp;

	max = array[0];
	pos = 0;
	for(i = 0; i < size-1; i++)	//迴圈一次就找到一個較大的數,然後與陣列的第n個數據交換位置(n從0開始)
	{
		for(j = i; j <= size-1; j++)
		{
			if(array[j] > max)
			{
				max = array[j];
				pos = j;
			}
		}
		tmp = array[i];
	       	array[i] = array[pos];
		array[pos] = tmp;
		max = array[i+1];
		pos = i+1;
	}
}

//快速排序
void quick_sort(int *array, int low, int high)
{

	if(low < high)
	{
		int i = low;
		int j = high;
		int key = array[low];
		
		while(i < j)	//兩個遊標重合,即可確定key的位置
		{
			while(i < j && array[j] > key)	//從右往左找,找到一個小於等於key的資料,然後與key交換位置
			{
				j--;
			}
			if(i < j)
			{
				array[i++] = array[j];	//把找到的這個數放到key的位置
			}

			while(i < j && array[i] <= key)	//從左往右找,找到一個大於key的資料,然後與key交換位置
			{
				i++;
			}
			if(i < j)
			{
				array[j--] = array[i];	//把找到的這個數放到key的位置
			}
		}

		array[i] = key;
		
		quick_sort(array, low, i-1);
		quick_sort(array, i+1, high);
	}
}

程式碼分析:

首先是使用srand函式設定隨機數種子,time(0)是用來獲取隨機數種子;
然後使用rand函式產生隨機數,賦值給陣列元素;
如果要產生[m,n]範圍內的隨機數num,可用:

int num=rand()%(n-m+1)+m;

其中的rand()%(n-m+1)+m算是一個公式

上面的程式碼可以選擇氣泡排序、選擇排序或者選擇快速排序,可以通過巨集選擇。
我有看過別人寫的快速排序,我覺得有些操作是可以優化的;比如交換資料這一步,因為key的位置有可能一直在變,所以沒必要每次都把key寫到某個位置(過渡位置),只需要確定好最終位置再賦值。

當然,上面這個只是示例程式碼,還可以再封裝、再優化,需要大家再斟酌一下。

除錯結果

下圖是氣泡排序,對陣列從小到大排序

zzc@zzc-virtual-machine:~/share$ ./1
排序之前,陣列為:26	43	47	37	22	48	48	8	37	15	
排序之後,陣列變為:8	15	22	26	37	37	43	47	48	48

下圖是選擇排序,對陣列從大到小排序

zzc@zzc-virtual-machine:~/share$ ./1
排序之前,陣列為:  40   10	31	26	23	48	50	6	30	41	
排序之後,陣列變為:50	  48	41	40	31	30	26	23	10	6	

下圖是快速排序,對陣列從小到大排序

zzc@zzc-virtual-machine:~/share$ ./1
排序之前,陣列為:33	30	24	27	23	41	29	9	8	4	
排序之後,陣列變為:4	8	9	23	24	27	29	30	33	41	

氣泡排序改良

我們可以對冒泡進行改良,一個數組中的資料可以分為有序數列和無序數列;排序過程中,有序數列處於陣列的後半段,不需要再給它排序,只需要對無序數列進行排序。
另外,如果第一輪比較下來發現沒有資料交換位置,那就表示整個陣列已經排列好了,直接結束排序即可。

按照這個思路,我們來實現一個示例,如下:

//氣泡排序改良版
void BubbleSortNew(int *a, int length) 
{
	int temp;
	//記錄最後一次交換的位置
	int last_exchange_index = 0;
	//無序數列的邊界,每次比較只需要比到這裡	
	int sort_border = length - 1;

	for (int i = 0; i < length - 1; i++)
	{
		bool isSorted = true;
		for (int j = 0; j < sort_border; j++)
		{
			if (a[j] > a[j + 1])
			{
				isSorted = false;	//有資料要交換位置,所以不是有序,標誌置為false 
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
				last_exchange_index = j;
			}
		}
		sort_border = last_exchange_index;
		if(isSorted)	//一輪下來是否發生資料交換,沒有就退出排序
			break;
	}
}

實測對10000個隨機數(範圍為0到9999)進行排序,在筆者的主機環境中傳統的冒泡花費了158237 個cpu clock,而改良版話費了151638 個cpu clock,節省了6599個cpu clock,所以效率上還是有所提高的。

延伸思考

常見演算法時間複雜度如下圖:

1、快速排序雖然效率較高、耗時短,但是一直壓棧(資料量很大的時候)很容易造成堆疊溢位
2、排序演算法應該儘量避免使用遞迴。
3、排序演算法非常多,應該結合具體的應用場景來選擇合適的演算法。(從來沒有最好的,只有最適合你的)


總結

本文主要介紹了C語言中氣泡排序、選擇排序和快速排序的思想,並進行了相應的程式碼實現和除錯。
同時對氣泡排序進行了改良。