古典的なソートアルゴリズムの紹介とソースコード(c ++の実装)

 

アルゴリズム分類

10の一般的なソートアルゴリズムは、次の2つのカテゴリに分類できます。

  • 比較ソート:要素の相対的な順序は比較によって決定されます。時間計算量はO(nlogn)を超えることができないため、非線形時間比較ソートとも呼ばれます。
  • 非比較ソート:要素の相対的な順序は比較によって決定されません。比較ソートに基づいて時間の下限を突破し、線形時間で実行される可能性があるため、線形時間非比較ソートとも呼ばれます。

アルゴリズムの複雑さ

関連する概念

  • 安定:aが元々bの前にあり、a = bの場合、ソート後もaはbの前にあります。
  • 不安定:aが元々bの前にあり、a = bの場合、ソート後にaがbの後ろに表示されることがあります。
  • 時間計算量:ソートされたデータに対する操作の総数。nが変化したときの演算数の法則を反映しています。
  • スペースの複雑さ:コンピューターのアルゴリズムを指します

内部実行に必要なストレージスペースの測定値。これはデータサイズnの関数でもあります。

1.バブルソート(バブルソート)

バブルソートは単純なソートアルゴリズムです。ソートするシーケンスに繰り返しアクセスし、一度に2つの要素を比較し、順序が間違っている場合はそれらを交換しました。シーケンスにアクセスする作業は、交換が不要になるまで繰り返されます。これは、シーケンスがソートされたことを意味します。このアルゴリズムの名前の由来は、要素が小さいほど、交換によってシーケンスの先頭にゆっくりと「フロート」するためです。

1.1アルゴリズムの説明

  • 隣接する要素を比較します。最初のものが2番目のものよりも大きい場合は、2つを交換します。
  • 最初の最初のペアから最後の最後のペアまで、隣接する要素の各ペアに対して同じ作業を行い、最後の要素が最大数になるようにします。
  • 最後の要素を除くすべての要素に対して上記の手順を繰り返します。
  • ソートが完了するまで、手順1〜3を繰り返します。

1.2アニメーションデモ

 1.3コードの実装

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 100;
int arr[maxn], length;

//设置随机数组
void setRandArray(int a[]) {
	srand((unsigned)time(NULL));
	for (int i = 0; i < length; i++) {
		int j = rand() % 100;
		a[i] = j;
	}
}

