9つのソートアルゴリズムの比較

目次

この記事のコードはすべてオリジナルです。間違いがあれば修正してください。

目次

  • 序文
  • 1.挿入ソート
    • 1. 直接挿入
    • 2.半分に差し込みます
    • 3. ヒルソート
  • 2.選択ソート
    • 1.直接選択
    • 2.ツリーの選択
  • 3. 交換ソート
    • 1. バブルソート
    • 2. クイックソート
  • 4. その他の仕分け
    • 1. マージソート
    • 2. 基数ソート
  • 要約する


序文

        私は現在2年生でアルゴリズムの基礎が弱いので、アルゴリズムの能力を強化し、ACMなどの競技会に向けてしっかりとした基礎を築くためにこのブログを書いています。このブログには間違いがたくさんあるかもしれませんが、修正していただければ幸いです。同じ考えを持つ友達がいれば、一緒にアルゴリズムを学び、一緒に進歩することができます。


提示:以下代码仅供参考!!!

1. 挿入ソート

        挿入ソートは、名前が示すように、ソートする配列の途中に要素を挿入し、最終的に配列をソートする効果を実現します。

1. 直接挿入

        直接挿入は最も基本的なソート方法であり、その原理は、最初の k 個の配列がソートされた状態であると考え、配列の k+1 番目の要素とその前の k 個の要素を比較し、挿入する位置を見つけ、後続の要素を 1 つずつ後方に移動します。時間計算量は O(n^2) です。(比較の便宜上、配列のサイズは 100000 (10^5) で、直接挿入アルゴリズムと比較されます)。

具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <time.h>
#include <algorithm>

using namespace std;


void Print(int* a, int n);
void direct_sort(int* a, int n);

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

int main()
{
	srand((unsigned)time(NULL));
	int n;
	cout << "请输入待排序的数组大小:";
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	direct_sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	sort(num1, num1 + n);
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

        ソートされた配列のサイズは 100000 で、配列 (10^5) が共有される場合は 5727 ミリ秒になります。検証結果は正しいです。

 2.半分に差し込みます

       半挿入は、直接挿入アルゴリズムに基づいたアップグレードです。実際、比較のために前の k 個の要素を走査する必要はありません。サイズが k である前の配列の中央値と比較するだけで済みます。この数値が中央値より大きい場合は、間隔を半分にして比較を続行し、それ以外の場合は半分で比較します。最終的には挿入位置を見つける必要があり、この検索の時間計算量は O(logn) に削減されます。位置を見つけたら、挿入を続けます。したがって、このアルゴリズムの時間計算量は O(nlogn) です。

       具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
#include <algorithm>

using namespace std;

void Binary_Sort(int* a, int n);
void Print(int* a, int n);
int find_insert_point(int* a, int n, int l, int r, int i);
void direct_sort(int* a, int n);

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

int find_insert_point(int* a, int n, int l, int r, int i)
{
	if (l > r)return l;
	int m = (l + r) / 2;
	if (a[i] < a[m])return find_insert_point(a, n, l, m - 1, i);
	else return find_insert_point(a, n, m + 1, r, i);
}

void Binary_Sort(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
		int p = find_insert_point(a, n, 0, i - 1, i);
		int temp = a[i];
		for (int j = i; j > p; j--)
		{
			a[j] = a[j - 1];
		}
		a[p] = temp;
	}
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

int main()
{
	srand((unsigned)time(NULL));
	cout << "请输入待排序的数组大小:";
	int n;
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Binary_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

        このアルゴリズムでは、配列サイズ 100000 の配列を並べ替えるのに 2845 ミリ秒かかります。検証結果は正しいです。 

           時間に問題があり、コードのどこに問題があるのか​​がわかりません。上司に指摘してもらいたいと思っています。

3. ヒルソート

        ヒル ソートの原理は、配列内のギャップ要素をグループに分割し、そのグループをソートし、ギャップを徐々に減らしていくことです。ギャップが 1 のとき、配列はすでにソートされています。ここでは、gap/=2 としてギャップを徐々に減らすことを選択し、このアルゴリズムの時間計算量が O(nlogn^2) または O(n^1.3) になるようにします。

具体的なコード:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>

using namespace std;

void Shell_Sort(int* a, int n);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int n);

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Shell_Sort(int* a, int n)
{
	//Print(a, n,n);
	for (int gap = n/2; gap >= 1; gap >>= 1)
	{
		for (int i = gap; i < n; i++)
		{
			int temp = a[i];
			for (int j = i - gap; j >= 0; j -= gap)
			{
				if (temp < a[j])Swap(a[j + gap], a[j]);
				else break;
			}
		}
	}
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ",a[i]);
		//if ((i+1) % gap == 0)cout << endl;
	}
	cout << endl;
}
 
