複習----常見的排序算法

到目前爲止,學習過的排序算法已經有很多了。它們因爲時間複雜度以及空間複雜度的差異適用於不同的場合,所以現在來整理一下。

(注:此處的swap()是一個交換函數,只給出具體的實現函數)

待排序數組如下圖:


一、交換排序

交換排序,就是相鄰數的兩兩交換。

經過一趟排序之後(即i=0時),數組變爲如圖:


由上面我們可以看出,經過一趟交換排序之後,最小的數已經被放在數組的開始。

void exchange_sort(int *arr,int len)
{
	for (int i=0; i<len-1; i++)
	{
		for(int j=i+1; j<len;j++)
		{
			if(arr[i] > arr[j])
			{
				swap(&arr[i],&arr[j]);
			}
		}
	}
}
二、冒泡排序

冒泡排序,顧名思義就是數字像泡泡一樣,輕(小)的往上飄,重(大)的往下沉。同樣一趟排序之後數組爲:


同樣我們可以觀察得到經過一趟排序後最大的已經排到了數組的最後一個,即是有序的。

void bubble_sort(int *arr,int len)
{
	for (int i=0; i<len-1; i++)
	{
		for(int j=0; j<len-i-1; j++)
		{
			if(arr[j] > arr[j+1])
			{
				swap(&arr[j],&arr[j+1]);
			}
		}
	}
}

//加強版的冒泡排序

void bubble_sort_ex(int *arr,int len)
{
	bool flag = true;
	for (int i=0; i<len-1; i++)
	{
		flag = false;
		for(int j=0; j<len-i-1; j++)
		{
			if(arr[j] > arr[j+1])
			{
				swap(&arr[j],&arr[j+1]);
				flag = true;
			}
		}
		if(flag)
		{
			break;
		}
	}
}
優化版的冒泡排序,減少了不必要的交換,可以自己測試一下,交換次序大大減少。

三、插入排序

插入排序就是每一步都將一個待排序的數據按其大小插入到已經排序的數據中的適當位置,直到全部插入完畢。

插入排序方法分爲直接插入排序和折半插入排序,這裏只說了直接插入排序。

void insert_sort(int *arr,int len)
{
	int j = 0;.
	for(int i=1; i<len; i++)
	{
		int tmp = arr[i];
		for(j=i-1; j>=0; j--)
		{
			if (tmp > arr[j])
			{
				break;
			}
			arr[j+1] = arr[j];
		}
		arr[j+1] = tmp;
	}
}


四、希爾排序

希爾排序(Shell Sort)是插入排序的一種。是針對直接插入排序算法的改進。該方法又稱縮小增量排序

static void shell(int *arr,int len,int gap)

{
	int j = 0;
	for(int i=gap; i<len; i++)
	{
		int tmp = arr[i];
		for(j=i-gap; j>=0; j-=gap)
		{
			if(tmp > arr[j])
			{
				break;
			}
			arr[j+gap] = arr[j];
		}
		arr[j+gap] = tmp;
	}
}

void shell_sort(int *arr,int len)
{
	shell(arr,len,3);
	shell(arr,len,1);
}


五、歸併排序

static void meger(int *arr,int len,int gap)
{
	int L1 = 0;
	int H1 = L1+gap-1;

	int L2 = H1+1;
	int H2 = L2+gap-1 < len ? L2+gap-1 : len-1;

	int *buff = (int *)malloc(sizeof(int) * len);
	assert(buff != NULL);

	int k = 0;

	while(L2 < len)
	{
		while (L1 <= H1 && L2 <= H2)
		{
			if(arr[L1] <= arr[L2])
			{
				buff[k++] = arr[L1++];
			}

			else if(arr[L1] > arr[L2])
			{
				buff[k++] = arr[L2++];
			}
		}
		while(L1 <= H1)
		{
			buff[k++] = arr[L1++];
		}

		while (L2 <= H2)
		{
			buff[k++] = arr[L2++];
		}

		L1 = H2+1;
		H1 = L1+gap-1;

		L2 = H1+1;
		H2 = L2+gap-1 < len ? L2+gap-1 : len-1;
	}
	while(L1 < len) //歸併段1不完全,只有部分
	{
		buff[k++] = arr[L1++];
	}

	for(int i=0; i<len; i++)
	{
		arr[i] = buff[i];
	}

	free(buff);
}

void meger_sort(int *arr,int len)
{
	for(int gap=1; gap<len; gap=gap*2)
	{
		meger(arr,len,gap);
	}
}


六、快速排序

static int partition(int *arr,int left,int right)
{
	int L = left;
	int R = right;
	int tmp = arr[L];

	while(L < R)
	{
		while(arr[R] >= tmp && L < R)
		{
			R--;
		}
		arr[L] = arr[R];

		while (arr[L] <= tmp && L < R)
		{
			L++;
		}
		arr[R] = arr[L];
	}
	arr[L] = tmp;

	return L;
}

static void quick_sort_m(int *arr,int left,int right)
{
	if(left < right)
	{
		int base = partition(arr,left,right);
		quick_sort_m(arr,left,base-1);
		quick_sort_m(arr,base+1,right);
	}

}

void quick_sort(int *arr,int len)
{
	quick_sort_m(arr,0,len-1);
}


七、堆排序

static void heap_adjust(int *arr,int start,int end)
{
	int i = 0;
	int tmp = arr[start];

	for(i=2*start+1; i<end; i=2*i+1)
	{
		//判斷是否有有孩子,並讓i停在較大數的下標處
		if(i+1 < end && arr[i] < arr[i+1])
		{
			i++;
		}

		if(tmp > arr[i])
		{
			break;
		}
		else if(tmp < arr[i])
		{
			arr[start] = arr[i];
		}
		start = i;
	}
	arr[start] = tmp;
}

void heap_sort(int *arr,int len)
{
	for(int i=len/2-1; i>=0; i--)
	{
		heap_adjust(arr,i,len);
	}

	for(int j=len-1; j>0; j--)
	{
		swap(&arr[0],&arr[j]);
		heap_adjust(arr,0,j-1);
	}
}

排序算法的優良評判主要是看是時間複雜度以及空間複雜度,以下是幾種排序算法的時間複雜度以及空間複雜度。


雖然以上各種排序都有自己的優點和缺點,但是對於程序員來說應該熟悉什麼樣的情況下使用什麼樣的排序算法能使算法的時間複雜度和空間複雜度都能夠達到最好的狀態,發揮它們最大的作用。


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