国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【數(shù)據(jù)結(jié)構(gòu)】詳解七大排序算法(直接插入排序、希爾排序、直接選擇排序、堆排序、冒泡排序、快速排序)

這篇具有很好參考價值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】詳解七大排序算法(直接插入排序、希爾排序、直接選擇排序、堆排序、冒泡排序、快速排序)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。


直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言

??直接插入排序

1、基本思想

???把待排序的數(shù)按其關(guān)鍵碼值的大小逐個插入到一個已經(jīng)排好序的有序序列中,直到所以的記錄插入完為止,得到一個新的有序序列。

???實(shí)際中我們玩撲克牌時,就用到了插入排序的思想直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
基本步驟:
???當(dāng)插入第i個元素時,前面的arr[0]、arr[2]…arr[n-1]已經(jīng)排好序,此時用arr[i]待排序的值與前面的數(shù)進(jìn)行比較,找到插入的位置,將arr[i]插入,原來位置上的元素依次向后移動。

直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
2、代碼實(shí)現(xiàn)

void insertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

插入排序特性總結(jié):

  1. 元素集合越接近有序,直接插入排序算法的時間效率越高。
  2. 時間復(fù)雜度:O(N^2)
  3. 空間復(fù)雜度:O(1),是一種穩(wěn)定的排序算法。
  4. 穩(wěn)定性:穩(wěn)定

??希爾排序

1、基本思想
希爾排序又稱縮小增量法,其基本思想:

  • 先選定一個整數(shù)gap,把待排序的數(shù)列分成gap組,對每一組內(nèi)的元素進(jìn)行排序
  • 然后逐漸減小gap,重復(fù)排序
  • 直到gap=1,再進(jìn)行直接插入排序,完成排序

第一步進(jìn)行預(yù)排序,分組排序,把大的排后面,小的排前面
第二步進(jìn)行直接插入排序

直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言

動態(tài)演示:
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言

2、代碼實(shí)現(xiàn)

void shellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

希爾排序特性總結(jié):

  1. 希爾排序是對直接插入排序的優(yōu)化
  2. 當(dāng)gap>1時都是預(yù)排序,目的是讓數(shù)列更接近有序。當(dāng)gap==1時,數(shù)列就已經(jīng)接近有序,再進(jìn)行直接插入排序就會很快,進(jìn)而到達(dá)優(yōu)化效果
  3. 由于gap取值不唯一,希爾排序的時間復(fù)雜度不好計算,因此在希爾排序的時間復(fù)雜度不固定。
  4. 時間復(fù)雜度:O(N^1.3)
  5. 穩(wěn)定性:不穩(wěn)定

??直接選擇排序

1、基本思想

  • 每次從待排序的數(shù)列中選出最?。ɑ蜃畲螅┑囊粋€元素,存放在數(shù)列的起始位置,直到全部待排序的元素排完。

基本步驟:

  1. 在待排序的數(shù)列中選擇最?。ɑ蜃畲螅┑囊粋€元素。
  2. 若它不是這組待排序的數(shù)列中的第一個(或最后一個)元素,則將它與這組數(shù)列的第一個(或最后一個)元素交換。
  3. 對剩余的數(shù)據(jù),重復(fù)上述操作,直到數(shù)列排完。
    直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
    2、代碼實(shí)現(xiàn)
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int min = begin;
		int max = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] < a[min])
			{
				min = i;
			}
			if (a[i] > a[max])
			{
				max = i;
			}
		}
		Swap(&a[begin], &a[min]);
		if (max == begin)
		{
			max = min;
		}
		Swap(&a[end], &a[max]);
		begin++;
		end--;
	}
}

直接選擇排序特性總結(jié):

  1. 直接選擇排序效率不好,實(shí)際中很少使用
  2. 時間復(fù)雜度:O(N^2)
  3. 空間復(fù)雜度:O(1)
  4. 穩(wěn)定性:不穩(wěn)定

??堆排序

1、基本思想
堆排序分兩個步驟:

  1. 建堆
    ? 升序:建大堆
    ? 降序:建小堆
  2. 利用堆刪除思想來進(jìn)行排序
    把堆頂數(shù)據(jù)和最后一個數(shù)據(jù)進(jìn)行交換,把最后一個數(shù)不看做堆里面的,相當(dāng)于n-1個數(shù),向下調(diào)整,選出次大的數(shù)。

