C++ は 7 つの並べ替えアルゴリズムを実装しています

一種の

ソート処理中に、ソート対象のすべてのレコードがメモリに配置されているかどうかによって、ソートは内部ソートと外部ソートに分けられます。

内部ソート:ソート対象のすべてのレコードがメモリに配置されます。

外部ソート:ソート レコードの数が多すぎてメモリに配置できないため、ソート プロセスでは、内部メモリと外部メモリの間でデータを複数回交換する必要があります。

ソート処理中にソート対象のレコードを比較するかどうかによって、比較ソートと非比較ソートに分けることができます。

比較ソート:バブルソート、選択ソート、挿入ソート、マージソート、ヒープソート、クイックソートなど

非比較ソート:基数ソート、バケットソート、カウントソートなど


ソートアルゴリズムに影響を与える 3 つの指標

私たちが通常参照するソート アルゴリズムは、内部ソート アルゴリズムです. 内部ソートの場合、主に 3 つの側面の影響を受けます。それらは、時間性能、補助空間 (空間性)、およびアルゴリズムの複雑さです。

1.指標1 - タイムパフォーマンス

内部ソートには、比較と移動という 2 つの主な操作があります。比較はキーワード間の比較であり、移動はある場所から別の場所へのレコードの移動を指します。したがって、並べ替えアルゴリズムの時間オーバーヘッドは、その品質の重要な指標です。

2. インジケータ 2 - 補助スペース

補助スペースとは、アルゴリズムの品質を測定するもう 1 つの基準である、並べ替えによって占有されるストレージ スペースに加えて、アルゴリズムを実行するために必要なその他のストレージ スペースを指します。

3. 指標 3 - アルゴリズムの複雑さ

ここでのアルゴリズムの複雑さは、時間の複雑さではありません。アルゴリズムの複雑さに応じて、単純なアルゴリズムと改良されたアルゴリズムの 2 つのカテゴリに分けることができます。

シンプルなアルゴリズム: バブル、選択、挿入

改善されたアルゴリズム: ヒル、ヒープ、マージ、クイック ソート


構造とその他の機能

構造定義

typedef int ElemType;
#define MAXSIZE 10
#define N 10 
typedef struct {
    
    
	ElemType data[MAXSIZE];//存储的数据元素 
	int length;// 线性表当前长度
}SqList;//线性表 

交換機能

/*交换线性表L中下标为i和j的元素*/
void Swap(SqList &L,int i,int j) {
    
    
	int temp=L.data[i];
	L.data[i]=L.data[j];
	L.data[j]=temp;
}

線形テーブルを初期化する

 /* 初始化顺序线性表 */
bool InitList(SqList &L) 
{
    
     
    L.length=MAXSIZE;
    return true;
}

線形リストの要素を出力する

 /*输出线性表中的元素*/
 void PrintList(const SqList &L){
    
    
 	
 	for(int i=0;i<L.length;i++)
 		cout<<L.data[i]<<" ";
	cout<<endl;
 }
 

1. バブルソート

交換ソートの一種であるバブルソート (Bubble Sort) は、基本的な考え方は、隣接するレコードのキーワードを 2 つずつ比較し、逆順のレコードがなくなるまで逆順になっている場合は交換します。

元データが格納されている配列に対して、前から後ろに複数回スキャンを行い、各スキャンを1パスとし、2つのデータのサイズがビット列と一致しない場合、データを入れ替える。ソートされた場合、最大の数字を最後に置きます。各比較は各パスの最大数を底に沈めるため、各パスの比較数は徐々に減少します。

特徴: 昇順ソートでは、比較の各ラウンドで最大数が下に沈むため、各ラウンドでの相互比較の数は、前のラウンドよりも 1 つ少なくなります。

N 個の数値を N-1 回 (丸め) 並べ替え、昇順の並べ替え (大きなシンク) と降順の並べ替え (小さなシンク) を比較します。

バブルソート ベーシックエディション

/*
 	冒泡排序初级版
 	不算是标准的冒泡排序算法,因为不满足
	"两两比较相邻记录的" 的冒泡排序思想。
	
	算法思路:
		让每一个数据元素和后一个数据元素
	进行比较,如果大则进行交换,在第一趟
	比较,最后一个元素一定是最大值。 
	
	
 */
 void EasyBubbleSort(SqList &L){
    
    
 	for(int i=0;i<L.length-1;i++)//n个数比较 n-1趟 
 		for(int j=0;j<L.length-i;j++)//比较n-i次 
 			if(L.data[j]>L.data[j+1])
			 	Swap(L,j,j+1) ;//交换 L->data[i]与 L->data[j]的值 
	
 }

通常のバブルソート

/*冒泡排序*/
 void BubbleSort(SqList &L) {
    
    
 	for(int i=0;i<L.length-1;i++)
 		for(int j=L.length-1;j>=i;j--)//将L.data[i] 元素归位 
 			if(L.data[j]>L.data[j+1])//相邻的练个元素反序时 
 				Swap(L,j,j+1);//将 L->data[i]与 L->data[j]的值 交换
 }