//输出数组
void showArray(int* a) {
	cout << "数组元素内容:";
	for (int i = 0; i < length; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}

//冒泡排序
void bubbleSort(int *arr) {
	for (int i = 0; i < length - 1; i++) {
		for (int j = 0; j < length - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
	}
}

int main()
{
	cout << "输入元素个数:";
	cin >> length;
	setRandArray(arr);
	showArray(arr);
	bubbleSort(arr);
	cout << "冒泡排序后的数组:" << endl;
	showArray(arr);
	return 0;
}

2.選択ソート(選択ソート)

Selection-sort(Selection-sort)は、シンプルで直感的なソートアルゴリズムです。その動作原理:最初にソートされていないシーケンスで最小(大きい)要素を見つけ、それをソートされたシーケンスの先頭に格納し、次に残りのソートされていない要素から最小(大きい)要素を見つけて、それをソートされたシーケンス最後に。など、すべての要素が並べ替えられるまで続きます。

2.1アルゴリズムの説明

nレコードの直接選択と並べ替えは、直接選択してn〜1回並べ替えることができ、順序付けられた結果を取得できます。具体的なアルゴリズムは次のとおりです。

  • 初期状態:無秩序な領域はR [1..n]であり、順序付けられた領域は空です。
  • i番目のソート(i = 1,2,3 ... n-1)の開始時に、現在の順序付けられた領域と無秩序な領域はR [1..i-1]とR(i..n)です。それぞれ。このソートトリップは、現在の無秩序領域から最小のキーを持つレコードR [k]を選択し、それを無秩序領域の最初のレコードRと交換して、R [1..i]とR [i + 1..nを作成します。 )レコード数が1増加した新しい順序付けられた領域と、レコード数が1つ減少した新しい無秩序領域になります。
  • n-1パスの終わりに、配列が順序付けられます。

2.2動画デモンストレーション

2.3コードの実装

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 100;
int arr[maxn], length;

//设置随机数组
void setRandArray(int a[]) {
	srand((unsigned)time(NULL));
	for (int i = 0; i < length; i++) {
		int j = rand() % 100;
		a[i] = j;
	}
}

//输出数组
void showArray(int* a) {
	cout << "数组元素内容:";
	for (int i = 0; i < length; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}

//选择排序
void selectionSort(int arr[]) {
	int minIndex, temp;
	for (int i = 0; i < length - 1; i++) {
		minIndex = i;
		for (int j = i + 1; j < length; j++) {
			if (arr[j] < arr[minIndex]) {
				minIndex = j;
			}
		}
		temp = arr[i];
		arr[i] = arr[minIndex];
		arr[minIndex] = temp;
	}
}

int main()
{
	cout << "输入元素个数:";
	cin >> length;
	setRandArray(arr);
	showArray(arr);
	selectionSort(arr);
	showArray(arr);
	return 0;
}

 

3.挿入ソート(挿入ソート)

Insertion-Sortのアルゴリズムの説明は、シンプルで直感的なソートアルゴリズムです。その動作原理は、順序付けられたシーケンスを構築することです。ソートされていないデータの場合、ソートされたシーケンスで後ろから前にスキャンし、対応する位置を見つけて挿入します。

3.1アルゴリズムの説明

一般的に、挿入ソートはインプレースを使用して配列に実装されます。具体的なアルゴリズムは次のとおりです。

  • 最初の要素から始めて、要素はソートされたと見なすことができます。
  • 次の要素を取り出し、並べ替えられた要素のシーケンスで後ろから前にスキャンします。
  • 要素(ソート済み)が新しい要素よりも大きい場合は、要素を次の位置に移動します。
  • ソートされた要素が新しい要素以下になる位置が見つかるまで、手順3を繰り返します。
  • 新しい要素をこの位置に挿入した後。
  • 手順2〜5を繰り返します。

3.2動画デモンストレーション

3.2コードの実装

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 100;

//设置随机数组
void setRandArray(int a[], int length) {
	srand((unsigned)time(NULL));
	for (int i = 0; i < length; i++) {
		int j = rand() % 100;
		a[i] = j;
	}
}

//输出数组
void showArray(int* a, int length) {
	cout << "数组元素内容:";
	for (int i = 0; i < length; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}

//插入排序
void insertionSort(int *arr,int len){
	int preIndex,current;
	for(int i = 1; i < len; i++){
		preIndex = i - 1;
		current = arr[i];
		while(preIndex >= 0 && arr[preIndex] > current){
			arr[preIndex + 1] = arr[preIndex];
			preIndex--;
		}
		arr[preIndex + 1] = current;
	}
} 

int main()
{
	int arr[maxn], length;
	cout << "数组元素个数:";
	cin >> length;
	setRandArray(arr,length);
	showArray(arr,length);
	insertionSort(arr,length);
	showArray(arr,length);
	return 0;
}

4.ヒルソート(シェルソート)

シェルは1959年に発明されました。これは、単純な挿入ソートの改良版であるO(n2)を破る最初のソートアルゴリズムです。それと挿入ソートの違いは、最初に遠い要素を比較することです。ヒルソートは、縮小増分ソートとも呼ばれます

4.1アルゴリズムの説明

まず、直接挿入ソートのために、ソートされるレコードのシーケンス全体をいくつかのサブシーケンスに分割します。具体的なアルゴリズムの説明は次のとおりです。

  • インクリメンタルシーケンスt1、t2、...、tkを選択します。ここでti> tj、tk = 1;
  • インクリメントシーケンスの数kに従ってシーケンスをk回ソートします。
  • 各ソートパスでは、対応する増分tiに従って、ソートされるシーケンスが長さmのいくつかのサブシーケンスに分割され、各サブテーブルが直接挿入されてソートされます。増分係数のみが1の場合、シーケンス全体がテーブルとして扱われ、テーブルの長さはシーケンス全体の長さになります。

まず、大きなデータセットをいくつかのグループに分割し(論理的にグループ化)、次に各グループを個別に挿入して並べ替えます。現時点では、挿入並べ替えで使用されるデータの量は比較的少なく(各グループ)、挿入比較的高い

グラフィカルアルゴリズム---ヒルソート

グラフィカルアルゴリズム---ヒルソート

彼は下付き文字が4ポイントで区切られているグループであることがわかります。つまり、下付き文字は4つのグループに異なります。たとえば、この例では、a [0]とa [4]はグループです。 [1]とa [5]はグループです...ここでは、差(距離)を増分と呼びます

グラフィカルアルゴリズム---ヒルソート

各グループの挿入ソート後、各グループが順序付けられます(全体が必ずしも順序付けられているとは限りません)

グラフィカルアルゴリズム---ヒルソート

この時点で、配列全体が半順序になります(順序の程度はそれほど高くない場合があります)

グラフィカルアルゴリズム---ヒルソート

次に、増分を前の増分の半分に減らします。2、グループ化を分割し続けます。このとき、各グループの要素数は多くなりますが、配列の一部が整然とし、挿入ソートの効率も高くなります。 。

グラフィカルアルゴリズム---ヒルソート

同様に、各グループはソート(挿入ソート)されるため、各グループは個別に順序付けられます。

グラフィカルアルゴリズム---ヒルソート

最後に、増分を前の増分の半分に設定します:1、次に配列全体がグループに分割されます。この時点で、配列全体が順序に近く、挿入ソートの効率が高くなっています。

グラフィカルアルゴリズム---ヒルソート

同様に、データのセットのみを並べ替えると、並べ替えが完了します。

4.2アニメーションデモ

[ビデオからgifへの出力画像]

4.3コードの実装

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 100;

//设置随机数组
void setRandArray(int a[], int length) {
	srand((unsigned)time(NULL));
	for (int i = 0; i < length; i++) {
		int j = rand() % 100;
		a[i] = j;
	}
}

//输出数组
void showArray(int* a, int length) {
	cout << "数组元素内容:";
	for (int i = 0; i < length; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}

void insertI(int arr[], int gap, int i){
	int inserted = arr[i];
	int j;
	//插入的时候按组进行插入(组内元素两两相隔gap)
	for(j = i - gap; j >= 0 && inserted < arr[j]; j -= gap){
		arr[j + gap] = arr[j];
	} 
	arr[j + gap] = inserted;
}

void shellSort(int arr[], int len){
	//进行分组,最开始时的增量(gap)为数组长度的一半
	for(int gap = len / 2; gap > 0; gap /= 2){
		//对各个分组进行插入排序
		for(int i = gap; i < len; i++){
			//将arr[i]插入到所在分组的正确位置上
			insertI(arr,gap,i);
		} 
	} 
}

int main()
{
	int arr[maxn], length;
	cout << "数组元素个数:";
	cin >> length;
	setRandArray(arr,length);
	showArray(arr,length);
	shellSort(arr,length);
	showArray(arr,length);
	return 0;
}

function shellSort(arr) {
    var len = arr.length;
    for (var gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        // 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
        for (var i = gap; i < len; i++) {
            var j = i;
            var current = arr[i];
            while (j - gap >= 0 && current < arr[j - gap]) {
                 arr[j] = arr[j - gap];
                 j = j - gap;
            }
            arr[j] = current;
        }
    }
    return arr;
}

5.マージソート(マージソート)

マージソートは、マージ操作に基づく効果的なソートアルゴリズムです。このアルゴリズムは、分割統治法の非常に典型的なアプリケーションです。既存の順序付けられたサブシーケンスを組み合わせて、完全に順序付けられたシーケンスを取得します。つまり、最初に各サブシーケンスを順番に作成し、次にサブシーケンスを順番に作成します。2つの順序付きリストが1つの順序付きリストにマージされる場合、それは双方向マージと呼ばれます。

マージソートの主なアイデアは分割統治法です。主なプロセスは次のとおりです。

  1. 中央からn個の要素を切り取り、2つの部分に分割します。(右側よりも左側に1つ多い場合があります)
  2. 手順1を2つに分割し、再帰分解を実行します。すべてのパーツの要素数が1になるまで。
  3. 最下層から始めて、2つのシーケンスされた番号を徐々にマージします。

問題を考えてみましょう。2つの序数のシーケンスを1つの序数のシーケンスにマージする方法は?

非常に簡単です。2つのシリーズはすでに順番になっているので、2つのシリーズの下位からPKまでの最小数を取得するだけで済みます。敗者は小さい値であり、この値を一時的なシリーズに入れてから、負けたパーティは、一方のパーティに要素がなくなり、もう一方のパーティのすべての要素の後に一時的なシーケンスが続くまで、PKの値を考え続けます。現時点では、一時的なシーケンスは2つのシーケンスの整然とした組み合わせです。マージソートでのマージは、このアイデアを使用することです。

5.1アルゴリズムの説明

  • 長さnの入力シーケンスを長さn / 2の2つのサブシーケンスに分割します。
  • これらの2つのサブシーケンスをそれぞれマージして並べ替えます。
  • 2つのソートされたサブシーケンスは、最終的なソートされたシーケンスにマージされます。

5.2映画のデモ

 ここに写真の説明を書いてください

ここに写真の説明を書いてください

ここに写真の説明を書いてください

ここに写真の説明を書いてください

 ここに写真の説明を書いてください

5.3コードの実装

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 100;

//随机数组
int arr[maxn];
int length;

//设置随机数组
void setRandArray(int a[]) {
	srand((unsigned)time(NULL));
	for (int i = 0; i < length; i++) {
		int j = rand() % 100;
		a[i] = j;
	}
}

//输出数组
void showArray(int *a) {
	cout << "数组元素内容:";
	for (int i = 0; i < length; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}

//将数组a的[L1,R1]与[L2,R2]区间合并为有序区间,此处,L2即为L1 + 1
void merge(int a[], int L1, int R1, int L2, int R2) {
	int i = L1; //i指向a[L1]
	int j = L2; //j指向a[L2]
	int temp[maxn]; //存放合并后的数组
	int index = 0; //index为temp的下标
	while (i <= R1 && j <= R2) {
		if (a[i] <= a[j]) {
			temp[index++] = a[i++];
		}
		else {
			temp[index++] = a[j++];
		}
	}
	while (i <= R1)
		temp[index++] = a[i++];
	while (j <= R2)
		temp[index++] = a[j++];
	//合并后,再赋回数组a
	for (int i = 0; i < index; i++) {
		a[L1 + i] = temp[i];
	}
}

void mergeSort(int arr[], int left, int right) {
	if (left < right) {  //只要left小于right
		int mid = (left + right) / 2;  //取left和right中点
		mergeSort(arr, left, mid);  //递归,左区间归并
		mergeSort(arr, mid + 1, right);	 //递归,右区间归并
		merge(arr, left, mid, mid + 1, right);  //左右区间合并
	}
}

int main() {
	cout << "输入数组元素个数:";
	cin >> length;
	setRandArray(arr);
	showArray(arr);
	mergeSort(arr, 0, length - 1);
	cout << "归并排序后:" << endl;
	showArray(arr);
	return 0;
}

5.4アルゴリズム分析

マージソートは安定したソート方法です。選択ソートと同様に、マージソートのパフォーマンスは入力データの影響を受けませんが、常にO(nlogn)時間計算量であるため、選択ソートよりもはるかに優れたパフォーマンスを発揮します価格は、追加のメモリスペースの必要性です。

6.クイックソート(クイックソート)

 

7.ヒープソート(ヒープソート)

 

8.ソートのカウント

 

9.バケットソート(バケットソート)

 

10.基数ソート(基数ソート)

 

 

 

 

 

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/qq_43629083/article/details/109027542
おすすめ