1.はじめに
私は本のの6.7.9セクション読みSTL源码解析の実装についてのsort()
C ++の機能を、とも一読この記事のセクションを理解する上で私に多くのことができます。
私はの実装と結論sort()
STL関数があることを教えてくれる
- 独立して仕事を終えることができるアルゴリズムをすることができます混合し、実装のパフォーマンスを向上させることができます。
- 私たちは教科書から学んだ多くの古典的なアルゴリズムが可能な改善され、より速く、より効率的に。
参考文献:
(1)「STLソース解析」
(2)無限のソース解析の既知のstd ::ソート
2.準備
実装に焦点を当て前に、次の内容を理解しておいてください。あなたがそれらを理解する場合は、このセクションをスキップすることができます。
2.1。インライン関数
インライン関数は、それが呼び出されたときに、関数を呼び出す文が暗黙的に関数の実装(コード)に置き換えられますことを関数です。具体的には、
#include <iostream>
using namespace std;
inline int call()
{
static int ctr = 1;
return ctr++;
}
int main()
{
for (int i = 0; i < 500; i++)
{
cout << call() << " " << endl;
}
}
上記のコードで、call()
で
cout << call() << " " << endl;
暗黙のうちにあるコードに置き換えられますcall()
機能。
2.2。テンプレート
テンプレートの技術は、各パラメータの値の単一種類以上を受信する機能や他のものを可能にします。2つの数値の大きいものを計算する次のコードを検討してください。
def max(a, b):
if a > b:
return a
return b
タスクはpythonで簡単に行うことができます。しかし、C ++のために、そうではありません。あなたは、値の異なるタイプを受信する多くのオーバーロード関数を定義する必要があります。別の状況を考えてみましょう:あなたは、スタックをカスタマイズしたい場合、あなたは複雑で不便である、異なるデータ型のスタックであることを多くのクラスを定義する必要があります。しかし、テンプレートを使用して、我々は簡単に多くの類似したコードを書く繰り返す必要なしに上記のタスクを完了することができます。例を見てみましょう
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
単一の関数があることに注意してくださいMax()
複数のデータ型の値を受け取ります。上記のコードが実行されると、結果は次のようになります
Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World
声明ことに注意してください
template <typename T>
ことを意味しT
、任意のデータタイプにすることができます。変数の型がある場合はint
、T
になりint
、変数の型がある場合はstring
、T
になりますstring
。
template <class T>
同様の方法で動作します。
参考文献:
(1)C ++テンプレート
(2) <転載>テンプレート宣言テンプレート
2.3。RandomAccessIterator
STLでのイテレータはポインタのように動作するオブジェクトです。イテレータは、過負荷*
や->
動作が。そしてRandomAccessIteratorのonjectは、配列を指し、ポインタのようなものです。場合はp
、p1
、p2
RandomAccessIteratorのオブジェクトをしているとn
されるint
ような操作、値p[n]
、p + n
、p += n
、p1 - p2
、p1 <= p2
などが有効です。
3.ソート()STLで探検
sort()
STLの用途での機能
- クイックソート
- 挿入ソート
- ヒープソート
一緒にソートのパフォーマンスを向上させます。クリックしてここにコード全体を見るために。
3.1。ソートするはじめに()
クイックソートは、平均時間複雑で、最速のソートアルゴリズムです\(O(NlogN)\) 。しかし、リスト内の要素をソートする際、ほぼ順に、クイックソートアルゴリズムの時間複雑さがに変わりますされている\(O(N ^ 2)\) 。再帰の回数が増加する場合と、クイックソートアルゴリズムが原因関数呼び出しの過剰使用にあまり効率的になります。幸いなことに、挿入ソートは、に近い時間複雑性を有する(Oは、(N)\)\要素はほとんど順序になっているソートされている、とヒープソート、再帰を必要としない、最悪時の複雑持っている\(Oを( NlogN)\) 。
ここでの実装のsort()
機能が。
template <class RandomAccessIterator>
inline void sort(RandomAccessIterator first, RandomAccessIterator last) {
// when the length of the list to be sorted is greater than 0
if (first != last) {
// combination of the quick sort and the heap sort
__introsort_loop(first, last, value_type(first), __lg(last - first) * 2);
// the insertion sort
__final_insertion_sort(first, last);
}
}
ソートするリストの長さが0より大きい場合、__introsort_loop()
この関数はクイックソートとヒープソートの組み合わせである、実装されます。クイックソートやヒープソートは高速であるため、データ量が多い場合には、ソートのための時間を短縮することができます。ソートされたリストは、順番に、ほとんどの場合は、クイックソートアルゴリズムの使用が増加することをソート時間は、これ__final_insertion_sort()
関数は、リストの順番で、ほとんどの場合に、アルゴリズムは、パフォーマンスが向上、挿入ソートである、実装されます。
ことに注意してください__lg()
関数が最大再帰の深さを計算するために使用されます。再帰の深さがの結果より深くなると__lg()
機能、挿入ソートは__final_insertion_sort()
クイックソートアルゴリズムが原因の余分な時間と深い再帰によってもたらされるスペースのコストに遅くなるという潜在的なリスクを避けるために、代わりに使用され、参照以下。実装__lg()
機能があります
template <class Size>
inline Size __lg(Size n) {
Size k;
for (k = 0; n > 1; n >>= 1) ++k;
return k
}
単ににおける最大K発見\(2 ^ k個の\当量のn \)を。したがって\(K = log_2nの\) 。sort()
関数\(K = log_2(lengthOfTheListToBeSorted)\)
3.2。クイックソート
ここではクイックソートの本体です
template <class RandomAccessIterator, class T, class Size>
void __introsort_loop(RandomAccessIterator first,
RandomAccessIterator last, T*,
Size depth_limit) {
while (last - first > __stl_threshold) { // __stl_threshold: const int 16
// when the condition is true,
// the recursion depth is deep
if (depth_limit == 0) {
// use the heap sort
partial_sort(first, last, last);
return;
}
--depth_limit;
// the quick sort
// find the pivot
RandomAccessIterator cut = __unguarded_partition
(first, last, T(__median(*first, *(first + (last - first)/2),
*(last - 1))));
// recursively sort the part on the right of the pivot
__introsort_loop(cut, last, value_type(first), depth_limit);
// in the next loop the left part will be sorted
last = cut;
}
}
再帰の深さが深くない場合はソートが速くなるように、クイックソートが使用されています。クイックソートアルゴリズムのアルゴリズムは、私たちの教科書のものと非常によく似ています:
- ピボットを見つけます
- 再帰的にピボットの右側のリストを並べ替えます
- ピボットの左側のリストを並べ替えます
3.2.1。一つの再帰の代わりに二つの
通常の溶液とは異なる第3工程を、注意してください。ここでのアルゴリズムは、ピボットの左側のリストをソートするために再帰を使用しますが、次のループでソートを終了していないオリジナルのクイックソートアルゴリズムを改良したものであり、。この方法では、より高速なソートを行います。コードに注意last = cut
ループ本体の最後の行にします。この文の後、last
の引数__unguarded_partition()
次のループ内の関数は次のようになりますcut
ピボットです。
3.2.2。中央・オブ・スリー
この関数は__median()
、最初の最後のピボットなるように中間要素に中央値を選択します。その後、約クイックソートターンの時間複雑になりますこれは、ソートされたリストは順番にかなりあるというリスクを取り除くことができます(\ O(N ^ 2))\を。これは、アルゴリズムの別の改良です。そして、ここでその実装であります
template <class T>
inline const T& __median(const T& a, const &T b, const T& c) {
if (a < b)
if (b < c) // a < b < c
return b;
else if (a < c) // a < b, b >= c, a < c
return c;
else
return a;
else if (a < c) // c > a >= b
return a;
else if (b < c) // a >= b, a >= c, b < c
return c;
else
return b;
}
3.2.3。より高速なパーティション
そして、__unguarded_partition
ピボットを見つけました。それは私たちの身近なものに似ているが少し異なっています。ここでは1より向上します。コードを見てみましょう
template <class RandomAccessIterator, class T>
RandomAccessIterator __unguarded_partition(RandomAccessIterator first,
RandomAccessIterator last,
T pivot) {
while (true) {
// the first pointer iterates over the list until the current element
// it points at is smaller than the pivot
while (*first < pivot) ++first;
// shift left by one
--last;
// the last pointer iterates over the list until the current element
// it points at is greater than the pivot
while (pivot < *last) --last;
// if the first pointer is after the last pointer
// return the first pointer
if (!(first < last)) return first;
// else swap the two elements the two pointer are pointing at
iter_swap(first, last);
// shift right by one
++first;
}
}
境界チェックがあることに注意してくださいfirst < last
(中央値の-3の戦略から持ってきたbenifitである、ここでは必要ありません__median()
上記で紹介)。このように、境界チェックのための時間は、データ量が膨大な場合は特に、ソートはるかに高速になりますされ、保存することができます。
境界チェックがここで必要とされていない理由を見つけるのをしてみましょう。まず、中央値の-3の戦略なしでソートを実装します。簡単な条件を考えてみましょう。ソートするリストは逆の順序である場合には
3 2 1
ピボットは、最後の要素であるとしましょう、1ここでは、および実装する__unguarded_partition()
機能を。
// F: first
// L: last
// P: the pivot
// original state
3 2 1
F L
P
/* the code below is not implemented in the current loop
while (*first < pivot) ++first;
*/
// --last
3 2 1
F L
P
// while (pivot < *last) --last;
? 3 2 1
F
L // notice that the last pointer here is beyond the boundary!
P
私たちは、後のことを見ることができますwhile (pivot < *last) --last;
実行され、last
境界を越えています。このように、境界チェックは中央値の-3の戦略なしで必要とされています。
我々は中央値の-3の戦略を使用するときに何が起こるか見てみましょう。配列において1 2 3
中央値2、ピボットあるように選択されます。
// the original state
3 2 1
F P L
/* the code below is not implemented in the current loop
while (*first < pivot) ++first;
*/
// --last;
3 2 1
F
L
P
/* the code below is not implemented in the current loop
while (pivot < *last) --last;
if (!(first < last)) return first;
*/
// iter_swap(first, last);
1 2 3
F
L
P
// ++first
1 2 3
F
L
P
// notice that the sequence is in order now!
// the second loop
/* the code below is not implemented in the current loop
while (*first < pivot) ++first;
*/
// --last;
1 2 3
F
L // notice thst L is before F now!
P
/* the code below is not implemented in the current loop
while (pivot < *last) --last;
*/
// if (!(first < last)) return first;
// then first (points at 2 now) is returned
上記のコードから、我々は、彼らが境界を越えている前に、2つのポインタの動きが止まるだろうことがわかります。
3.3。ヒープソート
条件がときにif (depth_limit == 0)
真である、ヒープの並べ替えが代わりに実装されます。
template <class RandomAccessIterator, class T, class Compare>
void __partial_sort(RandomAccessIterator first, RandomAccessIterator middle,
RandomAccessIterator last, T*, Compare comp) {
make_heap(first, middle, comp);
for (RandomAccessIterator i = middle; i < last; ++i)
if (comp(*i, *first))
__pop_heap(first, middle, i, T(*i), comp, distance_type(first));
sort_heap(first, middle, comp);
}
template <class RandomAccessIterator, class Compare>
inline void partial_sort(RandomAccessIterator first,
RandomAccessIterator middle,
RandomAccessIterator last, Compare comp) {
__partial_sort(first, middle, last, value_type(first), comp);
}
理由sort()
STL関数を直接ヒープソートアルゴリズムを使用していないが、ヒープ・ソートの時間複雑でもあるが、ということです\(O(NlogN)\) 、それは実際のコストソート時間はそれよりも2〜5倍長いですクイックソートアルゴリズムの。
3.4。挿入ソート
conditonがするとwhile (last - first > __stl_threshold)
偽である場合には、__stl_threshold
あるconst int 16
ここでは、ソートするリストが順番にほとんどです。さて、クイックソートアルゴリズムを引き続き使用することは、順番に(ほとんど)されているリストのための悪い時間の複雑さにあまり効率的でしょう。したがって、__introsort_loop()
関数が終了しますと__final_insertion_sort()
、実際にいくつかの改善と挿入アルゴリズムである関数は、リストをソートするために実装されます。ここで挿入アルゴリズムの実装があります。
template <class RandomAccessIterator>
void __final_insertion_sort(RandomAccessIterator first,
RandomAccessIterator last) {
// when there are more than 16 elements in the list
if (last - first > __stl_threshold) {
// take the first 16 elements to be sorted using the
// __insertion_sort() function
__insertion_sort(first, first + __stl_threshold);
// use __unguarded_insertion_sort() to sort the remaining elements
__unguarded_insertion_sort(first + __stl_threshold, last);
}
else
// sort the elements with the __insertion_sort function
__insertion_sort(first, last);
}
__final_insertion_sort()
機能は、if-else文が含まれています。2つの同様の機能があることに注意してください:__insertion_sort()
と__unguarded_insertion_sort
。なぜそのように書かれたコードは、後述しますさ。の実装を初めて目してみましょう__insertion_sort()
。
template <class RandomAccessIterator, class T>
void __unguarded_linear_insert(RandomAccessIterator last, T value) {
RandomAccessIterator next = last;
--next;
// find a position for the value to insert to
// and insert the value into that position
while (value < *next) {
*last = *next;
last = next;
--next;
}
*last = value;
}
template <class RandomAccessIterator, class T>
inline void __linear_insert(RandomAccessIterator first,
RandomAccessIterator last, T*) {
T value = *last;
// if the value is smaller than the first element in the sorted sublist
if (value < *first) {
// all elements in the sorted sublist are shifted right by one
copy_backward(first, last, last + 1);
// the (smallest) value is then in the first position
*first = value;
}
else
// implement the insertion sort without the need to check boundary
__unguarded_linear_insert(last, value);
}
template <class RandomAccessIterator>
void __insertion_sort(RandomAccessIterator first, RandomAccessIterator last) {
// if the list is empty, do nothing
if (first == last) return;
// implement the insertion sort
for (RandomAccessIterator i = first + 1; i != last; ++i)
__linear_insert(first, i, value_type(first));
}
ソートするリストが空でない場合、リストには挿入ソートアルゴリズムを使用してソートされます。しかし、ここでの改善があります。
3.4.1。値が最小でありますか?
注意してください__linear_insert()
機能を。挿入される値は、最初にソートされたサブリストの最小の要素、つまり、最初の要素と比較されます。値がソートサブリストの最初の要素より小さい場合、値は、元のソートサブリスト内のすべての要素と、第一の位置に挿入される一方によって右にシフト。具体的には、我々はソートされたサブリストを持っていると仮定しましょう
1 6 8
挿入される値は0 0最初の(最小)ソートされたサブリスト内の要素よりも小さくなっています。従ってソートサブリストの全ての要素は右に1つシフトされ、値0は、最初の位置に挿入されます。
`cpp 1 6 8 ↓ 1 6 8 ↓ 0 1 6 8
値がソートされたサブリストの最初の要素(最小1)よりも小さくない場合や、__unguarded_linear_insert()
関数が私たちの身近な挿入アルゴリズムの挿入プロセスと同様に、実装されますが、必要としない境界を確認すること。言い換えれば、値がソートされたサブリストの最初の有効な位置の前に挿入されるかどうかをチェックする必要はありません。次のようにチェックが必要とされていない理由はあります。conditonので、value < *first
else文で偽である(したがって、コード__unguarded_linear_insert(last, value);
、実行される。)、今挿入される値は、ソートされたサブリストで最小ではないであろう。換言すれば、値はソートサブリストに(また、最小のものである)最初の要素よりも小さくないであろう。こうして値が挿入される位置は、最初の(最も小さい)要素の後でなければなりません。具体的には、我々はソートされたサブリストを持っていると仮定しましょう
1 6 8
そして、私たちは今、挿入している値は2以来です\(1 <2 \) 、最初の条件では__linear_insert()
実装されません。代わりに、else文のコードは、__unguarded_linear_insert(last, value);
実行されます。ソートされたリスト内の位置は、値2を挿入するために発見されます。
1 6 8 (insert 2 here? no)
1 6 (insert 2 here? no) 8
1 (insert 2 here? yes) 6 8
↓
1 2 6 8
値2が1より小さくなることはありませんので、それは1の後に挿入する必要があります。
改善の利点は、境界チェックが不要であるため、確認に費やす時間が保存されるということです。データの量が膨大である場合に、保存された時間は驚くほど高くなります。
その後のは、に焦点を当ててみましょう__unguarded_insertion_sort()
機能。ここではその実装です。
template <class RandomAccessIterator, class T>
void __unguarded_insertion_sort_aux(RandomAccessIterator first,
RandomAccessIterator last, T*) {
for (RandomAccessIterator i = first; i != last; ++i)
__unguarded_linear_insert(i, T(*i));
}
template <class RandomAccessIterator>
inline void __unguarded_insertion_sort(RandomAccessIterator first,
RandomAccessIterator last) {
__unguarded_insertion_sort_aux(first, last, value_type(first));
}
何それは実際に行うことは要求している__unguarded_linear_insert()
上で議論されている機能を、。
3.4.2。なぜならば-else文?
さて、なぜ見てみましょう__final_insertion_sort()
機能がそのように書かれています。私たちは知っていることを__unguarded_linear_insert()
smallet要素がソートされたサブリストにあるときに関数を呼び出すことができます。
データの量が少ない場合は量が少ない場合には省略することができるように、境界チェックに費やされる時間が非常に少数であることから、通常の挿入ソートアルゴリズムは、直接リストをソートするために使用することができます。したがって、挿入ソートを__insertion_sort()
直接使用することができます。
データの量が膨大であるときしかし、境界チェックに費やした時間は、このようにして、省略することができない__unguarded_insertion_sort()
機能が境界チェックのための時間を節約するために必要とされます。
しかし、どのように我々は、最小の要素がソートされたサブリストであることを保証することができますか?クイックソートの最小のものは全体のリストの左部分の面積に常にあるので、私たちは使用してその領域を並べ替えることができ__insertion_sort()
、かつ高速を使ってsmellest要素が含まれていない残りの部分__unguarded_insertion_sort()
。
4.ミー・コードを表示します
/* the heap sort */
template <class RandomAccessIterator, class T, class Compare>
void __partial_sort(RandomAccessIterator first, RandomAccessIterator middle,
RandomAccessIterator last, T*, Compare comp) {
make_heap(first, middle, comp);
for (RandomAccessIterator i = middle; i < last; ++i)
if (comp(*i, *first))
__pop_heap(first, middle, i, T(*i), comp, distance_type(first));
sort_heap(first, middle, comp);
}
template <class RandomAccessIterator, class Compare>
inline void partial_sort(RandomAccessIterator first,
RandomAccessIterator middle,
RandomAccessIterator last, Compare comp) {
__partial_sort(first, middle, last, value_type(first), comp);
}
/* the quick sort */
template <class RandomAccessIterator, class T>
RandomAccessIterator __unguarded_partition(RandomAccessIterator first,
RandomAccessIterator last,
T pivot) {
while (true) {
// the first pointer iterates over the list until the current element
// it points at is smaller than the pivot
while (*first < pivot) ++first;
// shift left by one
--last;
// the last pointer iterates over the list until the current element
// it points at is greater than the pivot
while (pivot < *last) --last;
// if the first pointer is after the last pointer
// return the first pointer
if (!(first < last)) return first;
// else swap the two elements the two pointer are pointing at
iter_swap(first, last);
// shift right by one
++first;
}
}
/* the quick sort + the insertion sort in the sort() function */
// find the pivot
inline const T& __median(const T& a, const &T b, const T& c) {
if (a < b)
if (b < c) // a < b < c
return b;
else if (a < c) // a < b, b >= c, a < c
return c;
else
return a;
else if (a < c) // c > a >= b
return a;
else if (b < c) // a >= b, a >= c, b < c
return c;
else
return b;
}
// the combination of the quick sort and the heap sort
template <class RandomAccessIterator, class T, class Size>
void __introsort_loop(RandomAccessIterator first,
RandomAccessIterator last, T*,
Size depth_limit) {
while (last - first > __stl_threshold) { // __stl_threshold: const int 16
// when the condition is true,
// the recursion depth is deep
if (depth_limit == 0) {
// use the heap sort
partial_sort(first, last, last);
return;
}
--depth_limit;
// the quick sort
// find the pivot
RandomAccessIterator cut = __unguarded_partition
(first, last, T(__median(*first, *(first + (last - first)/2),
*(last - 1))));
// recursively sort the part on the right of the pivot
__introsort_loop(cut, last, value_type(first), depth_limit);
// in the next loop the left part will be sorted
last = cut;
}
}
/* the insertion sort */
/* __insertion_sort */
// insert elements without the need to check the boundary
template <class RandomAccessIterator, class T>
void __unguarded_linear_insert(RandomAccessIterator last, T value) {
RandomAccessIterator next = last;
--next;
// find a position for the value to insert to
// and insert the value into that position
while (value < *next) {
*last = *next;
last = next;
--next;
}
*last = value;
}
// implement different code depending on
// whether the value is smaller than the minimum
// in the sorted sublist
template <class RandomAccessIterator, class T>
inline void __linear_insert(RandomAccessIterator first,
RandomAccessIterator last, T*) {
T value = *last;
// if the value is smaller than the first element in the sorted sublist
if (value < *first) {
// all elements in the sorted sublist are shifted right by one
copy_backward(first, last, last + 1);
// the (smallest) value is then in the first position
*first = value;
}
else
// implement the insertion sort without the need to check boundary
__unguarded_linear_insert(last, value);
}
// the main body of the __insertion_sort
template <class RandomAccessIterator>
void __insertion_sort(RandomAccessIterator first, RandomAccessIterator last) {
// if the list is empty, do nothing
if (first == last) return;
// implement the insertion sort
for (RandomAccessIterator i = first + 1; i != last; ++i)
__linear_insert(first, i, value_type(first));
}
/* __unguarded_insertion_sort */
// sort the whole list
template <class RandomAccessIterator, class T>
void __unguarded_insertion_sort_aux(RandomAccessIterator first,
RandomAccessIterator last, T*) {
for (RandomAccessIterator i = first; i != last; ++i)
__unguarded_linear_insert(i, T(*i));
}
// the main body of the __unguarded_insertion_sort
template <class RandomAccessIterator>
inline void __unguarded_insertion_sort(RandomAccessIterator first,
RandomAccessIterator last) {
__unguarded_insertion_sort_aux(first, last, value_type(first));
}
/* the insertion sort step in the sort() function */
template <class RandomAccessIterator>
void __final_insertion_sort(RandomAccessIterator first,
RandomAccessIterator last) {
// when there are more than 16 elements in the list
if (last - first > __stl_threshold) {
// take the first 16 elements to be sorted using the
// __insertion_sort() function
__insertion_sort(first, first + __stl_threshold);
// use __unguarded_insertion_sort() to sort the remaining elements
__unguarded_insertion_sort(first + __stl_threshold, last);
}
else
// sort the elements with the __insertion_sort function
__insertion_sort(first, last);
}
/* the main body of the sort() function */
// calculate the maximum recursion depth
template <class Size>
inline Size __lg(Size n) {
Size k;
for (k = 0; n > 1; n >>= 1) ++k;
return k
}
// the sort() function
template <class RandomAccessIterator>
inline void sort(RandomAccessIterator first, RandomAccessIterator last) {
// when the length of the list to be sorted is greater than 0
if (first != last) {
// combination of the quick sort and the heap sort
__introsort_loop(first, last, value_type(first), __lg(last - first) * 2);
// the insertion sort
__final_insertion_sort(first, last);
}
}
5.私が学んだこと
結論として、私たちは、STLは、ソートのパフォーマンスを向上させるために多くの方法を使用して見ることができます。
これは、使用してクイックソート、ヒープソートとソート挿入を並べ替えをより効率的にするために一緒に。それはかなり良いパフォーマンスを得るために、多くの異なるモデルをミックス機械学習におけるアンサンブル学習、のようなものです。リストは順番にほど遠い場合は、クイックソートアルゴリズムはすぐにリストをソートするために使用されます。再帰の深さが深い場合には、ヒープの並べ替えは、(同じデータで、実際に遅いですが)クイックソートと同様の時間複雑性を有し、代わりに使用されますが、再帰を必要としません。リストはほとんどの順序である場合には、挿入ソートが使用されています。
また、アルゴリズムを改善するために多くの素晴らしい技術を使用しています。ソート()関数で使用する3つのアルゴリズムは、私たちが教科書から学んだものを身近なものと非常によく似ています。しかし、STLは、特に大量のデータの場合のために、はるかに高速にそれらを作るためのアルゴリズムに多くの小さな変更を行います。