改善されたバブル ソート

/*
 	冒泡排序改进
 	假如序列{2,1,3,4} 
 	如果比较第一二次之后,在之前
	的冒泡排序还会一直比下去,从i=1一直
	执行到i=3,数量越大,则时间耗得越长,
	为了避免这种情况,设计了一个标志位。
	在每一次交换过后,置为1,循环判断如果为1则执行
	否则跳过本趟比较。 
 */
 void BubbleSortImprove(SqList &L){
    
    
 	bool flag= true;
 	for(int i=0;i<L.length-1&&flag;i++){
    
     
 		flag=false;
 		for(int j=L.length-1;j>=i;j--){
    
    
 			if(L.data[j-1]>L.data[j]){
    
    
 				Swap(L,j-1,j);
 				flag=true;//如果有数据交换,则flag为true 
			 }
		 }
	 }
 	
 }

バブルソートの総時間計算量は O(n^2) です


2.簡易選択ソート

アルゴリズムの考え方: n-i+1 個のレコードの中から、n 回のキーワードを比較して最小のキーワードを持つレコードを選択し、i 番目 (1<=i<=n) のレコードと交換します。

この最小のキーワードを持つ要素を選択する単純な比較方法が、単純選択ソートの名前の由来です。

 /*简单选择排序*/
 void SelectSort(SqList &L){
    
    
 	
 	for(int i=0;i<L.length;i++){
    
    
 		int min=i;//将当前的下标定义为最小值下标
		for(int j=i+1;j<L.length;j++) {
    
    
		 	
		 	if(L.data[min]>L.data[j]){
    
    //如果当前的值比下标为min中的还小 
		 		min=j;//把这个下标给min 
			 }
		}
		if(i!=min) //如果min与当前i值不想等,说明找到最小值 
			Swap(L,i,min);
	 }
 }
 

単純選択ソートとバブル ソートの時間計算量は O(n^2) ですが、単純選択ソートのパフォーマンスはバブル ソートよりわずかに優れています。


3. 挿入ソート

直接挿入ソート (配列の最初の要素をバッファーとして使用)

アルゴリズムのアイデア: 並べ替えられた順序付きリストにレコードを挿入すると、レコード数が 1 つ増えた新しい順序付きリストが得られます。

/*
 	直接插入排序:
 	第一种写法, 数组的第一个元素不存储,起到一个缓冲器的作用 
	线性表中的第一个元素在这里起到哨兵的作用 
	当前元素如果比后一个元素小,那么将当前元素设置为哨兵,
	再把当前元素之前的所有元素与哨兵进行比较,如果比哨兵小,
	那么哨兵为这个元素,反之,则进行交换。
	所以,最后一次比较,哨兵是最后一轮的最小元素。 
 */
 void InsertSort(SqList &L) {
    
    
 	int i,j;
 	for( i=2;i<L.length;i++){
    
    
 		if(L.data[i]<L.data[i-1]){
    
    
 			L.data[0]=L.data[i];//设置哨兵,哨兵为每轮比较的最小元素 
 			for( j=i-1;L.data[j]>L.data[0];j--){
    
    
 				L.data[j+1]=L.data[j];//记录后移 这里的j+1其实是i 
			}
			L.data[j+1]=L.data[0];//交换元素,把哨兵插入到正确的位置 
		 }
	 }
 }
 

この書き込み方法のソート レコードがランダムである場合、比較と移動の平均数は ≈ (n 2)/4 であり、時間計算量は O(n 2) ですが、直接挿入ソートのパフォーマンスはそれよりわずかに優れています。バブリングと選択ソートの。

アルゴリズムのアイデア: 順序付けされていない領域の開始要素 a[i] (1<=i<=n-1) を順序付けられた領域 a[0...i-1] の適切な位置に挿入すると、a[0 ...i ] が新しい順序付き領域になります。

直接挿入ソート(直接交換)

 /*
 	直接插入排序
	第二种写法,不采用数组第一个元素为缓冲器,
	默认第一个元素是有序的,直接进行交换 
	 
 */
 void InsertSort2(SqList &L) {
    
    
 	for(int i=1;i<L.length;i++){
    
    
 		int key=L.data[i];
 		int j=i-1;
 		while(j>=0&&L.data[j]>key){
    
    //定位,当前一个元素比key大  
 			L.data[j+1]=L.data[j];//插入,大记录后移,小记录前移 
 			j--;
		 } 
		 //如果j<0,则说明依次与前面进行比较比完了,此时是data[0]=key; 
		 //如果data[j]<key,则不比较data[j],key的值不变。 
		L.data[j+1]=key; 
	 }
 }

説明: 2 番目の書き込み方法の各パスで生成される順序付け領域は、グローバルな順序付け領域であるとは限りません、つまり、順序付け領域内の要素が最終位置に配置されるとは限りません。 .

バイナリ挿入ソート

アルゴリズムの考え方: 非順序領域の先頭要素 a[i] (1<=i<=n-1) を順序領域 a[0...i-1] に挿入し、逐次比較の方法を使用します。ここでは二分探索の手法を用いて a[0...i-1] 内の挿入位置を見つけて要素を移動することを二分挿入ソートとも呼びます。