int main()
{
	srand((unsigned)time(NULL));
	int n;
	cout << "请输入待排序的数组的大小:";
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Shell_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

         このアルゴリズムは、サイズ 100000 の配列をソートするのにわずか 28 ミリ秒しかかかりません。検証結果は正しいです。 

 

2. 選択ソート

        選択ソートでは、配列をソートできるように、配列内の最大または最小の要素を毎回選択します。

1. 直接選択

        直接ソートは最も直接的なソート方法であり、n 回ループし、毎回配列内で k 番目に小さいデータを選択し、それを配列の k 番目の位置に置きます。時間計算量は O(n^2) です。

具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>

using namespace std;

void Choose_Sort(int* a, int n);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int n);

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
		//if ((i+1) % gap == 0)cout << endl;
	}
	cout << endl;
}

void Choose_Sort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		int m = a[i];
		int dex = i;
		for (int j = i + 1; j < n; j++)
		{
			if (a[j] < m)
			{
				m = a[j];
				dex = j;
			}
		}
		Swap(a[i], a[dex]);
	}
}

int main()
{
	srand((unsigned)time(NULL));
	cout << "请输入待排序的数组的大小:";
	int n;
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Choose_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

        このアルゴリズムでは、配列サイズ 100000 の配列を並べ替えるのに 4945 ミリ秒かかります。検証結果は正しいです。

2. ツリーの選択

        樹木選別は、時間と引き換えに空間を利用するという考え方です。配列を補って 2 の k 乗の配列にし、余分な部分は無限大で表します。このようにして、完全な二分木を形成し、ヘッド ノードのデータがこの配列内の最小データとなるように、比較ごとに 2 つのうちの最小の番号をノードに割り当てることができます。次に、このデータを元の配列に割り当て、次の反復のために数値を無限大に設定します。このアルゴリズムの時間計算量は O(nlogn) です。

具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>
#define M 0x7fffffff

using namespace std;

void Tree_Sort(int* a, int n);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int n);


void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	cout << endl;
}

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Tree_Sort(int* a, int n)
{
	int n1 = n,t = 0;
	while (n1)
	{
		n1 >>= 1;
		t++;
	}
	int n2 = 1;
	n2 <<= t;
	int n_max = 2 * n2 - 1;
	int* num = new int[n_max];
	for (int i = 0; i < n2; i++)
	{
		if (i < n)num[i] = a[i];
		else num[i] = M;
	}
	for (int i = n2; i < n_max; i++)
	{
		num[i] = min(num[(i - n2) * 2], num[(i - n2) * 2 + 1]);
	}
	int h = n2;
	int pre1 = 0;
	for (int i = 0; i < 2 * n2 - 1; i++)
	{
		cout << num[i] << " ";
		if (i+1 == pre1 + h)
		{
			pre1 += h;
			cout << endl;
			h >>= 1;		
		}
	}
	cout << endl;
	a[0] = num[n_max-1];
	for (int i = 0; i < n - 1; i++)
	{
		int temp = num[n_max - 1];
		int t = n_max - 1;
		while (t >= n2)
		{
			if (num[(t - n2) * 2] == temp)t = (t - n2) * 2;
			else t = (t - n2) * 2 + 1;
		}
		num[t] = M;
		while (t < n_max - 1)
		{
			num[n2 + t / 2] = min(num[t], num[t ^ 1]);
			t = n2 + t / 2;
		}
		a[i + 1] = num[n_max - 1];
	}
	delete[]num;
}

int main()
{
	srand((unsigned)time(NULL));
	cout << "请输入待排序的数组大小:";
	int n;
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Tree_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

 

        このアルゴリズムは、配列サイズ 100,000 の配列をソートするのにわずか 29 ミリ秒しかかかりませんが、実装するには多くのスペースが必要です。検証後の結果は正しいです。

3. 交換ソート

        交換ソートは、大きな数値と小さな数値を連続的に交換して配列をソートするアルゴリズムです。

1. バブルソート

        バブルソートとは、大きな数値と小さな数値を継続的に交換し、大きな数値を後ろに置き、最大の数値を配列の最後に置くことです。あるトラバーサル中に何もやり取りがなければ、配列がソートされたことを意味し、この時点でループを中断して抜け出すことができます。このアルゴリズムの時間計算量は O(n^2) です。

具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>

using namespace std;

void Bubble_Sort(int* a, int n);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int n);

void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	cout << endl;
}

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Bubble_Sort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		bool over = true;
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(a[j], a[j + 1]);
				over = false;
			}
		}
		if (over)break;
	}
}

