圖解法----十大排序算法彙總----(總有你需要的那種)

引言

十大排序算法分別爲:

冒泡排序、選擇排序、插入排序、希爾排序、歸併排序、快速排序、堆排序、計數排序,基數排序、桶排序

十種排序算法一共可分爲兩類。分別是比較排序非比較排序

  • 比較排序:通過比較各個數的大小來交換各個數順序,達到排序的效果。
  • 非比較排序:不用通過比較,就可以達到排序的效果,平均時間複雜度比比較排序低。

十種排序算法的複雜度

穩定性:比如對期末數學成績進行排序,假如小明和小紅都是95分,在排序前小明在小紅的前面,如果排序後,小明仍然在小紅的前面,那就說明這個排序算法是穩定的,否則這種排序就是不穩定的。

時間複雜度:排序時,對數據操作的總次數。

空間複雜度:排序時,需要花費的內存空間。

        下面就會對十種排序算法彙總,但是不會展開說,如果對某個算法不太清楚,可以點擊下面的鏈接,在鏈接裏面有詳細的講解 

  1. 經典算法(一)----冒泡排序----圖解法讓你快速入門

  2. 經典算法(二)----選擇排序----圖解法讓你快速入門

  3. 經典算法(三)----插入排序----圖解法讓你快速入門

  4. 經典算法(四)----希爾排序----圖解法讓你快速入門

  5. 經典算法(五)----歸併排序----圖解法讓你快速入門

  6. 經典算法(六)----快速排序----圖解法讓你快速入門

  7. 經典算法(七)----  堆排序 ----圖解法讓你快速入門

  8. 經典算法(八)----計數排序----圖解法讓你快速入門

  9. 經典算法(九)----基數排序----圖解法讓你快速入門

  10. 經典算法(十)----  桶排序  ----圖解法讓你快速入門

上面的每篇文章都對各種算法有詳細得講解過程,可以 哪裏不會點哪裏!!!

一、冒泡排序

冒泡排序是一種簡單的排序算法,它的實現過程就是每遍歷一遍,把最大的數放到最後面,直到排序完成爲止

算法描述:

  1. 從頭到尾遍歷要排序的數,每次比較相鄰的兩個數,如果第一個比第二個大,就交換位置。
  2. 直到遍歷結束,這時候最大的數就會被移動到最後,那這個數 就處理完畢了。
  3. 除了已經處理好的數,其他的數重複1-2的操作,直到排序完成

動畫演示:

代碼實現:

#include<iostream>
using namespace std;
//冒泡排序函數    穩定 
void BubbleSort(int arr[],int len)
{
	int temp;
	for(int i=0;i<len-1;i++)
	{
		for(int j=0;j<len-i-1;j++)
		{
			if(arr[j]>arr[j+1])
			{
				temp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=temp;
			}
		}
	}
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={5,8,6,2,7,1,9,3,4};
	int len=9;//要排序的數組長度 
	
	//排序 
	BubbleSort(arr,len);
	
	//輸出 
	printf(arr,len);
	return 0;
}

二、選擇排序

選擇排序的原理:從未排序的數組中選擇出一個最大的放到數組的第一個位置,重複前面的操作,繼續從未排序的數中選擇最大的,放到第二個位置,直到排序完成。

算法描述:

  1. 遍歷所未排序的數,找出一個一個最小的數,記錄它的數組下標。
  2. 拿最小的數和未排序數組的第一個數交換位置。
  3. 重複1-2操作,直到排序完成。

動畫演示:

代碼實現:

#include<iostream>
using namespace std;
//選擇排序函數    不穩定 
void SelectionSort(int arr[],int len)
{
	int temp;
	for(int i=0;i<len-1;i++)
	{
		int minx=i;
		for(int j=i+1;j<len;j++)
		{
			if(arr[j]<arr[minx]) //尋找最小的數 
				minx=j;         //記錄對應的下標 
		}
		temp=arr[i];
		arr[i]=arr[minx];
		arr[minx]=temp;
	}
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={5,8,6,2,7,1,9,3,4};
	int len=9;//要排序的數組長度 
	
	//排序 
	SelectionSort(arr,len);
	
	//輸出 
	printf(arr,len);
	return 0;
}

三、插入排序

插入排序的原理:首先默認要排序的數組第一個元素爲有序序列,然後掃描未排序的每個數,把每個數都插入到有序序列中,插入的方法就是從後向前掃描有序序列,找到一個合適的位置,使插入後的序列仍然是有序序列。

