主頁 > 後端開發 > 詳解 八大排序

詳解 八大排序

2021-09-26 12:58:36 後端開發

排序

文章目錄

  • 排序
    • 排序的概念
    • 直接插入排序
    • 希爾排序
    • 選擇排序
    • 堆排序
    • 冒泡排序
    • 快速排序
      • 1.hoare版本
      • 2.挖坑法
      • 前后指標法
      • 快排的非遞回方法(回圈)
      • 時間復雜度優化問題
    • 歸并排序
      • 遞回法
      • 非遞回法
    • 計數排序
    • 總結

排序的概念

  • 排序 :所謂排序就是使一串記錄,按照其中的某個或某些關鍵字的大小,遞增或遞減的排列起來的操作
  • 排序的穩定性: 排序前后兩個相等的數相對位置不變,則演算法穩定,
    穩定性是一種人為刻意為之的行為,任何一個穩定的排序方法都可以寫成不穩定的,穩定性的意義在于確定值相等的值的輸入順序,有一定參考作用,

同時 還有兩個比較基礎的概念:

  • 內部排序: 資料元素全部放在記憶體中的排序
  • 外部排序: 資料元素太多不能同時放在記憶體中,根據排序程序的要求不能在內外存之間移動資料的排序

直接插入排序

插入排序的思想十分的簡單:
把待排序的元素插入到一個 有序序列 的 正確的位置,直到所有的記錄插入完全為止,得到一個新的有序序列,
其實我們在玩撲克牌的時候就是這個思想
在這里插入圖片描述
為了能更好的理解插入排序,我們從他的一次回圈看起:(以升序為例)

  • 假設陣列a為一個前(end+1)個數有序陣列,我們要把第end+1個數插入陣列中
    在這里插入圖片描述

  • 回圈的基本思路是: 如果a[end]比temp(待插入的數)要小,就說明還要繼續往下尋找,這是執行a[end+1]=a[end],并將end- -,回圈的條件時end >=0
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述

  • 直到找到比 temp要小的數 或者 end==0 時還沒有找到,這時就要跳出回圈執行:a[end+1]=temp
    注意: 這里如果end==0時還沒有找到的時候,出回圈的時候end為-1,但是a[end+1]實際上還是第一個元素

  • 這是直接插入排序的一次單次回圈,實際上我們發現直接插入排序一個重要的前提是要有一個有序的陣列

在排序的最開始 我們可以認定第一個數為一個大小為1的有序陣列,來啟動上述介紹的單次回圈,回圈結束我們就得到了一個前兩個元素有序的陣列 …直到前n-1個元素(陣列大小為n)有序,將最后一個元素插入,這是最后一次回圈, 所以我們還要在上面單次回圈的基礎上在加一個回圈來完成排序

代碼


