1. 程式人生 > >【資料結構】快速排序(遞迴)

【資料結構】快速排序(遞迴)

概念: 快速排序是Hoare於1962年提出的一種二叉樹結構的交換排序方法。

基本思想為:任取待排序元素序列中的某元素作為基準值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小於基準值,右子序列中所有元素均大於基準值,然後最左右子序列重複該過程,直到所有元素都排列在相應位置上為止。

 將區間按照基準值劃分為左右兩半部分的常見方式有:

    1. hoare版本

    具體程式碼如下:

//hoare版本
void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int PartSort1(int* a, int begin, int end)
{
	int key = a[0];
	int start = begin;
	while (begin < end)
	{
		//end從後面走,找到比key小的就停下來
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		//begin從前面走,找到比key大的就停下來
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		//交換begin和end
		Swap(&a[begin], &a[end]);
	}
	//此時begin和end相遇
	Swap(&a[begin], &a[start]);

	return begin;
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int div = PartSort1(a, left, right);
	//在以div為基準的左邊區間去找新的key
	//左邊區間:[left,div-1]
	QuickSort(a, left, div - 1);
	//在以div為基準的右邊區間去找新的key
	//右邊區間:[div+1,right]
	QuickSort(a, div + 1, right);
}

優化後的程式碼:

int GetMidIndex(int* a, int begin, int end)
{
	int mid = begin + ((end - begin) >> 1);
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
		{
			return mid;
		}
		else if (a[begin]>a[end])
		{
			return begin;
		}
		else//end<mid
		{
			return end;
		}
	}
	else//begin>mid
	{
		if (a[mid] > a[end])
		{
			return mid;
		}
		else if (a[begin] < a[end])
		{
			return begin;
		}
		else//end>mid
		{
			return end;
		}
	}
}

//為防止所給資料直接有序,進行三數取中的優化
int PartSort(int* a, int begin, int end)
{
	//三數取中
	int midindex = GetMidIndex(a, begin, end);
	Swap(&a[begin], &a[midindex]);
	int key = a[begin];
	int start = begin;
	while (begin < end)
	{
		//end從後面走,找到比key小的就停下來
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		//begin從前面走,找到比key大的就停下來
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		//交換begin和end
		Swap(&a[begin], &a[end]);
	}
	//此時begin和end相遇
	Swap(&a[begin], &a[start]);

	return begin;
}
//小區間優化
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	if ((right - left + 1) < 10)
	{
		InsertSort(a + left, right - left + 1);
	}
	else
	{
		int div = PartSort(a, left, right);
		//在以div為基準的左邊區間去找新的key
		//左邊區間:[left,div-1]
		QuickSort(a, left, div - 1);
		//在以div為基準的右邊區間去找新的key
		//右邊區間:[div+1,right]
		QuickSort(a, div + 1, right);
	}
}

2.挖坑法

  具體程式碼:

//挖坑法
int PartSort2(int* a, int begin, int end)
{
	int key = a[begin];
	while (begin < end)
	{
		//end找比key小的資料
		while (begin < end && a[end] >= key)
		{
			end--;
		}
		//當end找到比key小的資料後,用此時的end填begin這個坑,end就變成了新的坑
		a[begin] = a[end];

		//begin找比key大的資料
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		//當begin找到比key大的資料後,用此時的begin填end這個坑,begin又變成了新的坑
		a[end] = a[begin];
	}
	a[begin] = key;

	return begin;

}
void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int div = PartSort(a, left, right);
	//在以div為基準的左邊區間去找新的key
	//左邊區間:[left,div-1]
	QuickSort(a, left, div - 1);
	//在以div為基準的右邊區間去找新的key
	//右邊區間:[div+1,right]
	QuickSort(a, div + 1, right);
}

3.前後指標

具體程式碼如下:

//前後指標法
int PartSort3(int* a, int begin, int end)
{
	int key = a[begin];
	int prev = begin;
	int cur = begin + 1;
	while (cur <= end)
	{
		if (a[cur] < key && (++prev) != a[cur])//當cur小於key時,且++prev的值不等於cur的值時,才發生交換,當cur>key時,不交換,繼續向前走
		{
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[begin], &a[prev]);

	return prev;
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int div = PartSort3(a, left, right);
	//在以div為基準的左邊區間去找新的key
	//左邊區間:[left,div-1]
	QuickSort(a, left, div - 1);
	//在以div為基準的右邊區間去找新的key
	//右邊區間:[div+1,right]
	QuickSort(a, div + 1, right);
}

以上就是快速排序遞迴的幾種思想及程式碼,

注:需要說明的是,如果要在vs下面執行的話,得有標頭檔案,交換函式和測試資料等等,直接是執行不出來的.