int main()
{
	srand((unsigned)time(NULL));
	int n;
	cout << "请输入待排序的数组的大小:";
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Bubble_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

         このアルゴリズムでは、配列サイズ 100,000 の配列を並べ替えるのに 25806 ミリ秒かかり、時間のオーバーヘッドは比較的大きくなります。検証後の結果は正しいです。

2. クイックソート

         クイック ソートでは、配列内の中間の数値を比較値として選択し、この値より大きい要素を右側に、この要素より小さい要素を左側に配置します。このように、左右の配列に対して同じ操作を行うと、両側の境界値が1だけ異なった時点で配列はソート済みになります。

        ここでは、比較する 3 つの数値を選択し、中間の値を選択します。これにより、中間の値が配列の中央値にできるだけ近くなり、再帰配列の時間の複雑さが軽減されます。配列が十分に小さい場合は、再帰呼び出しの必要がなく、並べ替えに直接挿入アルゴリズムを選択できるため、時間の複雑さも軽減できます。最終的な時間計算量は O(nlogn) です。

具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>

using namespace std;

void Quick_Sort(int* a, int n);
void Quick_Divide(int* a, int left, int right);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int left,int right);

void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	cout << endl;
}

void direct_sort(int* a, int left,int right)
{
	if (left > right)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = left + 1; i <= right; i++)
	{
		int k = left;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Quick_Divide(int* a, int left, int right)
{
	if (right <= left)return;
	if (right - left < 10)
	{
		direct_sort(a, left, right);
		return;
	}
	int num[3] = { a[left],a[right],a[(left + right) / 2] };
	sort(num, num+3);
	int dex;
	if (num[1] == a[left])dex = left;
	else if (num[1] == a[right])dex = right;
	else dex = (left + right) / 2;
	Swap(a[dex], a[left]);
	int i = left, j = right;
	int temp = a[left];
	while (i < j)
	{
		while (i < j && a[i] < temp)i++;
		while (i < j && a[j] >= temp)j--;
		Swap(a[i], a[j]);
	}
	if (a[i] > temp)
	{
		Quick_Divide(a, left, i - 1);
		Quick_Divide(a, i, right);
	}
	else
	{
		Quick_Divide(a, left, i);
		Quick_Divide(a, i + 1, right);
	}
}

void Quick_Sort(int* a, int n)
{
	Quick_Divide(a, 0, n - 1);
}

int main()
{
	srand((unsigned)time(NULL));
	int n;
	cout << "请输入待排序的数组的大小:";
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Quick_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, 0, n - 1);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

         このアルゴリズムは、サイズ 100000 の配列をソートするのにわずか 14 ミリ秒しかかかりません。検証後の結果は正しいです。

 

4. その他の仕分け

1. マージソート

        マージ ソートでは、配列を徐々に小さな配列に分割し、その後徐々に配列をマージして、時間計算量を O(nlogn) に減らすことができます。

        注: ここでは、元の配列のサイズを 2 の k 乗に拡張する必要があります。

        具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>

using namespace std;

#define M 0x7fffffff

void Merge_Sort(int* a, int n);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int n);
void Merge(int* a, int n, int hn);

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Merge_Sort(int* a, int n)
{
	//Print(a, n);
	int t = 0;
	int n1 = n;
	while (n1)
	{
		n1 >>= 1;
		t++;
	}
	int n2 = 1;
	for (int i = 0; i < t; i++)
	{
		n2 <<= 1;
	}
	int* num = new int[n2];
	for (int i = 0; i < n; i++)
	{
		num[i] = a[i];
	}
	for (int i = n; i < n2; i++)
	{
		num[i] = M;
	}
	for (int i = 0; i < n2 / 2; i++)
	{
		if (num[2 * i] > num[2 * i + 1])Swap(num[2 * i], num[2 * i + 1]);
	}
	int hn = 2;
	while (hn <= n2 / 2)
	{
		Merge(num, n2, hn);
		hn <<= 1;
		//Print(num, n);
	}
	//Print(num, n2);
	for (int i = 0; i < n; i++)
	{
		a[i] = num[i];
		
	}
	//Print(a, n);
}
void Merge(int* num, int n, int hn)
{
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num1[i] = num[i];
	}
	int t = 2 * hn;
	for (int i = 0; i < n / t; i++)
	{
		int start = i * t, end = start + t - 1, k = start, p1 = start, p2 = start + hn;
		while (p1 < start + hn && p2<=end)
		{
			if (num1[p1] <= num[p2])
			{				
				num[k++] = num1[p1++];
				//if (k == 1)cout << num[0] << endl;
			}
			else
			{
				num[k++] = num1[p2++];
				//if (k == 1)cout << num[0] << endl;
			}
		}
		while (p1 < start + hn)num[k++] = num1[p1++];
		while (p2 <= end)num[k++] = num1[p2++];
	}
	delete[]num1;
}
void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ",a[i]);
		//if ((i+1) % gap == 0)cout << endl;
	}
	cout << endl;
}