void InsertSort(int* a, int n)
{

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

總結

  • 時間復雜度:O(N^2)
  • 空間復雜:O(1)
  • 穩定性:穩定

希爾排序

希爾排序實際上是在插入排序的基礎上做出了一些改進,希爾排序又叫縮小增量法,

其基本思想是:
將陣列按一定間隔分成若干個組,對每個組里面的元素進行直接選擇排序,然后縮小間隔,在對重新分的組挨個進行選擇排序…直到最后間隔為1(實際上直接插入排序是間隔為1的希爾排序)

  • 我們以一個陣列為例進行希爾排序:
    在這里插入圖片描述
  • 然后就要對陣列進行分組排序,第一次我們把間隔gap設為5
    在這里插入圖片描述
  • 然后我們要不斷縮小間隔,在進行重新分組并排序,這次我們把間隔調成2
    在這里插入圖片描述
  • 最后一次我們把間隔gap調成1
    在這里插入圖片描述
    我們從中可以發現一些規律:
  1. 希爾排序是對直接插入排序的一種優化
  2. 隨著gap的不斷減小,陣列越來越趨于有序,其實gap>1時都是預排序,目的是讓陣列更有序,gap==1時,陣列已近接近有序,這樣最后一次排序就很快,達到優化的效果,
  3. 希爾排序的時間復雜度不好去計算,因為gap的取值方法很多,導致很難去計算,

代碼


void ShellSort(int* a, int n)
{
	int gap = n ;

	while (gap > 1)
	{
		gap = gap / 3 + 1; //注意這里是以三倍的速度縮小gap,這里+1是因為要保證最后一次gap為1
		for (int i = 0; i < n -gap; i++)
		{
			int end = i;
			int temp = a[i + gap];
			while (end >= 0)
			{
				if (a[end] > temp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
					break;
			}
			a[end + gap] = temp;
		}
	}
}

總結

  • 時間復雜度:O(N^1.25) 這是通過大量實驗得到的經驗公式
  • 空間復雜:O(1)
  • 穩定性:不穩定

選擇排序

基本思路:
每次遍歷整個陣列從陣列中選擇出最大的數(或最小的數),最放到陣列的末尾(或陣列的首尾),直到全部待排序的資料元素排完

我們以一個陣列為例:

  • 在begin和end之間(包括begin和end)中找到max和min,并讓min和begin、max和end交換
    注意:這里交換的先后順序,到后面會有些區別!
    在這里插入圖片描述
  • 按上述步驟不斷重復在這里插入圖片描述

在這里插入圖片描述

  • 最后一次交換比較有代表意義,是一個容易犯的錯誤,如果先讓begin和min交換再讓max和end交換會產生一個問題:那就是max和begin重合的時候,如果begin先和min交換,那么max的值會被交換到min上,這時就需要end和min交換
    在這里插入圖片描述
    代碼:
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int min = begin;
		int max = end;
		for (int i = begin+1; i < end;i++)
		{
			if (a[i] > a[max])
				max = i;
			if (a[i] < a[min])
				min = i;
		}


		swap(&a[begin], &a[min]);

		if (begin != max)
			swap(&a[end], &a[max]);
		else
			swap(&a[end], &a[min]);

		begin++;
		end--;
	}
}

總結:

  • 時間復雜度:O(N^2)
  • 空間復雜:O(1)
  • 穩定性:不穩定

堆排序

可以參考一下以前的博客:資料結構:堆 的詳解

代碼:


void AjustDown(int* a, int n, int parents)
{
	int child = 2 * parents + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
			child++;
		if (a[child] > a[parents])
			swap(&a[child], &a[parents]);
		parents = child;
		child = 2 * parents + 1;
	}
}


void HeapSort(int* a, int n)
{
	for (int i = n/2-1 ; i >= 0; i--)
	{
		AjustDown(a,n,i);
	}

	for (int i = n-1; i >0 ; i--)
	{
		swap(&a[0], &a[i]);
		AjustDown(a, i, 0);
	}
}


總結:

  • 時間復雜度:O(N*logN)
  • 空間復雜:O(1)
  • 穩定性:不穩定

冒泡排序

基本思想: 就是根據序列中兩個記錄鍵值的比較結果來對換這兩個記錄在序列中的位置,在視覺上的效果像:較大鍵值的記錄向序列的尾部移動,鍵值較小的記錄向序列的前部移動,

  • 我們以冒泡排序的一次遍歷為例:
    遍歷的規則是:兩兩進行比較,如果前者大于后者就交換,否則就繼續往下走,這里要注意一下a[j]的范圍是閉區間[0,n-2],也就是n-1次比較,

在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述
在這里插入圖片描述
看完一次回圈大家或許就理解了冒泡排序名字的由來,最大的數像氣泡一樣向上移動,在往下思考這次遍歷過后,實際上最后一個已經是有序的了,所以還要進行n-1次這樣的遍歷,所以時間復雜的是O((n-1)+(n-2)+(n-3)+(n-4).....+0)=O(n^2)
代碼

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int k = 0;
		for (int j = 0; j < n - 1 - i; j++) //每一次遍歷結束,下一次要遍歷的個數都會減少一個
		{
			if (a[j] > a[j + 1])
			{
				int temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
				k = 1;
			}
		}
		if (k == 0)
			break;
	}

}
  • 這里特意設計了以個變數K,來面對 陣列已經是有序的情況 ,如果陣列有序的話,就不會進行任何一次交換,所以這時k的值就不會被修改,這樣檢測到未修改,就可以直接跳出回圈了,

總結:

  • 時間復雜度:O(N^2) 最好情況是:O(N)
  • 空間復雜:O(1)
  • 穩定性:穩定

