筆試之排序-直接插入排序、氣泡排序、快速排序

兔美醬xz發表於2014-03-28

1. 插入排序

入排序是一種簡單直觀的排序方法,其基本思想在於每次將一個待排序的記錄,按其關鍵字大小插入到前面已經排好序的子序列中,直到全部記錄插入完成。

由插入排序的思想可以引申出兩個重要的排序演算法:直接插入排序和希爾排序。

直接插入虛擬碼:

void InsertSort(ElemType A[], int n)
{
	int i ,j;
	for(i = 2;i<=n;i++)   //依次將A[1]~A[n]插入到前面已排序序列
		if(A[i].key<A[i-1].key)
		{
			A[0].key = A[i].key;  //待插入值複製為哨兵
			for(j = i-1;A[0].key<A[j].key;j--) //從後往前查詢待插入位置
				A[j+1] = A[j];  //後移
			A[j+1] = A[0];    //將哨兵插入到空位
		}
}
直接插入排序的時間複雜度是O(n^2)空間複雜度為O(1)

在最好的情況下,表中元素已經有序,此時每插入一個元素,都只需比較一次而不用移動元素,因而時間複雜度為O(n)

穩定性:由於每次插入元素總是從後向前先比較再移動,所以不會出現相同元素相對位置發生變化的情況,即直接插入排序是一個穩定的排序方法


2. 交換排序

謂交換,就是根據序列中兩個元素關鍵字的比較結果來對換這兩個記錄在序列中的位置。

氣泡排序

氣泡排序演算法基本思想是:假設待排序表長為n,從後往前(或從前往後)兩兩比較相鄰元素的值,若為逆序(即A[i-1]>A[i]),則交換它們,直到序列比較完。

氣泡排序虛擬碼:

void BubbleSort(ElemType A[], int n)
{
	//用氣泡排序將序列A中的元素從小到大排列
	for (int i = 0; i < n; i++)
	{
		flag = false;   //表示本趟冒泡是否發生交換的標誌
		for (int j = n-1; j > i ; j--)
		{
			if(A[j-1].key>A[j].key)
			{
				swap(&A[j-1], &A[j]); //這裡有一個空間複雜度
				flag = true;
			}
		}
		if(flag == false)
			return ;   //本趟遍歷後沒有發生交換,說明已經有序
	}
}
氣泡排序演算法的效能分析:空間複雜度O(1),最壞情況下時間複雜度為O(n^2),最好的情況下(表示元素基本有序)時間複雜度為O(n),其平均複雜度為O(n^2)

氣泡排序是一個穩定的排序。


3. 快速排序

速排序是對氣泡排序的一種改進。其基本思想是基於分治法的。想詳細瞭解的朋友請轉到http://blog.csdn.net/v_JULY_v/article/details/6116297

完整程式碼:

#include<stdio.h>
#include<exception>
#include<stdlib.h>
#include<string.h>

void Swap(int *a,int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

int Partition(int data[], int length, int start , int end)
{
	if(data == NULL || length<0 || start<0 || end>=length)
		throw new std::exception("invalid parameters");

	int index =(int)(start + end)/2;  //產生start到end間的隨機數

	int small = start - 1;
	Swap(&data[index], &data[end]);

	for (int i = start; i < end; i++)
	{
		if(data[i]<data[end])
		{
			small++;
			if(small != i)
				Swap(&data[small], &data[i]);
		}
	}

	++small;
	Swap(&data[small], &data[end]);

	return small;
}


void QuickSort(int data[], int length, int start, int end)
{
	if(start == end)
		return;

	int index = Partition(data, length, start,end);
	if(index>start)
		QuickSort(data,length,start,index-1);
	if(index<end)
		QuickSort(data,length,index+1,end);
}

void Test(int dataS[], int dataD[], int length)
{
	while(length>=0)
	{
		if(dataS[length]!=dataD[length])
			break;
		length--;
		
	}
	if(length>=0)
		printf("failed");
	else
		printf("success");
}

int main()
{
	int data[] = {2,8,3,7,1,5,6,4};
	QuickSort(data,8,0,7);

	int expectedData[]={1,2,3,4,5,6,7,8};
	Test(data,expectedData,7);

	getchar();
	return 0;
}
快速排序演算法的效能分析:

空間效率:由於快速排序是遞迴的,需要藉助一個遞迴工作棧來儲存每一層遞迴呼叫的必要資訊,其容量應與隊規呼叫的最大深度一致。最好情況下為取上界的log2(n+1);最壞的情況下,因為要進行n-1次遞迴呼叫,所以棧深度為O(n):平均情況為O(log2n)

時間效率:快速排序的執行時間與劃分是否對稱有關,而後者又與具體使用的劃分演算法有關。快速排序的最壞情況發生在兩個區域分別包含n-1個元素和0個元素時,這種最大程度的不對稱性發生在每一層遞迴上,即對應於初始排序表基本有序或基本逆序時,就得到最壞情況下的時間複雜度O(n^2)

有很多方法可以提高演算法的效率。一種方法是當遞迴過程中劃分得到的子序列的規模較小時不要再繼續遞迴呼叫快速排序,可以採用直接插入排序演算法進行後續的排序工作。另一種方法就是儘量選取一個可以將資料中分的樞軸元素。可以用三分法或者隨機法,這樣使得最壞情況在實際排序中基本不會出現。

時間複雜度O(nlog2n)

快速排序是一種不穩定的排序

相關文章