int main()
{
	srand((unsigned)time(NULL));
	cout << "请输入待排序的数组的大小:";
	int n;
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Merge_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1, n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

        このアルゴリズムは、サイズ 100000 の配列をソートするのにわずか 15 ミリ秒しかかかりません。検証後の結果は正しいです。

2. 基数ソート

        基数ソートは、特定の基数系に基づいて配列を複数の配列に分割し (複数の基数が使用されます)、剰余が同じ数値を 1 つの配列に入れ、その数値を 1 つ右にシフトします。ここでは、最大数の最高次数を見つける必要があります。そうすれば、何回反復する必要があるかを知ることができます。このように並べ替えた後、順序に従って段階的に並べ替えるので、最終的な結果も正しいですが、データを保存するために多くのスペースが必要になります。時間計算量は O(nlogn) です。

具体的なコードは次のとおりです。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>

using namespace std;

void Base_Sort(int* a, int n);
void Print(int* a, int n);
void Swap(int& a, int& b);
void direct_sort(int* a, int n);
void Base_Sort_pre(int* a, int n, int loop);

void direct_sort(int* a, int n)
{
	if (n <= 1)
	{
		cout << "Array Error!" << endl;
	}
	for (int i = 1; i < n; i++)
	{
		int k = 0;
		while (a[i] > a[k])k++;
		int temp = a[i];
		for (int j = i; j > k; j--)
		{
			a[j] = a[j - 1];
		}
		a[k] = temp;
	}
}

void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

void Print(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
		//if ((i+1) % gap == 0)cout << endl;
	}
	cout << endl;
}


void Base_Sort_pre(int* a, int n,int loop)
{
	//cout << t << endl;	
	int d[10] = { 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 };
	int* bucket[10] = {  };
	int c[10] = { 0 };
	for (int j = 0; j < n; j++)
	{
		c[(a[j] / d[loop]) % 10]++;
	}
	for (int k = 0; k < 10; k++)
	{
		bucket[k] = (int*)malloc(c[k] * sizeof(int));
	}
	for (int m = 0; m < 10; m++)c[m] = 0;
	for (int l = 0; l < n; l++)
	{
		int row = (a[l] / d[loop]) % 10;
		bucket[row][c[row]] = a[l];
		c[row]++;
	}
	int s = 0;
	for (int x = 0; x < 10; x++)
	{
		for (int y = 0; y < c[x]; y++)
		{
			a[s] = bucket[x][y];
			s++;
		}
	}
	for (int p = 0; p < 10; p++)
	{
		free(bucket[p]);
	}
}

void Base_Sort(int* a, int n)
{
	int ma = 0;
	for (int i = 0; i < n; i++)
	{
		if (a[i] > ma)ma = a[i];
	}
	int t = 0;
	int ai = ma;
	//cout << ai << endl;
	while (ai)
	{
		ai /= 10;
		t++;
	}
	for (int i = 0; i < t; i++)
	{
		Base_Sort_pre(a, n, i);
	}
}
int main()
{
	srand((unsigned)time(NULL));
	cout << "请输入待排序的数组的大小:";
	int n;
	cin >> n;
	int* num = new int[n];
	int* num1 = new int[n];
	for (int i = 0; i < n; i++)
	{
		num[i] = rand();
		num1[i] = num[i];
	}
	clock_t start = clock();
	Base_Sort(num, n);
	clock_t end = clock();
	cout << "Sort " << n << " datas use " << (double)(end - start) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	//Print(num, n);
	clock_t start1 = clock();
	direct_sort(num1,n);
	clock_t end1 = clock();
	cout << "Sort " << n << " datas use " << (double)(end1 - start1) / CLOCKS_PER_SEC * 1000 << " ms." << endl;
	bool equal = true;
	for (int i = 0; i < n; i++)
	{
		if (num[i] != num1[i])
		{
			equal = false;
			break;
		}
	}
	//printf("%d\n", equal);
	if (equal)cout << "Right!" << endl;
	else cout << "Error!" << endl;
	delete[]num;
	delete[]num1;
	return 0;
}

        このアルゴリズムは、サイズ 100000 の配列をソートするのにわずか 13 ミリ秒しかかかりません。検証結果は正しいです。

 


要約する

        これら 9 つのソート アルゴリズムの時間速度は、基数ソート > クイック ソート > マージ ソート > ヒル ソート > ツリー ソート > ハーフ挿入 > 直接選択 > 直接挿入 > バブル ソートです。

         その中で、スペースを大量に消費するものは、基数ソートとツリーソートです。

        各ソート アルゴリズムには独自の用途があり、ソート アルゴリズムの品質は時間の速さだけで判断することはできません。

        上記のコードは私が独自に作成したものであり、多くの間違いや理解できない点があるかもしれません。修正していただければ幸いです。

おすすめ

転載: blog.csdn.net/m0_74133913/article/details/129760431