快速排序

快排的思路:
任取待排序元素序列中的某元素作為基準值,按照該基準值將排序分成兩個子序列,左子序列的元素均小于基準值,右子序列的元素均大于基準值,,然后在左、右子序列重復上述程序,直到所有元素都有序為止

下面介紹快排的三個版本:

1.hoare版本

思路

  1. 首先從頭部或者尾部選出一個值做key(基準值),這里取頭部元素a[0]
  2. 右邊先找(這個很重要!!!!),從陣列尾部開始尋找比key小的值,找到后停止,
  3. 左邊去找,從陣列首部開始尋找比key大的值,找到后與右邊的進行交換
  4. 重復上述步驟直到左邊與右邊相遇為止,這時把key與相遇位置的值進行交換,完成排序!

看完這個思路之后,我們直到整體思路就是,從左邊找比基準值大的與右邊找到的比基準值小的進行交換,但是在這里要思考清楚一個問題:
既然最后一步是將相遇的值與key的值交換,也就是說明左右相遇時指向的值一定比key小,這是為什么?
我們從兩個情況去考慮:

  1. 首先是左邊移動與右邊相遇,這毋庸置疑右邊本來就是找比key小的值,所以相遇時指的值一定比key小
  2. 其次時右邊移動與左邊相遇,這種情況也分兩種情況,一種就是左邊從來就沒移動過,還指向key值,這是key值與自己交換,第二種是左邊移動過一次或多次,雖然左邊是找比key大的值,但不要忘了在右找左之前,會交換左右的值,交換過后左邊就是比key小的值,相遇時也就是比key小的值

我們以第一次回圈為例:

在這里插入圖片描述


在這里插入圖片描述


在這里插入圖片描述

這里我們就實作了把陣列分成了兩個子序列(左子序列的元素均小于基準值,右子序列的元素均大于基準值),然后我們把這兩個子序列看成一個新的序列,對其指向執行相同的程式,并一直遞回下去,直到 傳進去的陣列的 大小為 0 (沒有左子序列 或 右子序列 也就是key比該序列的所有元素都要大或小) 或 1(沒有執行程式的必要),實際上執行的思路入下圖所示
在這里插入圖片描述

代碼

void QuickSort1(int* a, int n)     //hoare版本
{
	if (n == 1 || n == 0)
		return;
	int key = 0;
	int left = 0;   //這里必須從0開始,如果從1開始,當陣列只有兩個的時候代碼會出現問題
	int right = n - 1;
	while (left < right)  //這里必須是小于號保證出回圈left一定等于right
	{
		while (left<right && a[right] > a[key])
			right--;


		while (left<right && a[left] <= a[key])    //注意這里的大于等于號 因為key在左邊,為了讓key往左走必須大于等于
			left++;

		swap(&a[left], &a[right]);


	}
	swap(&a[left], &a[key]);
	key = left;

	QuickSort1(a, key);
	QuickSort1(a + key + 1, n - key - 1);

}

2.挖坑法

思路

  1. 首先從頭部或者尾部選出一個值做key(基準值),把key的值拷貝給臨時變數temp,并把陣列第一個元素當作第一個“坑”
  2. 右邊先找(這個很重要!!!!),從陣列尾部開始尋找比key小的值,然后填上面挖的“坑”,
  3. 左邊去找,從陣列首部開始尋找比key大的值,找到后填上面挖的“坑”
  4. 重復上述步驟直到左邊與右邊相遇為止,這時把temp與相遇位置的“坑”填上,完成排序!

我們以一次回圈作為基礎:

  • 首先我們選擇首元素作為第一個坑
    在這里插入圖片描述
  • 然后右邊找小,填到左邊的坑里
    在這里插入圖片描述
  • 然后是左邊找大,填到右邊的坑里,后面重復上述步驟
    在這里插入圖片描述

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


在這里插入圖片描述

-最后一步左右重合,將temp中的值填入坑內
在這里插入圖片描述
代碼