算法描述:

  1. 默認第一個數爲有序序列,開始遍歷未排序的每一個數。
  2. 當遍歷到某個數時,首先拿一個變量把這個數存取來,再拿這個數和有序序列從後向前比較,如果有這個數比有序序列小,且沒有有序序列還有數未比較,那就把有序序列的數後移(爲插入數準備位置)。
  3. 重複比較操作,直到找到合適的位置,把要插入的數放進去即可。
  4. 遍歷未排序的每個數,直到所有的數都插入到有序序列中之後,數組就排序好了。

動畫演示:

代碼實現:

#include<iostream>
using namespace std;
//插入排序函數    穩定 
void InsertionSort(int arr[],int len)
{
	int temp;
	for(int i=1;i<len;i++)
	{
		int index=i-1;//前i-1個數已經排序好了
		int current=arr[i];
		while(index>=0&&arr[index]>current)
		{
			arr[index+1]=arr[index]; 
			index--; 
		}
		arr[index+1]=current;
	}
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={5,8,6,2,7,1,9,3,4};
	int len=9;//要排序的數組長度 
	
	//排序 
	InsertionSort(arr,len);
	
	//輸出 
	printf(arr,len);
	return 0;
}

四、希爾排序

希爾排序就是插入排序的改進版,在希爾排序中引入一個 “增量”,實現了分組插入排序的過程,首先把各自分組用插入排序實現有序,再實現數組的整體有序。

算法描述:

  1. 定義增量,確定增量的變化過程(最後增量一定要變成1)。
  2. 對於每個增量,都用一邊插入排序,實現各自分組的有序。
  3. 最後一增量爲1時,再用一遍插入排序,就是實現了整理有序。

代碼實現:

