プリンキピア

第3章データ構造とアルゴリズム

3.5 Sample Algorithms by Problem3.5 問題別のアルゴリズムの例

Learning Objectives学習目標
By the end of this section, you will be able to:
この節を終えると、次のことができるようになる。
• Discover algorithms that solve data structure problems
・データ構造の問題を解くアルゴリズムを見いだす。
• Understand graph problems and related algorithms
・グラフの問題と、それに関連するアルゴリズムを理解する。
Earlier, we introduced several computing problems, like searching for a target value in a list or implementing an abstract data type (lists, sets, maps, priority queues, graphs). Although every computational problem is unique, these types of problems often share significant similarities with other problems. Computer scientists have identified many canonical problems that represent these common problem templates. Although each of these canonical problems may have many algorithmic solutions, computer scientists have also identified canonical algorithms for solving these problems. In this section, we will introduce canonical problems and survey canonical algorithms for each problem.
英語のヒント
先に、リスト内で目的の値を探したり、抽象データ型(リスト、集合、マップ、優先度付きキュー、グラフ)を実装したりする、いくつかの計算上の問題を紹介した。問題はそれぞれ異なるが、ほかの問題と大きな共通点を持つことが多い。コンピューター科学者は、こうした共通のひな型を表す、多くの標準的な問題を特定してきた。それぞれの問題に、アルゴリズムによる多くの解法があり得るが、その中でも標準的なアルゴリズムが見いだされている。この節では、標準的な問題を紹介し、それぞれの代表的なアルゴリズムを概観する。

Data Structure Problemsデータ構造の問題

Data structure problems are not only useful for implementing data structures, but also as fundamental algorithm design patterns for organizing data to enable efficient solutions to almost every other computing problem.
英語のヒント
データ構造の問題は、データ構造の実装に役立つだけでなく、ほぼすべてのほかの計算上の問題で効率的な解法を可能にするようにデータを整理する、基本的なアルゴリズム設計パターンとしても役立つ。
Searching探索
Searching is the problem of retrieving a target element from a collection of elements. Searching in a linear data structure, such as an array list, can be done using either sequential search or binary search.
探索は、要素の集まりから目的の要素を取得する問題である。配列リストなどの線形データ構造では、逐次探索または二分探索を使って探索できる。
Sequential Search Algorithm
逐次探索アルゴリズム
A sequential search algorithm is a searching algorithm that sequentially checks the collection element-by-element for the target. The runtime of sequential search is in O(N) with respect to N, the number of elements in the list (Figure 3.22).
英語のヒント
逐次探索アルゴリズムは、集まりの中を要素ごとに順番に確認し、目的の要素を探すアルゴリズムである。逐次探索の実行時間は、リストの要素数NについてO(N)に属する(図3.22)。
Figure 3.22

attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license · CC BY 4.0 · Source出典