/*
 	折半插入排序
 	利用二分查找定位,再进行插入排序 
 */
 void BinInsertSort(SqList &L){
    
    
 	int i,j,low,mid,high;
 	for( i=1;i<L.length;i++){
    
    
 		if(L.data[i]<L.data[i-1]){
    
    
 			int tmp=L.data[i];
 			low=0;
 			high=i-1;
 			while(low<=high){
    
    //在L.data[low...high] 中查找插入的位置 
 				mid=(low+high) /2;//取中间位置 
 				if(tmp<L.data[mid])
					high=mid-1;//插入点在左半区间
				else
					low=mid+1;//插入点在右半区间  
			 }
			for(j=i-1;j>=high+1;j--) {
    
    
				L.data[j+1]=L.data[j];
			}
			L.data[high+1]=tmp;
		 }
	 }
 	
 }

直接挿入ソートと比較すると、ハーフソートは移動要素のパフォーマンスを向上させず、キーワードの比較回数を減らすだけです. 平均的なパフォーマンスの観点からは、ハーフ検索はシーケンシャル検索よりも優れているため、ハーフ挿入ソートも直接よりも優れています.挿入ソート. 挿入ソートの空間複雑度は O(1) です.


4.クイックソート

バブルソートで改良されており、ソートするn個の要素の中からランダムに要素(通常は最初の要素)をベンチマークとして選び、その要素を適切な位置に配置した後、これでデータ列を分割するというのが基本的な考え方です。要素 2 つの部分。要素より小さい要素を手前に、要素より大きい要素を奥に、要素を2つの要素の真ん中に配置する(要素ホーミングとなる)この処理をクイックソート(パス部門ともいう)。

次に、生成された 2 つのパーツに対して上記のプロセスを繰り返し、各パーツに要素が 1 つだけになるか、空になるまで繰り返します。簡単に言えば、各分割は、テーブルの最初の要素を適切な位置に配置し、テーブルを2つに分割し、分割されたサブテーブルの長さが1または0になるまで、サブテーブルでこの分割を再帰的に続けます。

 /* 
 	快排 
 */
 void QuickSort(SqList &L,int l,int r){
    
    
 	//判断合法性 递归结束 
 	if(l>=r) return ;
 	
 	int key=L.data[l],i=l-1,j=r+1;
 	while(i<j){
    
    
 		//保证左边的<key,右边的>key 
 		do i++;while(L.data[i]<key) ;
 		do j--;while(L.data[j]>key);
 		if(i<j)Swap(L,i,j);
	 }
	 QuickSort(L,l,j);
	 QuickSort(L,j+1,r);
 }

5.マージソート

基本的な考え方:

マージのアイデアを使用して実装されたソート方法は、初期シーケンスに n レコードが含まれていると仮定すると、n 個の順序付けられたサブシーケンスと見なすことができ、各サブシーケンスの長さは 1 であり、ペアでマージすると、⌈n を取得できます。 / 2⌉ (⌈x⌉ は x 以上の最小の整数を表す) は、長さ 2 または 1 の順序付けられた部分シーケンスを 2 つずつマージし、長さ n の順序付けられたシーケンスが得られるまでこれを繰り返します。

/*
 	归并排序 
*/
int temp[MAXSIZE];
void MergeSort(SqList &L, int l,int r){
    
    
	if(l>=r)return ;
	//划分区间
	int mid=(l+r)/2;
	MergeSort(L,l,mid);
	MergeSort(L,mid+1,r);	
	
	int k=0,i=l,j=mid+1;	
	//把两边区间最小的数存入temp中 
	while(i<=mid&&j<=r) {
    
    
		if(L.data[i]<=L.data[j])
			temp[k++]=L.data[i++];
		else
			temp[k++]=L.data[j++];
	}
	
	//把两边任一剩下的数存入temp中
	while(i<=mid) 
		temp[k++]=L.data[i++];
	while(j<=r)
		temp[k++]=L.data[j++];
		
	//从左边开始把temp中的数放回至a中 
	for(i=l,j=0;i<=r;i++,j++)	
		L.data[i]=temp[j];
}
 

7 つのアルゴリズムのパフォーマンスのまとめ

ソート方法 時間の複雑さ スペースの複雑さ 安定 複雑
平均的なケース 最悪の場合 最良の場合
バブルソート O(n^2) O(n^2) の上) O(1) 安定させる 単純
単純選択ソート O(n^2) O(n^2) O(n^2) O(1) 不安定 単純
直接挿入ソート O(n^2) O(n^2) の上) O(1) 安定させる 単純
バイナリ挿入ソート O(n^2) O(n^2) の上) O(1) 安定させる 単純
クイックソート O(nlogn) O(n^2) O(nlogn) O(ログ) 不安定 より複雑
双方向マージソート O(nlogn) O(nlogn) O(nlogn) の上) 安定させる より複雑

おすすめ

転載: blog.csdn.net/qq_42242452/article/details/124641223