2、代碼如下

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//向下調(diào)整
void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 確認(rèn)child指向大的那個孩子
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}

		// 1、孩子大于父親,交換,繼續(xù)向下調(diào)整
		// 2、孩子小于父親,則調(diào)整結(jié)束
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

堆排序特性總結(jié):

  1. 堆排序效率比直接選擇排序高
  2. 時間復(fù)雜度:O(N*logN)
  3. 空間復(fù)雜度:O(1)
  4. 穩(wěn)定性:不穩(wěn)定

??冒泡排序

1、基本思想
將待排序數(shù)列中的元素兩兩進(jìn)行比較,直到將最大的數(shù)移動到末尾,一共進(jìn)行n-1趟。
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
2、代碼實(shí)現(xiàn)

void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
void bubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{                          
		int exchange = 0;
		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
			{
				Swap(&a[j - 1], &a[j]);
				exchange = 1;
			}
		}
		//一趟冒泡過程中,沒有發(fā)生交換,說明已經(jīng)有序了,不需要再處理
		if (exchange == 0)
		{
			break;
		}
	}
}

冒泡排序特性總結(jié):

  1. 冒泡排序是一種非常容易理解的排序
  2. 時間復(fù)雜度:O(N^2)
  3. 空間復(fù)雜度:O(1)
  4. 穩(wěn)定性:穩(wěn)定

??快速排序

1、基本思想
快速排序是Hoare于1962年提出的一種二叉樹結(jié)構(gòu)的交換排序方法,其基本思想:

  • 任意取待排序數(shù)列中的某一元素key,按照所取元素key值將待排數(shù)列分割成兩個子數(shù)列
  • 左子數(shù)列小于所取元素key值,右子數(shù)列大于所取元素key值
  • 然后左右子數(shù)列重復(fù)該過程,直到排序完成

基本步驟:

  1. 首先進(jìn)行單趟排序,左邊比key小,右邊比key大
  2. 然后遞歸排序左右兩邊子數(shù)列

全程圖解:
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言

將區(qū)間按照所取元素劃分為左右兩部分的常見方式有三種

第一種:hoare版
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
若左邊做key,右邊先走,保證相遇位置比key小
若右邊做key,左邊先走,保證相遇位置比key大
相遇的兩種情況:
1、R停住,L遇到R,相遇的位置就是R停住的位置
2、L挺住,R遇到L,相遇的位置就是L停住的位置

第二種:挖坑版
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言