void QuickSort2(int* a, int n)          //快排——挖坑法
{
	if (n == 1 || n == 0)
		return;
	int key = 0;
	int left = 0; 
	int right = n - 1;
	int temp = a[key];
	while (left < right)
	{
		while (left<right && a[right]>temp)
			right--;
		a[left] = a[right]; //填左邊的坑


		while (left<right && a[left]<= temp)
			left++;
		a[right] = a[left];  //填右邊的坑

	}
	a[left] = temp;  //最后左右相遇時,填坑
	key = left;
	
	QuickSort2(a, key);
	QuickSort2(a + key + 1, n - key - 1);
}

前后指標法

基本思路: 設定兩個指標(prev,cur),還是在陣列的頭或尾找一個基準值,prev指向第一個元素,cur指向第二個元素,然后cur開始遍歷陣列,找到比基準值小的值就與prev下一個元素交換,直到cur遍歷完陣列,
我們以一次回圈作為基礎:

  • 首先我們選擇首元素作為第一個坑
    在這里插入圖片描述

在這里插入圖片描述
如果沒有遇到比基準值小的數cur就直接往后走,找到比基準值小的數就開始交換,

其實我們還可以把陣列的最后一個數設為基準值,這時程式也要相應的做出一點改變
這時prev的其實位置就必須時陣列第一個數的前一個位置,也就是下表為-1的位置,cur起始位置時陣列的 首元素,其他的步驟與上面的一樣,

代碼

void QuickSort3(int* a, int n)   //快排——雙指標法
{
	if (n == 1 || n == 0)
		return;
	int key = 0;
	int prev = 0;
	int cur = 1;
	while (cur < n)
	{
		if (a[cur] < a[key])
			swap(&a[++prev], &a[cur]);
		cur++;
	}
	swap(&a[prev], &a[key]);
	key = prev;
	
	QuickSort3(a, key);
	QuickSort3(a + key + 1, n - key - 1);
}

快排的非遞回方法(回圈)

把遞回轉換成非遞回有兩種方法:

  1. 直接轉換成 回圈
  2. 把遞回問題轉換成 堆疊 + 回圈

因為遞回是把大事化成若干個小問題去解決,回圈正好是反過來人為的從小問題開始解決,但是有時候遞回在把大問題化成小問題的時候,小問題是建立在大問題的基礎上的,不解決大問題就無法得到小問題,所以用回圈就無法從小問題開始解決,這時就要用到堆疊的結構,堆疊的結構正好將這種情況完美解決,從 入堆疊時的大->小 ,到 出堆疊進入回圈時的 小->大

本體思路:

  1. 首先把陣列的左右邊界先入堆疊
  2. 然后進入回圈,取出堆疊里第一個區間的左右邊界,并讓左右邊界出堆疊,對齊做單趟排序,這時就的到了key和兩個左右子序列,把左右子序列的左右邊界分別入堆疊,
  3. 重復上述步驟,直到堆疊為空

注意:
這里要注意入堆疊和出堆疊時的左右邊界的順序,左邊界先入堆疊,右邊界后入堆疊,取堆疊頂元素時就要先取右邊界并出堆疊,再取左邊界

在這里插入圖片描述
代碼

int Partsort(int* a, int left,int right)  //單趟排序
{
	int key = left;
	int temp =Choose_Key(a,left,right);
	swap(&a[temp], &a[left]);
	while (left < right)
	{
		while (left<right && a[right] >= a[key])
			right--;

		while (left<right && a[left] <= a[key])
			left++;

		swap(&a[left], &a[right]);
	}
	swap(&a[left], &a[key]);
	return left;
}

void QuickSortNonR(int* a, int n)   //快排的非遞回寫法
{
	Stack ps;
	StackInit(&ps);
	StackPush(&ps, 0);        // 入左邊界
	StackPush(&ps, n - 1);     // 入右邊界

	while (!StackEmpty(&ps))
	{
		int end = StackTop(&ps);  //右邊界先出堆疊  一定要和上面入堆疊的順序相反!!!!
		StackPop(&ps);

		int begin = StackTop(&ps);  //左邊界出堆疊
		StackPop(&ps);

		int keyi = Partsort(a, begin, end);
		if (keyi + 1 < end)   //判斷是否要繼續遞回
		{
			StackPush(&ps, keyi + 1);    // 右子序列的 左邊界入堆疊
			StackPush(&ps, end);         // 右子序列的 右邊界入堆疊
		}

		if (begin  < keyi-1)
		{
			StackPush(&ps, begin);       //右子序列的 左邊界入堆疊
			StackPush(&ps, keyi - 1);    // 右子序列的 右邊界入堆疊
		}
	}
	StackDestroy(&ps);

}