#include<iostream>
using namespace std;
//希爾排序函數    不穩定 
void ShellSort(int arr[],int len)
{
	//increment是增量 
	int increment=len;
	do{
		increment=increment/3+1;
		for(int i=increment;i<len;i++)
		{
			int current=arr[i];
			int index=i-increment;
			while(index>=0&&arr[index]>current)
			{
				arr[index+increment]=arr[index];
				index-=increment;
			}
			arr[index+increment]=current;
		}
		
	} while(increment>1);
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{

	//要排序的數組 
	int arr[]={5,8,6,2,7,1,9,3,4};
	int len=9;//要排序的數組長度 
	//排序 
	ShellSort(arr,len);
	//輸出 
	printf(arr,len);
	   
	return 0;
}

五、歸併排序

        歸併排序的過程一共分兩大步,第一步:分,把一個大數組分成一個個小數組;第二步:合,從一個個的小數組,合成大數組,在合的過程中,按照大小順序進行合成,合成的大數組就是有序的,這樣就達到了排序的效果。

算法描述:

  1. 利用分治法,把數組實現分的操作。
  2. 再寫一個實現數組合並的函數,合成之後,數組實現了有序。

動畫演示:

代碼實現:

#include<iostream>
using namespace std;

//將兩個有序數列arr[first...mid]和arr[mid...last]合併
void MergeArray(int arr[],int first,int mid,int last,int temp[])
{
	int i=first, j=mid+1;
	int m=mid  , n=last;
	int k=0;
	
	while(i<=m&&j<=n)
	{
		if(arr[i]<=arr[j])
			temp[k++]=arr[i++];
		else
			temp[k++]=arr[j++];
	}
	
	while(i<=m)
		temp[k++]=arr[i++];
		
	while(j<=n)
		temp[k++]=arr[j++];
		
	for(i=0;i<k;i++)
		arr[first+i]=temp[i];
} 


//歸併排序函數    穩定 
void MergeSort(int arr[],int first,int last,int temp[])
{
	if(first<last)
	{
		int mid=(first+last)/2;
		MergeSort(arr,first,mid,temp);
		MergeSort(arr,mid+1,last,temp);
		MergeArray(arr,first,mid,last,temp);
	}
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" "; 
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={5,8,6,2,7,1,9,3,4};
	int len=9;//要排序的數組長度 
	int temp[9];
	//排序 
	MergeSort(arr,0,len-1,temp);
	
	//輸出 
	printf(arr,len);
	return 0;
}

六、快速排序

        快速排序的原理就是首先定一個 “基準數”,再把基準數移動到合適的位置,這個位置要滿足他前面位置的數都比基準數小,它後面位置的數,都比基準數大。

算法描述:

  1. 首先定一個基準數,在設置兩個哨兵,這兩個哨兵分別從後面往前走、從前面往後走。
  2. 如果後面的哨兵找到了比基準數小的數,那就停下。
  3. 如果前面的哨兵找到了比基準數大的數,那就停下。
  4. 交換這兩個哨兵位置的數。然後這兩個哨兵再按照自己先前的方向繼續走。
  5. 通過上面幾步,就把基準數移動到了合適的位置,然後在利用分治的思想,以基準數爲分界線,把數組分爲兩部分,這兩部分都執行上面的操作。直到數組有序位置。

動畫演示:

代碼實現:

#include<iostream>
using namespace std;
//快速排序函數    不穩定 
void QuickSort(int arr[],int first,int last)
{
	if(first>last)//控制遞歸結束 
		return ;
	int i=first,j=last;
	int temp=arr[first];//基準數
	while(i!=j)//i和j不碰頭 
	{
		//順序很重要,要先從右往左找 
		while(arr[j]>=temp&&i<j) 
			j--;
		//上面循環結束的條件有兩種,
		//一是查到了比基準數小的,
		//二是 i與j碰頭了
		while(arr[i]<=temp && i<j)
			i++; 
		//循環結束條件同上
		
		//下面交換兩個數在數組中的位置
		if(i!=j) //兩個循環結束的條件都不是i和j碰頭
		{
			int t=arr[i];
			arr[i]=arr[j];
			arr[j]=t;
		} 
	} 
	//最終一定會碰頭,交換基準數和碰頭那個位置的數 
	arr[first]=arr[i];
	arr[i]=temp; 
		 
	QuickSort(arr,first,i-1);//分的前一部分 
	QuickSort(arr,i+1,last); //分的後一部分 
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={5,8,6,2,7,1,9,3,4};
	int len=9;//要排序的數組長度 
	
	//排序 
	QuickSort(arr,0,len-1);
	
	//輸出 
	printf(arr,len);
	return 0;
}

七、堆排序

        堆排序的算法實現過程就是利用了大頂堆或者小頂堆,比如要從小到大排序,那就先把未排序的數組轉換成大頂堆,然後把最大的數放到調到數組尾部,重複前面的操作即可。

算法描述:

  1. 首先堆化數組,把要排序的數轉換成大頂堆。
  2. 把大頂堆中堆頂數據移動到數組尾部,然後在堆化數組。
  3. 重複第二步的操作,知道所有的數據都排序完成。

代碼實現:

#include<iostream>
using namespace std;
//堆排序函數    穩定 

void HeapAdjust(int arr[],int first,int last)
{
	int temp=arr[first];//暫存“根”結點 
	int j;//子結點 
	for(j=2*first;j<=last;j=j*2)
	{
		//下面if語句的作用是找出子結點中比較大的那個 
		//j是左節點,j+1是右節點,
		//如果右節點大,那j+1就可以了,如果左節點大那就不用+1
		//執行完下面的語句,j下標是較大的那個子結點的下標 
		if(j<last &&arr[j]<arr[j+1])
			j++;
		
		//下面if語句的作用是如果“根”結點大於子結點,
		//結束查找即可
		if(temp>arr[j])
			break; 
		
		//理解下面兩條語句可以類比插入排序,
		//還記得插入排序中的元素後移嗎? 這裏是“下移” 
		arr[first]=arr[j];
		first=j; //如果下移,記錄對應的下標,方便下次下移 
	}
	//同樣類比插入排序,把要插入的元素,放到合適的位置 
	arr[first]=temp; 
} 

void HeapSort(int arr[],int len) 
{
	for(int i=len/2;i>0;i--)
	{
		HeapAdjust(arr,i,len);
	}
	for(int i=len;i>0;i--)//需要交換幾次位置的次數 
	{
		//下面三行的代碼是把堆頂最大的元素和堆尾最後一個元素換位置
		//這樣一來,最大元素就在數組尾部了,
		//因此大頂堆 是用來從小 到大排序的 
		int temp=arr[1];
		arr[1]=arr[i];
		arr[i]=temp;
		
		//對堆剩下的元素繼續排序。 
		HeapAdjust(arr,1,i-1);
	}
}

//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=1;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 ,爲了方便理解,數組下標從1開始 
	int arr[]={0,5,8,6,2,7,1,9,3,4};
	int len=10;//要排序的數組長度 
	
	//排序 
	HeapSort(arr,len-1);
	
	//輸出 
	printf(arr,len);
	return 0;
}

八、計數排序

        計數排序的思想就是統計每個數的出現的次數,然後再根據統計的次數,輸出每個數。

算法思想:

  1. 首先找出要排序數組的最大值和最小值。
  2. 根據最大值和最小值確定統計數組的長度。
  3. 遍歷一遍,統計出來每個數出現的次數。
  4. 按照統計數組,輸出每個數。

動畫演示:

代碼實現:

