1. 程式人生 > >八大排序演算法

八大排序演算法

直接插入排序(穩定)

我們認為在一組數中,可以分為三個部分,有序區,無序區和待排序區,例如下圖:

在這裡插入圖片描述

藍色部分是有序區,黃色部分表示即將要排序的,灰色是無序區,那麼我們怎麼在有序部分找到黃色應該插入的位置呢? 1.從後往前找。可以從藍色部分的後面往前找,分別和黃色比較,找到相應的位置 2.二分方式找。 程式碼如下:

void InsertSort(int array[], int size)
{
	int key;
	int i, j;
	for (i = 1; i < size; i++)
	{
		key = array[i];
		for (j = i - 1; j >=
0; j--) { if (key >= array[j]) { break; } else { array[j + 1] = array[j]; } } array[j+1] = key; } }

時間複雜度: 最好/最壞 O(n)/O(n^2) 空間複雜度: O(1)

希爾排序(不穩定)

希爾排序也是插排的一種,在排序之前會先做預排序,例如下面這組資料

在這裡插入圖片描述

希爾排序會先分組進行插敘,例如上面的陣列相同顏色的圈代表一組,先在每一組進行排序,然後不斷的重複,那麼這就需要考慮分組策略了: 1.分組間隔越大,排序次數越少 2.分組間隔越小,結果越有序 一般情況下:我們會把一組資料分為幾組呢?一般情況gap=size/3+1;

程式碼:

void _InsertSort(int array[], int size, int gap)
{
	for (int g = 0; g < gap; g++)
	{
		int key;
		int i, j;
		for (i = gap + g; i < size; i+=gap) {
			key = array[i];
			for (j = i - gap; j >= 0; j -= gap) {
				if (key >= array[j]) {
					break;
				}
				else {
					array[j + gap]
= array[j]; } } array[j + gap] = key; } } } void ShellSort(int array[], int size) { int gap = size; while (1) { gap = gap / 3 + 1; _InsertSort(array, size, gap); if (gap == 1) { break; } } }

時間複雜度: 最好/平均/最差 O(n)/O(n^(1.2~1.3) ) /O(n^2) 空間複雜度: O(1)

選擇排序(不穩定)

每次在序列中選出最大(最小),交換到最後,簡而置之,然後迴圈

程式碼:

void Swap(int *a, int *b)
{
	int t = *a;
	*a = *b;
	*b = t;
}
void SelectSort(int array[], int size)
{
	for (int i = size; i >1; i--)
	{
		int max = 0;
		for (int j = 1; j < i; j++)
		{
			if (array[j] > array[max]) {
				max = j;
			}
		}
		Swap(array + max, array + i - 1);
	}
}

上面這種是每次選出最大的或者最小放好位置,當然,我們也可以,每次既選出最大的,也選出最小的同時放好位置。

程式碼:

void Swap(int *a, int *b)
{
	int t = *a;
	*a = *b;
	*b = t;
}
void SelectSort1(int array[], int size)
{
	int left = 0;
	int right = size - 1;
	while (left < right)
	{
		int min = left;
		int max = left;
		for (int j = left + 1; j <= right; j++)
		{
			if (array[j] > array[max])
			{
				max = j;
			}
			if (array[j] < array[min])
			{
				min = j;
			}
		}
		Swap(array + left, array + min);
		if (max == left)
		{
			max = min;
		}
		Swap(array + right, array + max);
		left++;
		right--;
	}
}

時間複雜度:O(n^2) 空間複雜度:O(1)

堆排序(不穩定)

堆排序的思想其實就是,每次找到最大值與最後一個元素交換,然後向下調整。排序的過程其實很簡單,主要在於建立大堆和堆的向下調整。

void AdjustDown(int array[], int size, int root)
{
	int left = 2 * root + 1;
	int right = 2 * root + 2;
	if (left >= size)
	{
		return;
	}
	int max = left;
	if (right<size&&array[right]>array[left])
	{
		max = right;
	}
	if (array[root] >= array[max])
	{
		return;
	}
	Swap(array + root, array + max);
	AdjustDown(array, size, max);
}
void CreateHeap(int array[], int size)
{   //建立大堆
	//從最後一個非葉子結點到0
	//不斷的進行向下調整
	for (int i = size / 2 - 1; i >= 0; i--)
	{
		AdjustDown(array, size, i);
	}
}
void HeapSort(int array[], int size)
{
	CreateHeap(array, size);
	for (int i = 0; i < size; i++)
	{
		Swap(array, array + (size - i - 1));
		AdjustDown(array,size-i-1,0);
	}
}