時間復雜度優化問題

快排的時間復雜度最快的時候可以達到O(N*logN) ,但是當陣列為有序的時候 或 有大面積重復的時候,時間復雜度會飆升到O(N^2)
為了解決這種情況,首先要了解為什么會出現這種情況?
最主要的原因是 key每次都取到了整個陣列的 最小值或最大值 ,使二叉樹變成單邊樹,這時我們只需要對key選取的值稍加處理就可以了,我們采用三數取中 即選取陣列 左 、 中 、 右 大小在中間的數,與key所在的位置交換,
代碼

int Choose_Key(int *a,int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] > a[right])
	{
		if (a[mid] > a[left])
			return left;
		else
		{
			if (a[right] > a[mid])
				return right;
			else
				return mid;
		}

	}
	else
	{
		if (a[mid] > a[right])
			return right;
		else
		{
			if (a[left] > a[mid])
				return left;
			else
				return mid;
		}
	}
}

void QuickSort2(int* a, int n)          //以快排——挖坑法為例
{
	if (n == 1 || n == 0)
		return;
	int key = 0;
	int t = Choose_Key(a, 0, n - 1);  // 選出中間元素的下標
	swap(&a[key], &a[t]);         //與key所在位置的元素交換
	int left = 0; 
	int right = n - 1;
	int temp = a[key];
	while (left < right)
	{


		while (left<right && a[right]>temp)
			right--;
		a[left] = a[right];


		while (left<right && a[left]<= temp)
			left++;
		a[right] = a[left];



	}
	a[left] = temp;


	key = left;
	QuickSort2(a, key);
	QuickSort2(a + key + 1, n - key - 1);
	

}


總結:

  • 時間復雜度:O(N*logN) 最壞的情況:O(N^2)
  • 空間復雜:O(logN)
  • 穩定性:不穩定

歸并排序

基本思想
歸并排序(MERGE_SORT) 是建立在歸并操作上的一種有效的排序演算法,該演算法采用分治法的一個典型用例,將已有序的子序列合并,得到完全有序的序列;即使每個子序列有序,再使子序列段間有序,若將兩個有序表合并成一個有序表,稱為二路歸并,

在這里插入圖片描述

遞回法

如上圖所示,單趟排序的程序是一個很經典的問題,創建一個額外的陣列,將兩個順序的子序列排序,我們只要定義兩個個指標,分別指向 待排序的兩個序列,對指標所指的內容進行比較,如果滿足條件就進入新陣列,重復上述步驟直至所有元素都拷貝到新陣列,
單趟排序思路了解之后,我們直到單趟排序需要兩個有序的序列,所以就用遞回將陣列不斷劃分,當序列只有一個元素的時候我們認為他就是大小為1的有序序列,進行單趟排序,然后把排完序的子序列在進行合并的程序,

void _MergeSort(int* a, int left,int right,int *temp)
{
	int mid = (left + right) / 2;
	if (left >= right)
		return;
	_MergeSort(a, left, mid,temp);
	_MergeSort(a, mid + 1, right,temp);

	int cur1 = left, end1 = mid;
	int cur2 = mid + 1, end2 = right;
	int cur3 = left;

	while (cur1 <= end1 && cur2 <= end2)
	{
		if (a[cur1] <= a[cur2])
		{
			temp[cur3] = a[cur1];
			cur3++;
			cur1++;
		}
		else
		{
			temp[cur3] = a[cur2];
			cur3++;
			cur2++;
		}
	}
	while (cur1 <= end1)
	{
		temp[cur3] = a[cur1];
		cur3++;
		cur1++;
	}
	while (cur2 <= end2)
	{
		temp[cur3] = a[cur2];
		cur3++;
		cur2++;
	}
	for (int i = 0; i <= right; i++)
	{
		a[i] = temp[i];
	}

}

void MergeSort(int* a, int n)
{
	int* temp = (int*)malloc(n * sizeof(int));
	_MergeSort(a, 0, n - 1, temp);
}


非遞回法

