為替注文
交換とは、シーケンス内の2つのキーワードの比較結果に従って、シーケンス内のこれら2つのレコードの位置を交換することで、主にバブルソートとクイックソートがあります。
バブルソート(Bubble Sort)
バブルソートの基本的な考え方:前面から背面へ、または背面から前面へ、2つの隣接する要素を比較し、順序が逆の場合は交換します。各バブルソートは、少なくとも1つの要素を本来あるべき場所に移動し、n-1を繰り返し、n個のデータのソートが完了します。
データのグループ7、8、9、6、5、4を小さいものから大きいものに並べ替える場合、最初のバブルソートの詳細なプロセスは次のとおりです。
バブル操作の後、要素がn-1個のバブリング操作の後、n-1個の要素が本来あるべき位置に移動し、残りの要素は当然その位置にあります。
実際、バブリングプロセスは今すぐ最適化できます。バブリング操作のためのデータ交換がない場合、それは完全な順序に達したことを意味し、後続のバブリング操作を実行し続ける必要はありません。
// 冒泡排序c实现,a表示数组,n表示数组大小
/**
* Author: gamilian
*/
void bubble_sort(int a[], int n) {
if (n <= 1)
return;
for (int i = 0; i < n; ++i) {
boolean flag = false; // 提前退出冒泡循环的标志位
for (int j = 0; j < n - i - 1; ++j) {
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true; // 表示有数据交换
}
}
if (!flag)
break; // 没有数据交换,提前退出
}
}
# 冒泡排序python实现
"""
Author: gamilian
"""
def bubble_sort(a):
""" 冒泡排序
args:
a: List[int]
"""
length = len(a)
if length <= 1:
return
for i in range(length):
made_swap = False
for j in range(length - i - 1):
if a[j] > a[j + 1]:
a[j], a[j + 1] = a[j + 1], a[j]
made_swap = True
if not made_swap:
break
アルゴリズムの安定性:バブルソートでは、交換のみが2つの要素の順序を変更できます。バブル並べ替えアルゴリズムの安定性を確保するために、同じサイズの2つの隣接する要素がある場合、交換は行われません。同じサイズのデータは並べ替えの前後で順序を変更しないため、バブル並べ替えは安定した並べ替えアルゴリズムです。
スペースの複雑さ:バブリングプロセスは、隣接するデータの交換のみを含み、一定レベルの一時スペースのみを必要とするため、そのスペースの複雑度はO(1)であり、インプレースソートアルゴリズムです。
時間の複雑さ:最良の場合、並べ替えられるデータは既に順序付けられています。バブリング操作を実行するだけでデータを終了できるため、時間の複雑さはO(n)です。最悪の場合、並べ替えられるデータが逆の順序になることがあります。n-1個のバブリング操作を実行する必要があるため、最悪の場合の時間の複雑さはO(n ^ 2)です。平均時間の複雑さはより複雑であり、逆の次数で計算できます。
逆順の度合いは、順序付けられていない関係を持つ配列内の要素のペアの数です。
逆の順序の要素のペア:i <jの場合、a [i]> a [j]。
順序の次数は、順序付けられた関係を持つ配列内の要素のペアの数です。
順序付けられた要素のペア:a [i] <= a [j](i <jの場合)。
完全に配列された配列の場合、配列の次数はn *(n-1)/ 2です。このような完全に配列された配列の次数を完全な次数と呼びます。
同時に、逆次数=完全次数-次数。
バブルソートには、比較と交換という2つの操作アトムが含まれます。交換が発生するたびに、次数は1つずつ増加します。アルゴリズムがどのように改善されても、交換の数は常に決定されます。これは逆順次数、つまりn *(n-1)/ 2-初期次数です。
n個のデータを含む配列に対してバブルソートを実行しますが、最悪の場合、初期状態のn個のデータの順序が逆で、順序の次数が0であるため、n *(n-1)/ 2回の交換が行われます。最良のケースでは、初期状態データが順序付けされ、順序の次数はn *(n-1)/ 2なので、交換する必要はありません。平均すると、n *(n-1)/ 4のスワップ操作が必要であり、スワップ操作よりも比較操作の方が明らかに多く、複雑度の上限はO(n ^ 2)なので、平均時間の複雑度はO(n ^ 2)。
クイックソート
クイックソートの考え方は、分割統治の考え方に基づいています。配列の添え字を使用して、pからrまでのデータのセットをソートする場合、ピボット(パーティションポイント)としてpからrまでのデータを選択します。
pとrの間でデータをトラバースし、ピボットより小さい値を左に、ピボットより大きい値を右に、ピボットを中央に配置します。このステップの後、配列pからrのデータは3つの部分に分割され、前のpからq-1はピボットより小さく、中央はピボットであり、次のq + 1からrはピボットより大きい。
分割統治と再帰処理の考え方によれば、pからq-1までの添え字を持つデータとq + 1からrまでの添え字を持つデータの再帰的ソートを、間隔が1に減少するまで使用できます。データは整然としている。
再帰式:quick_sort(左...右)= quick_sort(左...ピボット-1)+ quick_sort(ピボット+1 ...右)
終了条件:左> =右
// 快排c实现
/**
* Author: gamilian
*/
// 对区间[left,right]划分
int Partition(int A[], int left, int right){
int pivot = A[left]; //将第一个元素设为pivot
while(left < right){ //只要left与right不相遇
while(left < right && right > pivot) right--;
//只要right比pivot大,就一直左移
A[left] = A[right]; //将比pivot小的元素移到左边
while(left < right && left <= pivot) left++;
//只要left比pivot小,就一直右移
A[right] = A[left]; //将比pivot大的元素移到右边
}
A[left] = pivot; //pivot放在最终left与right相遇的位置
return left; //返回存放pivot的下标
}
// A是数组,left与right初值为序列首尾下标
void quick_sort(int A[], int left, int right){
if(left < right){ //当前区间长度超过1
int pivot = Partition(A, left, right); //划分区间
quick_sort(A, left, pivot - 1); //对于左子区间快排
quick_sort(A, pivot + 1, right); //对右子区间快排
}
}
# 快排python实现,划分时用swap
"""
Author: gamilian
"""
import random
def quick_sort(A, left, high):
""" 快速排序
args:
A: List[int]
left: int
right: int
"""
if left < right:
# get a random position as the pivot
k = random.randint(left, high)
a[left], a[k] = a[k], a[left]
m = partition(a, left, high) # a[m] is in final position
quick_sort(a, left, m - 1)
quick_sort(a, m + 1, high)
def partition(a, left, right):
""" 划分区间
args:
A: List[int]
left: int
right: int
"""
pivot, j = a[left], left
for i in range(left + 1, right + 1):
if a[i] <= pivot:
j += 1
a[j], a[i] = a[i], a[j] # swap
a[left], a[j] = a[j], a[left]
return j
アルゴリズムの安定性:分割プロセスにはスワップ操作が含まれるため、配列に2つの同一の要素がある場合、最初の分割操作の後に、2つの同一の要素の相対的な順序が変わります。したがって、クイックソートは安定したソートアルゴリズムではありません。
スペースの複雑さ:再帰的ワークスタックがカウントされる場合、高速キューは再帰的であるため、再帰的コールの各層の必要な情報を保存するために再帰的ワークスタックが必要であり、その容量は再帰的コールの最大深度と一致します。高速キューの最良の場合のスペースの複雑度はO(nlogn)であり、最悪の場合はn-1回の再帰呼び出しが必要です。つまり、高速キューの最悪の場合のスペースの複雑度はO(n)であり、高速キューの平均スペース複雑度はO(nlogn)。
再帰的なワークスタックがカウントされない場合、高速ソートスペースの複雑度はO(1)です。これは、インプレースソートアルゴリズムです。
時間の複雑さ:各パーティション操作で配列をほぼ同じサイズの2つのセルに分割できる場合、時間の複雑さを再帰的に解決するための式はマージと同じです。したがって、高速ソートの最良のケースはO(nlogn)です。
T(1)= C; n = 1の場合、一定レベルの実行時間が必要なだけなので、Cとして表されます。T(n)= 2 * T(n / 2)+ n; n> 1
毎回最後の要素をピボットとして選択すると、各パーティションによって取得される2つの間隔は等しくありません。クイックキューのプロセス全体を完了するには、約n個のパーティション操作を実行する必要があります。パーティションごとに平均で約n / 2の要素をスキャンする必要があるため、高速ソートの最悪の場合の複雑さはO(n ^ 2)です。
各パーティション操作で、間隔をサイズが9:1の2つのセルに分割するとします。
T(1)= C; n = 1の場合、一定レベルの実行時間が必要なだけなので、Cとして表されます。
T(n)= T(n / 10)+ T(9 * n / 10)+ n; n> 1
したがって、高速キューの平均時間の複雑さはO(nlogn)です。