#include<iostream>
using namespace std;
//計數排序函數    穩定 
void CountingSort(int arr[],int len)
{
	int minx=99999,maxn=-99999;
	//下面for循環是求出來要排序數組的最大值和最小值 
	for(int i=0;i<len;i++)
	{
		if(arr[i]>maxn)
			maxn=arr[i];
		if(arr[i]<=minx)
			minx=arr[i];
	}
	//cout<<maxn<<" "<<minx<<endl;
	
	int l=maxn-minx+1;//變量 l 是要開闢數組的長度 
	int count[l]={0};
	
	//下面的代碼是統計作用,不過統計時要減去數組最小值,方便存儲 
	for(int i=0;i<len;i++)
		count[arr[i]-minx]++;
	
	//累加 
	for(int i=0;i<l;i++)
	{
		count[i]+=count[i-1];
	}
	
	int temp[len]={0};//用來排序好的數組 
	 
	for(int i=len-1;i>=0;i--)//逆序遍歷。 
	{
		int ans=arr[i]-minx;
		temp[--count[ans]]=arr[i];//先減減的原因是數組下標從0開始的
	}
	//經過上面的逆序遍歷,現在temp數組就是排序好的成績數組 
	
	//把排序好的數組放到arr數組中 ,方便後面的打印 
	for(int i=0;i<len;i++)
		arr[i]=temp[i];
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={1,5,4,2,2,3,1,1,3,1,5};
	int len=11;//要排序的數組長度 
	
	//排序 
	CountingSort(arr,len);
	
	//輸出 
	printf(arr,len);
	return 0;
}

九、基數排序

        基數排序的原理就是按位進行排序,比如從低位到高位,首先先個位,再十位百位,直到達到最高位。每一次的排序都是按計數排序來實現的

算法描述:

  1. 首先求出要排序數的最大值一共有幾位,根據這個決定排序的次數。
  2. 每位排序是都是用計數排序實現的

動畫描述:

代碼實現:

#include<iostream>
using namespace std;

//統計數組中 最大數的位數 
int max(int arr[],int len)
{
	int maxn=0;
	for(int i=0;i<len;i++)
	{
		int count=0,data=arr[i];
		while(data)
		{
			count++;
			data/=10;
		}
		if(count>maxn)
			maxn=count;
	}
	return maxn;
} 

//基數排序函數    穩定 
void RadixSort(int arr[],int len)
{
	int maxn=max(arr,len);//首先求出最大位數
	int num=1;//求位數用 
	for(int k=0;k<maxn;k++)//根據位數,判斷遍歷幾次 
	{
		int count[10]={0};
		for(int i=0;i<len;i++)//把數據放入桶內 
		{
			int ans=arr[i]/num%10;
			count[ans]++;
		}
		
		//下面的過程都是計數排序的過程。
		//根據計數排序,把桶內數據排序即可 
		for(int i=1;i<10;i++)//累加 
		{
			count[i]=count[i]+count[i-1];
		}
		
		int temp[len]={0};//用來存放排序後的結果 
		for(int i=len-1;i>=0;i--)//逆序遍歷,保證穩定性 
		{
			int ans=arr[i]/num%10;
			temp[count[ans]-1]=arr[i]; 
			count[ans]--;  
		} 
		
		for(int i=0;i<len;i++)
			arr[i]=temp[i]; 
		
		num*=10;//求更高位 
	}
		
}
//輸出數組的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{
	//要排序的數組 
	int arr[]={321,563,454,219,541,632,225,678,59,356};
	int len=10;//要排序的數組長度 
	
	//排序 
	RadixSort(arr,len);
	
	//輸出 
	printf(arr,len);
	return 0;
}

十、桶排序

        桶排序的思想就是首先定義幾個桶,再把數據放到桶內,然後再把每個桶都排序,最後按照順序輸出。桶排序不太常用

算法描述:

  1. 設計好有幾個桶,每個桶的範圍都爲多少
  2. 對每個桶進行排序
  3. 按照順序,把每個桶的數據都取出來

桶排序不常用的原因:

  1. 每個桶如果用數組存數據,那如果數組不夠用怎麼辦?,因爲一組數可能有的很集中,有的很分散,如果數組開大了,那分散的數,就會造成空間浪費。
  2. 如果每個桶用鏈表存,那就不用提前開闢空間了,但是又存在了時間問題,因爲如果對鏈表進行排序,只能進行一遍一遍的遍歷(訪問鏈表的數據不能隨機訪問),那時間複雜度就特別大了。
  3. 如果一個要排序的數據特別集中,比如有1000個數據,結果超過了800個數據在一個桶內,那桶排序的優勢更體現不出來了。
     

本文的參考和引用:https://www.cnblogs.com/onepixel/articles/7674659.html

 

這篇文章到這就結束了,當然算法排序永遠不會結束,各種算法還有各種優化的版本,這裏就不寫了。

創作不易(尤其是動畫的製作),如果本文對你起到了一些幫助,何不點個贊再走呢!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章