Figure 3.22 Sequential search is a search algorithm that checks the collection element by element to find a target element. (attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license)
図3.22 逐次探索は、集まりを要素ごとに確認して目的の要素を見つける探索アルゴリズムである。(出典表記:著作権者 Rice University、OpenStax、CC BY 4.0ライセンス)
Binary Search Algorithm
二分探索アルゴリズム
A binary search algorithm recursively narrows down the possible locations for the target in the sorted list. Initially, the algorithm has no information—the target can be anywhere in the entire range of the sorted list. By comparing the target to the middle element of the range, we can rule-out half of the elements in the range. This process can be repeated until we have found the expected location of the target in the sorted list. The runtime of binary search is in O(log N) with respect to N, the number of elements in the sorted list, so long as indexing is a constant-time operation (see Figure 3.11).
英語のヒント
二分探索アルゴリズムは、整列したリストの中で、目的の要素があり得る位置を再帰的に絞り込む。最初は情報がないので、リスト全体のどこにあってもよい。目的の要素を範囲の中央の要素と比較すると、その範囲の半分を候補から除外できる。この処理を、整列したリストで目的の要素があるはずの位置を見つけるまで繰り返せる。インデックスによる参照が定数時間の操作である限り、二分探索の実行時間は、リストの要素数NについてO(log N)に属する(図3.11参照)。
Although the sequential search algorithm works with any collection type such as lists, sets, dictionaries, and priority queues, the binary search algorithm relies on a sorted list with access to elements by index. Consequently, binary search is efficient on array lists that provide constant-time access to the element at any index and inefficient on linked lists, which do not enable constant-time access to elements by index. Binary search relies on the structure of the sorted list to repeatedly rule-out half of the remaining elements.
英語のヒント
逐次探索は、リスト、集合、辞書、優先度付きキューなど、どの種類の集まりでも使える。しかし二分探索は、インデックスで要素にアクセスできる、整列したリストに依存する。そのため、どのインデックスの要素にも定数時間でアクセスできる配列リストでは効率的であるが、それができない連結リストでは非効率である。二分探索は、整列したリストの構造を利用し、残る要素の半分を繰り返し除外する。
Binary search trees represent the concept of binary search in a tree data structure by arranging elements in the tree in sorted order from left to right. Ideally, the root node represents the middle element in the sorted tree and each child roughly divides each subtree in half. But, in the worst case, a binary search tree can look exactly like a linked list where each node contains either zero children or one child. Although such a tree still arranges its elements in sorted order from left to right, comparing the target to each node only reduces the size of the problem by one element rather than ruling out half of the remaining elements—the worst-case binary search tree degrades to sequential search. Balanced binary search trees, such as AVL trees, addressed this worst-case scenario by maintaining the AVL property of balance between left and right subtrees.
英語のヒント
二分探索木は、木の要素を左から右へ整列順に配置することで、二分探索の考え方を木のデータ構造で表す。理想的には、根ノードが整列した木の中央の要素を表し、各子ノードがそれぞれの部分木をおおむね半分に分ける。しかし最悪の場合、二分探索木は、各ノードの子が0個か1個の、連結リストとまったく同じ形になり得る。そのような木でも、要素は左から右へ整列順に並ぶが、目的の要素を各ノードと比較しても、残り半分を除外できず、問題のサイズが1要素ずつしか減らない。最悪の二分探索木は、逐次探索へと性能が低下する。AVL木などの平衡二分探索木は、左右の部分木が平衡するというAVLの性質を維持して、この最悪の場合に対処している。
Sorting整列
Sorting is the problem of rearranging elements into a logical order, typically from least-valued (smallest) to greatest-valued (largest). Sorting is a fundamental problem not only because of the tasks that it directly solves, but also because it is a foundation for many other algorithms such as the binary search algorithm or Kruskal’s algorithm for the minimum spanning tree problem.
英語のヒント
整列は、要素を論理的な順序、通常は値の小さいものから大きいものへ並べ替える問題である。整列が基本的な問題なのは、それが直接解決する作業のためだけでなく、二分探索や最小全域木問題のクラスカル法など、多くのほかのアルゴリズムの基礎にもなるためである。
The most common type of sorting algorithm solves the problem of comparison sorting, or sorting a list of elements where elements are not assigned numeric values but rather defined in relation to other elements. For simplicity, the data are typically assumed to be stored in an array list for indexed access, and the sorting algorithm can either return a new sorted list or rearrange the elements in the array list so that they appear in sorted order.
英語のヒント
最も一般的な整列アルゴリズムは、比較による整列を解く。これは、要素に数値が割り当てられているのではなく、ほかの要素との関係で定義されるリストの整列である。話を簡単にするため、データは通常、インデックスでアクセスできる配列リストに保存されていると仮定する。アルゴリズムは、新しい整列済みリストを返しても、配列リスト内の要素を整列順になるよう並べ替えてもよい。
Merge Sort Algorithm
マージソートアルゴリズム
A merge sort algorithm recursively divides the data into sublists until sublists are one element long—which we know are sorted—and then merges adjacent sorted sublists to eventually return the sorted list. The merge operation combines two sorted sublists to produce a new, larger sorted sublist containing all the elements in sorted order. The actual rearranging of elements occurs by repeatedly applying the merge operation on pairs of adjacent sorted sublists, starting with the smallest single-element sublists, to larger two-element sublists, and eventually reaching the two halves of the entire list of elements. The runtime of merge sort is in O(N log N) with respect to N, the number of elements (see Figure 3.17).
英語のヒント
マージソートは、データを部分リストへ再帰的に分割し、長さが1要素になるまで続ける。1要素のリストは、整列済みであることがわかっている。その後、隣り合う整列済みの部分リストをマージし、最終的に整列したリストを返す。マージ操作は、2つの整列済み部分リストを結合し、すべての要素を整列順に含む、より大きな新しい部分リストを作る。実際の並べ替えは、隣接する整列済み部分リストの組に、マージ操作を繰り返すことで進む。最小の1要素の部分リストから、より大きな2要素の部分リストへ進み、最後はリスト全体の左右半分に至る。マージソートの実行時間は、要素数NについてO(N log N)に属する(図3.17参照)。
Quicksort Algorithm
クイックソートアルゴリズム
A quicksort algorithm recursively sorts data by applying the binary search tree algorithm design pattern to partition data around pivot elements. Whereas the merge sort algorithm rearranges elements by repeatedly merging sorted sublists after each recursive subproblem, the quicksort algorithm rearranges elements by partitioning data before each recursive subproblem. The partition operation takes a pivot and rearranges the elements into three sections, from left to right: the sublist of all elements less than the pivot, the pivot element, and the sublist of all elements greater than (or equal to) the pivot. Each of the two sublists resulting from the partition operation is a recursive quicksort subproblem; when both of the sublists are sorted, the entire list will be sorted. The runtime of quicksort depends on the choice of each pivot element during the execution of the recursive algorithm, but in practice, for most of the inputs, the runtime is in O(N log N) with respect to N, the number of elements (Figure 3.23).
英語のヒント
クイックソートは、二分探索木の設計パターンを使い、ピボットとなる要素を基準にデータを分割して、再帰的に整列する。マージソートが、各再帰的な部分問題の後に整列済み部分リストを繰り返しマージして並べ替えるのに対し、クイックソートは、各部分問題の前にデータを分割して並べ替える。分割操作はピボットを1つ取り、左から順に、ピボット未満のすべての要素の部分リスト、ピボット要素、ピボット以上のすべての要素の部分リスト、という3つの部分へ並べ替える。この分割によってできた2つの部分リストは、それぞれクイックソートの再帰的な部分問題となる。両方を整列すると、リスト全体が整列される。クイックソートの実行時間は、再帰アルゴリズムの実行中に、それぞれのピボットをどう選ぶかに依存する。しかし実際には、大半の入力で、要素数NについてO(N log N)に属する(図3.23)。
Figure 3.23

attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license · CC BY 4.0 · Source出典

Figure 3.23 Quicksort is a divide and conquer sorting algorithm that sorts elements by recursively partitioning elements around a pivot. (attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license)
図3.23 クイックソートは、ピボットを基準に要素を再帰的に分割して整列する、分割統治型の整列アルゴリズムである。(出典表記:著作権者 Rice University、OpenStax、CC BY 4.0ライセンス)
Heapsort Algorithm
ヒープソートアルゴリズム
A heapsort algorithm adds all elements to a binary heap priority queue data structure using the comparison operation to determine priority value, and returns the sorted list by repeatedly removing from the priority queue element by element. The runtime of heapsort is in O(N log N) with respect to N, the number of elements. The logarithmic time factor is due to the time it takes to add or remove each element from the binary heap (Figure 3.24).
英語のヒント
ヒープソートは、比較操作で優先度を決めながら、すべての要素を二分ヒープによる優先度付きキューへ追加する。その後、キューから要素を1つずつ繰り返し取り出し、整列したリストを返す。ヒープソートの実行時間は、要素数NについてO(N log N)に属する。対数時間の因子は、二分ヒープへ各要素を追加したり、取り出したりするのにかかる時間による(図3.24)。
Figure 3.24

attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license · CC BY 4.0 · Source出典