第三種:前后指針版
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
2、代碼實(shí)現(xiàn)及優(yōu)化
第一種:hoare版

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//hoare版
int PartSort1(int* a, int begin, int end)
{
	int left = begin;
	int right = end;
	int keyi = left;
	while (left < right)
	{
		// 右邊先走,找小
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
		// 左邊再走,找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[keyi]);
	keyi = left;
	return keyi;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);

	// [begin, keyi-1]  keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

快排在理想狀態(tài)下,時間復(fù)雜度O(N*logN)
最壞情況下,時間復(fù)雜度O(N^2)
為解決最壞情況,可以對快排進(jìn)行優(yōu)化,即三數(shù)取中
加入三數(shù)取中后,快排瞬間從最壞變成最好,快排幾乎不會出現(xiàn)最后情況
代碼如下:

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//三數(shù)取中
int GetMidIndex(int* a, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
		if (a[mid] < a[end])
		{
			return mid;
		}
		else if (a[begin] > a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
	else
	{
		if (a[mid] > a[end])
		{
			return mid;
		}
		else if (a[begin] < a[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}
int PartSort1(int* a, int begin, int end)
{
	int mid = GetMidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);
	int left = begin;
	int right = end;
	int keyi = left;
	while (left < right)
	{
		// 右邊先走,找小
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}

		// 左邊再走,找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}

		Swap(&a[left], &a[right]);
	}

	Swap(&a[left], &a[keyi]);
	keyi = left;

	return keyi;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);

	// [begin, keyi-1]  keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

還有一種優(yōu)化方式,即遞歸到小的區(qū)間時,有直接插入排序,減少遞歸調(diào)用次數(shù)
代碼如下:

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	if ((end - begin + 1) < 15)
	{
		//小區(qū)間有直接插入排序
		insertSort(a + begin, end - begin + 1);
	}
	else
	{
		int keyi = PartSort1(a, begin, end);

		// [begin, keyi-1]  keyi [keyi+1, end]
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
}

第二種:挖坑版

//挖坑版
int PartSort2(int* a, int begin, int end)
{
	int mid = GetMidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);

	int left = begin;
	int right = end;
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		// 右邊找小,填到左邊坑里面
		while (left < right && a[right] >= key)
		{
			right--;
		}

		a[hole] = a[right];
		hole = right;

		// 左邊找大,填到右邊坑里面
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

第三種:前后指針版

//前后指針版
int PartSort3(int* a, int begin, int end)
{
	int mid = GetMidIndex(a, begin, end);
	Swap(&a[begin], &a[mid]);

	int keyi = begin;
	int prev = begin;
	int cur = begin + 1;
	while (cur <= end)
	{
		// 找到比key小的值時,跟++prev位置交換,小的往前翻,大的往后翻
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}

	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	return keyi;
}

若深度太深,就會造成溢出,快排的非遞歸就是次問題
代碼如下:

//非遞歸快排
void QuickSortNonR(int* a, int begin, int end)
{
	//創(chuàng)建棧
	ST st;
	StackInit(&st);
	
	StackPush(&st, begin);
	StackPush(&st, end);

	while (!StackEmpty(&st))
	{
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPop(&st);

		int keyi = PartSort3(a, left, right);
		// [left, keyi-1] keyi [keyi+1, right]
		if (keyi+1 < right)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}

		if (left < keyi-1)
		{
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}
	}
	StackDestroy(&st);
}

快速排序特性總結(jié):

  1. 快速排序整體的綜合性能和使用場景都是比較好的
  2. 時間復(fù)雜度:O(N*logN)
  3. 空間復(fù)雜度:O(logN)
  4. 穩(wěn)定性:不穩(wěn)定

??歸并排序

1、基本思想
歸并排序是建立在歸并操作上的一種排序算法,該算法是采用分治法的一個典型的應(yīng)用。其基本思想:

  • 先使每個子序列有序,再使子序列段間有序
  • 將有序的子序列合并成一個有序序列

若將兩個有序表合并成一個有序表,稱為二路歸并。

歸并全過程如下圖:
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
動態(tài)演示:
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
2、代碼實(shí)現(xiàn)

void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;
	// [begin, mid] [mid+1, end] 遞歸讓子區(qū)間有序
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);
	// 歸并[begin, mid] [mid+1, end]
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	// 左閉右開,直接減,就是個數(shù),左閉右閉,要加1
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}

非遞歸實(shí)現(xiàn):

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	// 歸并每組數(shù)據(jù)個數(shù),從1開始,因?yàn)?認(rèn)為是有序的,可以直接歸并
	int rangeN = 1;
	while (rangeN < n)
	{
		for (int i = 0; i < n; i += 2 * rangeN)
		{
			// [begin1,end1][begin2,end2] 歸并
			int begin1 = i, end1 = i + rangeN - 1;
			int begin2 = i + rangeN, end2 = i + 2 * rangeN - 1;
			int j = i;
			if (end1 >= n)
			{
				break;
			}
			else if (begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}

			// 歸并一部分,拷貝一部分
			memcpy(a + i, tmp + i, sizeof(int)*(end2 - i + 1));
		}

		rangeN *= 2;
	}
	free(tmp);
	tmp = NULL;
}

歸并排序特性總結(jié):

  1. 歸并的缺點(diǎn)在于需要O(N)空間復(fù)雜度,歸并排序的思考更多的是解決在磁盤中的外排序問題
  2. 時間復(fù)雜度:O(N*logN)
  3. 空間復(fù)雜度:O(N)
  4. 穩(wěn)定性:穩(wěn)定

??排序算法復(fù)雜度及穩(wěn)定性分析

直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
測試排序的性能對比,代碼如下:

int main()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	int j = 0;
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
	}

	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin7 = clock();
	BubbleSort(a5, N);
	int end7 = clock();

	int begin5 = clock();
	QuickSort(a6, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	MergeSort(a7, N);
	int end6 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("BubbleSort:%d\n", end5 - begin5);
	printf("QuickSort:%d\n", end6 - begin6);
	printf("MergeSort:%d\n", end7 - begin7);
	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
	return 0;
}

性能測試如下:
直接排序,初階數(shù)據(jù)結(jié)構(gòu)--C語言,排序算法,數(shù)據(jù)結(jié)構(gòu),算法,c語言
文章來源地址http://www.zghlxwxcb.cn/news/detail-766947.html

到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】詳解七大排序算法(直接插入排序、希爾排序、直接選擇排序、堆排序、冒泡排序、快速排序)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包