CSDN の皆さん、こんにちは。今日も Xiaoyalan のデータ構造とアルゴリズムのコラムです。クイック ソートの世界に入りましょう。!!
クイックソート
クイックソートは、1962 年に Hoare によって提案された 2 分木構造交換ソート手法です。その基本的な考え方は、ソートされる要素のシーケンス内の任意の要素が基準値とみなされ、ソートされる集合が基準値に従って 2 つの部分列に分割されるというものです。ソートコードに従って、左側のサブシーケンスのすべての要素が基準値より小さく、右側のサブシーケンスのすべての要素が基準値より大きく、その後、最も左のサブシーケンスがすべての要素が対応する位置に配置されるまでプロセスを繰り返します。
hoare法
一方向のアニメーションは次のとおりです。
//hoare法 int PartSort1(int* a, int left, int right) { int keyi = left; while (left < right) { //右边找小 //要防止死循环和越界的问题 while (left < right && a[right] >= a[keyi]) { --right; } //左边找大 while (left < right && a[left] <= a[keyi]) { ++left; } Swap(&a[left], &a[right]); } Swap(&a[left], &a[keyi]); return left; } void QuickSort(int* a, int begin, int end) { if (begin >= end) { return; } int keyi = PartSort1(a, begin, end); //[begin,keyi-1] keyi [keyi+1,end] QuickSort(a, begin, keyi - 1); QuickSort(a, keyi + 1, end); }
クイックソートを試してください:
void TestQuickSort()
{ int a[] = { 2,1,4,3,6,5,7,9,8,10 }; PrintArray(a, sizeof(a) / sizeof(a[0])); QuickSort(a, 0, sizeof(a) / sizeof(a[0]) - 1); PrintArray(a, sizeof(a) / sizeof(a[0])); }
ここに問題があります
左右が交わる位置が keyi よりも小さくなければならないようにするにはどうすればよいですか?
左側に keyi を作成し、右側に最初に移動して、ミーティング位置の値が keyi より小さいか、keyi の位置であることを確認します。
左と右が出会う、二つの状況に過ぎない、左が右と右が左と出会う
状況 1: 左が右と出会い、右が停止し、左が歩いています。右が先で、右の停止位置は keyi より小さい必要があり、ミート位置は右の停止位置で、keyi より小さい必要があります。
状況 2: 右が左と出会う、ラウンド中、左は動かない、右が動く、左と出会う、集合位置は左の位置、左の位置はキーイの位置、またはいくつかのラウンドの後、交換、左が一致 位置は keyi より小さい必要があります
右側で keyi を使用し、左側で先に進み、ミーティング位置の値が keyi より大きくなるようにします。
穴掘り
//挖坑法 int PartSort2(int* a, int left, int right) { int key = a[left]; int hole = left; while (left < right) { //右边找小 //要防止死循环和越界的问题 while (left < right && a[right] >= key) { --right; } a[hole] = a[right]; hole = right; //左边找大 while (left < right && a[left] <= key) { ++left; } a[hole] = a[left]; hole = left; } a[hole] = key; return hole; } void QuickSort(int* a, int begin, int end) { if (begin >= end) { return; } int keyi = PartSort2(a, begin, end); //[begin,keyi-1] keyi [keyi+1,end] QuickSort(a, begin, keyi - 1); QuickSort(a, keyi + 1, end); }
前方および後方ポインタメソッド
//前后指针法 int PartSort3(int* a, int left, int right) { int prev = left; int cur = left + 1; int keyi = left; while (cur <= right) { if (a[cur] < a[keyi]) { ++prev; Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[prev], &a[keyi]); keyi = prev; return keyi; } void QuickSort(int* a, int begin, int end) { if (begin >= end) { return; } int keyi = PartSort3(a, begin, end); //[begin,keyi-1] keyi [keyi+1,end] QuickSort(a, begin, keyi - 1); QuickSort(a, keyi + 1, end); }
クイックソートの最適化
- 3 つの数字は中間の方法でキーを選択します
- 小さな部分範囲に再帰する場合は、挿入ソートの使用を検討してください。
- クイック ソートの全体的な総合的なパフォーマンスと使用シナリオは比較的良好であるため、あえてクイック ソートと呼びます。
- 時間計算量: O(N*logN)
- 空間複雑度: O(logN)
- 安定性: 不安定
3 つの数値が取得されます。
int GetMidIndex(int* a, int left, int right) { int mid = (left + right) / 2; if (a[left] < a[mid]) { if (a[mid] < a[right]) { return mid; } else if (a[left] < a[right]) { return right; } else { return left; } } else // a[left] > a[mid] { if (a[mid] > a[right]) { return mid; } else if (a[left] > a[right]) { return right; } else { return left; } } }
全方向に最適化されたソースコード:
int GetMidIndex(int* a, int left, int right)
{ int Mid = (left + right) / 2; if (a[left] < a[mid]) { if (a[mid] < a[right]) { 中間を返します。 } else if (a[left] < a[right]) { return right; } else { 左に戻ります。 } } else // a[left] > a[mid] { if (a[mid] > a[right]) { return Mid; else if (a[左] > a[右])
{ 右に戻ります。 } else { 左に戻ります。 } } }
// ホア
// [left, right]
int PartSort1(int* a, int left, int right)
{ int midi = GetMidIndex(a, left, right); Swap(&a[left], &a[midi]);int keyi = 左;
while (left < right)
{ // 右边找小 while (left < right && a[right] >= a[keyi]) { --right; }
// 左边找大
while (left < right && a[left] <= a[keyi])
{ ++left; }Swap(&a[左], &a[右]);
}Swap(&a[keyi], &a[left]);
左に戻ります。
}
// 挖坑法
// [left, right]
int PartSort2(int* a, int left, int right)
{ int midi = GetMidIndex(a, left, right); Swap(&a[left], &a[midi]);int キー = a[左];
int 穴 = 左;
while (left < right)
{ // 右边找小 while (left < right && a[right] >= key) { --right; }
a[穴] = a[右];
穴 = 右;// 左边找大
while (left < right && a[left] <= key)
{ ++left; }a[穴] = a[左];
穴 = 左;
}a[穴] = 鍵;
リターンホール。
}// 前後指针法
// [left, right]
int PartSort3(int* a, int left, int right)
{ int midi = GetMidIndex(a, left, right); Swap(&a[left], &a[midi]);int 前 = 左;
int cur = left + 1;
int keyi = 左;
while (cur <= right)
{ if (a[cur] < a[keyi] && ++prev != cur) { Swap(&a[prev], &a[cur]); }
++cur;
}Swap(&a[prev], &a[keyi]);
keyi = 前;
keyi を返します。
}
void QuickSort(int* a, int begin, int end)
{ if (begin >= end) { return; int keyi = PartSort3(a, begin, end); //[begin,keyi-1] keyi [keyi+1,end] QuickSort(a, begin, keyi - 1); QuickSort(a, keyi + 1, end); }
クイックソート、非再帰的
void QuickSortNonR(int* a, int begin, int end) { Stack st; StackInit(&st); StackPush(&st, end); StackPush(&st, begin); while (!StackEmpty(&st)) { int left = StackTop(&st); StackPop(&st); int right = StackTop(&st); StackPop(&st); int keyi = PartSort1(a, left, right); // [left, keyi-1] keyi [keyi+1, right] if (keyi + 1 < right) { StackPush(&st, right); StackPush(&st, keyi + 1); } if (left < keyi - 1) { StackPush(&st, keyi - 1); StackPush(&st, left); } } StackDestroy(&st); }
クイックソートを非再帰的にテストします。
void TestQuickSortNonR()
{ int a[] = { 2,1,4,3,6,5,7,9,8,10 }; PrintArray(a, sizeof(a) / sizeof(a[0])); QuickSortNonR(a, 0, sizeof(a) / sizeof(a[0]) - 1); PrintArray(a, sizeof(a) / sizeof(a[0])); }
Stack.h と Stack.c の内容:
#pragma once #include<stdio.h> #include<assert.h> #include<stdlib.h> #include<stdbool.h> typedef int STDataType; typedef struct Stack { STDataType* a; int top;//栈顶 int capacity;//容量 }Stack; // 初始化栈 void StackInit(Stack* pst); // 销毁栈 void StackDestroy(Stack* pst); // 入栈 void StackPush(Stack* pst, STDataType x); // 出栈 void StackPop(Stack* pst); // 获取栈顶元素 STDataType StackTop(Stack* pst); // 获取栈中有效元素个数 int StackSize(Stack* pst); // 检测栈是否为空 bool StackEmpty(Stack* pst); #include"Stack.h" // 初始化栈 void StackInit(Stack* pst) { assert(pst); pst->a = NULL; pst->top = 0; pst->capacity = 0; } // 销毁栈 void StackDestroy(Stack* pst) { assert(pst); free(pst->a); pst->a = NULL; pst->top = pst->capacity = 0; } // 入栈 void StackPush(Stack* pst, STDataType x) { assert(pst); //扩容 if (pst->top == pst->capacity) { int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2; STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType)); if (tmp == NULL) { perror("realloc fail"); return; } pst->a = tmp; pst->capacity = newcapacity; } pst->a[pst->top] = x; pst->top++; } // 检测栈是否为空 bool StackEmpty(Stack* pst) { assert(pst); if (pst->top == 0) { return true; } else { return false; } //return pst->top==0; } // 出栈 void StackPop(Stack* pst) { assert(pst); assert(!StackEmpty(pst)); pst->top--; } // 获取栈顶元素 STDataType StackTop(Stack* pst) { assert(pst); assert(!StackEmpty(pst)); return pst->a[pst->top - 1]; } // 获取栈中有效元素个数 int StackSize(Stack* pst) { assert(pst); return pst->top; }
さて、Xiaoyalan のクイック ソートの内容はここまでです。引き続きデータ構造とアルゴリズムを学習していきます。Xiaoyalan の次回のマージ ソートの内容を楽しみにしていてください。!!