Figure 3.24 Heapsort uses the binary heap data structure to sort elements by adding and then removing all elements from the heap. (attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license)
図3.24 ヒープソートは、二分ヒープのデータ構造を使い、すべての要素をヒープへ追加してから取り出すことで整列する。(出典表記:著作権者 Rice University、OpenStax、CC BY 4.0ライセンス)
Many comparison sorting algorithms share the same O(N log N) runtime bound with respect to N, the number of elements. Computer scientists have shown with a combinatorial proof that, in the worst case, a comparison sorting algorithm cannot do better than O(N log N) comparisons. It is impossible to design a worst-case O(N) runtime comparison sorting algorithm. In fact, a commonly used version of heapsort (which is also asymptotically faster) is to first build a binary heap (i.e., first arrange the input numbers in an array, then repeatedly call a function to turn the original array into a binary heap; one can show that the running time of this part is linear in N, which is why this is faster than constructing the heap by adding numbers one by one), then remove elements one by one from the end of the heap.
英語のヒント
多くの比較整列アルゴリズムは、要素数NについてO(N log N)という同じ実行時間の限界を持つ。コンピューター科学者は、最悪の場合、比較整列アルゴリズムはO(N log N)より少ない比較で済ますことができないと、組合せによる証明で示した。最悪時の実行時間がO(N)である比較整列アルゴリズムを設計することは不可能である。実際、よく使われるヒープソートの一種は、漸近的にもより高速で、まず二分ヒープを構築し、その後、ヒープの末尾から要素を1つずつ取り出す。ヒープを構築するには、最初に入力の数を配列に置き、関数を繰り返し呼び出して、元の配列を二分ヒープに変える。この部分の実行時間はNに対して線形になると示せるため、数を1つずつ追加してヒープを作るより速い。
But not all sorting problems are comparison sorting problems. In fact, a commonly used version of heapsort (which is also asymptotically faster) is to first build a binary heap (i.e., first arrange the input numbers in an array, then repeatedly call a function to turn the original array into a binary heap. One can show that the running time of this part is linear in N, which is why this is faster than constructing the heap by adding numbers one by one), then remove elements one by one from the end of the heap as explained previously.
しかし、すべての整列問題が比較整列の問題というわけではない。実際、よく使われるヒープソートの一種は、漸近的にもより高速で、最初に二分ヒープを構築する。すなわち、まず入力の数を配列に並べ、次に関数を繰り返し呼んで、その配列を二分ヒープに変える。この部分の実行時間はNに対して線形になると示せる。数を1つずつ追加してヒープを作るより速いのは、このためである。その後、先に説明したように、ヒープの末尾から要素を1つずつ取り出す。
Another type of sorting problem that is not restricted to pairwise comparisons is known as count sorting, or sorting a list of elements by organizing elements into categories and rearranging the categories into a logical order. For example, the problem of sorting a deck of cards can be seen as a count sorting problem if we put the cards into numeric stacks and then rearrange the stacks into a logical order. By changing the assumptions of the problem, count sorting algorithms can run in O(N) time by first assigning each element to its respective category and then unpacking each category so that elements appear in sorted order.
英語のヒント
要素の対ごとの比較に制限されない、別の整列問題に、計数ソートがある。これは、要素を分類ごとに整理し、その分類を論理的な順序に並べ替えてリストを整列する方法である。たとえばトランプを数ごとの束に分け、束を論理的な順序に並べるなら、カードを整列する問題を計数ソートとして捉えられる。問題の仮定を変えることで、計数ソートは、最初に各要素を対応する分類へ割り当て、次に各分類から取り出して整列順に並べることにより、O(N)時間で実行できる。