非遞回的思路是

  1. 我們首先把陣列 兩個一組 分組,組內對半分成兩個子序列(序列大小為1),按照單趟排序排序
  2. 我們首先把陣列 四個一組 分組,組內對半分成兩個子序列(序列大小為2),按照單趟排序排序
  3. 直到陣列 小于等于 分組大小(這時最后一次),組內對半分成兩個子序列(序列大小為2),按照單趟排序排序

注意:
這里的難點是分組的邊界問題
在這里插入圖片描述

  1. 當第二個序列完全就不存在時,我們這時對end2進行修改,使其小于begin2,下面的單趟排序,由于受begin2 < end2的限制,會跳過一切與第二個序列有關的步驟,只留下對第一個序列的操作,對end1進行修改使其不會越界,
  2. 當第二個序列部分存在時,只需要修改end2,使其不會越界即可,
void fun(int cur1,int end1,int cur2,int end2,int cur3,int *temp,int *a)
{
	while (cur1 <= end1 && cur2 <= end2)
	{
		if (a[cur1] <= a[cur2])
		{
			temp[cur3] = a[cur1];
			cur3++;
			cur1++;
		}
		else
		{
			temp[cur3] = a[cur2];
			cur3++;
			cur2++;
		}
	}
	while (cur1 <= end1)
	{
		temp[cur3] = a[cur1];
		cur3++;
		cur1++;
	}
	while (cur2 <= end2)
	{
		temp[cur3] = a[cur2];
		cur3++;
		cur2++;
	}
	for (int i = 0; i <= end2; i++)
	{
		a[i] = temp[i];
	}
}
void MergeSortNonR(int* a, int n)
{
	int* p = (int*)malloc(n * sizeof(int));
	int index = 2;
	while ((n*2) / index)
	{
		int times = n / index;
		if (n % index != 0)
			times++;
		for (int i = 0; i < times; i++)
		{
			int cur1 = i * index, end1 = cur1+ index/2 -1;
			if (end1 > n - 1)
				end1 = n - 1;
			int cur2 = cur1 + index / 2;
			int	end2 = cur2 + index/2 - 1;
			if (end2 > n - 1)
				end2 = n - 1;
			int cur3 = cur1;
			fun(cur1, end1, cur2, end2, cur3, p, a);

		}
		index *= 2;

	}
	free(p);
}

總結:

  • 時間復雜度:O(N*logN)
  • 空間復雜:O(N)
  • 穩定性:穩定

計數排序

思想:

  1. 統計相同元素出現次數
  2. 根據統計的結果將序列回收到原來的序列中
    在這里插入圖片描述
void CountSort(int* a, int n)
{
	int max = a[0];
	int min = a[0];
	for (int i = 0; i < n; i++)   //找出最大最小值確定開辟陣列的范圍
	{
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}
	int range = max - min + 1;
	int* p = (int*)calloc(range, sizeof(int));
	for (int i = 0; i < n; i++)
	{
		p[a[i] - min]++;
	}
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (p[i])
		{
			a[j++] = i + min;
			p[i]--;
		}
	}
}

總結:

  • 時間復雜度:O(max(N,最大值與最小值之間元素的個數))
  • 空間復雜:O(范圍)
  • 穩定性:穩定

總結

排序方法平均情況最好情況最壞情況輔助空間穩定性
冒泡排序O(N^2)O(N)O(N^2)O(1)穩定
簡單選擇排序O(N^2)O(N^2)O(N^2)O(1)不穩定
直接插入排序O(N^2)O(N)O(N^2)O(1)穩定
希爾排序大概O(N^1.2)不確定O(N^2)O(1)不穩定
堆排序O(N*logN)O(N*logN)O(N*logN)O(1)不穩定
歸并排序O(N*logN)O(N*logN)O(N*logN)O(n)穩定
快速排序O(N*logN)O(N*logN)O(N^2)O(1)不穩定
計數排序O(max(N,最大值與最小值之間元素的個數))--O(范圍)穩定

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/303031.html

標籤:java

上一篇:2021秋招最新JAVA面試題|JVM剖析與常用的調優總結

下一篇:人的一生會遇到三種人,一個驚艷了時光,一個溫柔了歲月,一個講懂了“堆”

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more