時間複雜度:O(N*logN) 空間複雜度:O(1)

氣泡排序(穩定)

氣泡排序是最常見的排序,思想也很簡單,就是挨個交換,不斷迴圈。

在這裡插入圖片描述 在這裡插入圖片描述

void BubbleSort(int array[], int size)
{
	for (int i = 0; i < size - 1; i++)
	{
		int flag = 1;
		for (int j = 0; j < size - 1 - i; j++)
		{
			if (array[j] > array[j + 1])
			{
				Swap(array + j, array + j + 1);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

時間複雜度: 最好/平均/最壞 O(n) O(n^2) O(n^2) 空間複雜度: O(1)

快速排序(不穩定)

基本思想:分治演算法 1.找一個基準值 1>找邊上的 2.比基準值小的都放到基準值的左邊 比基準值大的都放到基準值的右邊 3.終止條件 1>小區間有序 區間長度==1 2>小區間沒有數 區間長度<=0

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

方法一:hoare法

快速排序是Hoare於1962年提出的一種二叉樹結構的交換排序方法,其基 本思想為:任取待排序元素序列中的某元素作為基準值,按照該排序碼將 待排序集合分割成兩子序列,左子序列中所有元素均小於基準值,右子序 列中所有元素均大於基準值,然後最左右子序列重複該過程,直到所有元 素都排列在相應位置上為止。

int Partition(int array[], int left, int right)
{
	int begin = left;
	int end = right;
	while (begin < end)
	{
		while (begin < end&&array[begin] <= array[right])
		{
			begin++;
		}
		while (begin < end&&array[end] >= array[right])
		{
			end--;
		}
		Swap(array + begin, array + end);
	}
	Swap(array + begin, array + right);
	return begin;
}
void _QuickSort(int array[],int left,int right)
{
	if (left == right)
	{
		return;
	}
	if (left > right)
	{
		return;
	}
	int div = Partition(array, left, right);
	_QuickSort(array, left, div - 1);
	_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
	_QuickSort(array, 0, size - 1);
}

挖坑法

挖坑法其實就是提前將基準值存起來,這樣基準值的位置其實就是一個坑,可以被別的元素填。挖坑法的具體過程就是將最後一個元素作為基準值,然後begin從前遍歷,遇到比基準值大的就去填基準值的坑,end從後往前遍歷,遇到比基準值小的再去填begin的坑。

int Partition_02(int array[], int left, int right)
{
	int begin = left;
	int end = right;
	int priot = array[right];
	while (begin < end)
	{
		while (begin < end&&array[begin] <= priot)
		{
			begin++;
		}
		array[end] = array[begin];
		while (begin < end&&array[end] >= priot)
		{
			end--;
		}
		array[begin] = array[end];
	}
	array[begin] = priot;
	return begin;
}
void _QuickSort(int array[],int left,int right)
{
	if (left == right)
	{
		return;
	}
	if (left > right)
	{
		return;
	}
	int div = Partition_01(array, left, right);
	_QuickSort(array, left, div - 1);
	_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
	_QuickSort(array, 0, size - 1);
}

快慢指標法

快慢指標也叫前後指標,當然並不是一個從前一個從後遍歷,而是一前一後遍歷,兩個指標都從頭開始向後遍歷,快的指標每次都向後移動,只有當快的指標所指的值小於基準值時,快慢指標所指的值進行交換,並且慢指標向後移動,否則,慢指標不動。

int Partition_03(int array[], int left, int right)
{
	int fast = left;
	int slow = left;
	while (fast<right)
	{
		if (array[fast] < array[right])
		{
			Swap(array + fast, array + slow);
			slow++;
			fast++;
		}
		else {
			fast++;
		}
	}
	Swap(array + right, array + slow);
	return slow;
}
void _QuickSort(int array[],int left,int right)
{
	if (left == right)
	{
		return;
	}
	if (left > right)
	{
		return;
	}
	int div = Partition_03(array, left, right);
	_QuickSort(array, left, div - 1);
	_QuickSort(array, div + 1, right);
}
void QuickSort(int array[], int size)
{
	_QuickSort(array, 0, size - 1);
}

時間複雜度:O(nlogn) 空間複雜度:O(1)