This commit is contained in:
krahets
2026-03-30 08:17:41 +08:00
parent 68cafe99dd
commit 46bccf0065
484 changed files with 60193 additions and 20315 deletions
+62 -63
View File
@@ -6,7 +6,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="アニメーション図解ワンクリック実行データ構造とアルゴリズムチュートリアル">
<meta name="description" content="アニメーション図解ワンクリック実行コードで学べるデータ構造とアルゴリズムの入門書">
<meta name="author" content="krahets">
@@ -117,7 +117,7 @@
<path
d="M480 32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9L381.7 53c-48 48-113.1 75-181 75H192 160 64c-35.3 0-64 28.7-64 64v96c0 35.3 28.7 64 64 64l0 128c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32V352l8.7 0c67.9 0 133 27 181 75l43.6 43.6c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V300.4c18.6-8.8 32-32.5 32-60.4s-13.4-51.6-32-60.4V32zm-64 76.7V240 371.3C357.2 317.8 280.5 288 200.7 288H192V192h8.7c79.8 0 156.5-29.8 215.3-83.3z" />
</svg>
<span>日本語版審閱者を募集しています!詳細<a href="https://github.com/krahets/hello-algo/blob/main/ja/CONTRIBUTING.md">CONTRIBUTING.md</a>参照してください。</span>
<span>日本語版のレビュアーを募集しています。詳しく<a href="/ja/chapter_appendix/contribution/">こちら</a>ご覧ください。</span>
</div>
</div>
@@ -341,7 +341,7 @@
<span class="md-ellipsis">
はじめに
@@ -358,7 +358,7 @@
<span class="md-nav__icon md-icon"></span>
はじめに
</label>
@@ -618,7 +618,7 @@
<span class="md-ellipsis">
1.1 &nbsp; アルゴリズムはどこにでもある
1.1 &nbsp; アルゴリズムは至るところにある
@@ -646,7 +646,7 @@
<span class="md-ellipsis">
1.2 &nbsp; アルゴリズムとは何か
1.2 &nbsp; アルゴリズムとは
@@ -783,7 +783,7 @@
<span class="md-ellipsis">
2.1 &nbsp; アルゴリズム効率評価
2.1 &nbsp; アルゴリズム効率評価
@@ -1062,7 +1062,7 @@
<span class="md-ellipsis">
3.3 &nbsp; 数値の符号化 *
3.3 &nbsp; 数値エンコーディング *
@@ -1090,7 +1090,7 @@
<span class="md-ellipsis">
3.4 &nbsp; 文字の符号化 *
3.4 &nbsp; 文字エンコーディング *
@@ -1175,7 +1175,7 @@
<a href="#1" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; 重要ポイント
1. &nbsp; 重要ポイントの振り返り
</span>
</a>
@@ -1663,7 +1663,7 @@
<span class="md-ellipsis">
第 6 章 &nbsp; ハッシュ
第 6 章 &nbsp; ハッシュテーブル
@@ -1685,7 +1685,7 @@
<span class="md-nav__icon md-icon"></span>
第 6 章 &nbsp; ハッシュ
第 6 章 &nbsp; ハッシュテーブル
</label>
@@ -1707,7 +1707,7 @@
<span class="md-ellipsis">
6.1 &nbsp; ハッシュ
6.1 &nbsp; ハッシュテーブル
@@ -2014,7 +2014,7 @@
<span class="md-ellipsis">
7.5 &nbsp; AVL木 *
7.5 &nbsp; AVL 木 *
@@ -2177,7 +2177,7 @@
<span class="md-ellipsis">
8.2 &nbsp; ヒープ構築操作
8.2 &nbsp; ヒープ構築
@@ -2563,7 +2563,7 @@
<span class="md-ellipsis">
10.2 &nbsp; 二分探索の挿入
10.2 &nbsp; 二分探索の挿入位置
@@ -2619,7 +2619,7 @@
<span class="md-ellipsis">
10.4 &nbsp; ハッシュ最適化戦略
10.4 &nbsp; ハッシュによる最適化戦略
@@ -2647,7 +2647,7 @@
<span class="md-ellipsis">
10.5 &nbsp; 探索アルゴリズムの再認識
10.5 &nbsp; 探索アルゴリズム再考
@@ -3185,7 +3185,7 @@
<span class="md-ellipsis">
12.1 &nbsp; 分割統治アルゴリズム
12.1 &nbsp; 分割統治
@@ -3241,7 +3241,7 @@
<span class="md-ellipsis">
12.3 &nbsp; 木の構築問題
12.3 &nbsp; 二分木の構築問題
@@ -3269,7 +3269,7 @@
<span class="md-ellipsis">
12.4 &nbsp; ハノイの塔問題
12.4 &nbsp; ハノイの塔問題
@@ -3462,7 +3462,7 @@
<span class="md-ellipsis">
13.3 &nbsp; 部分集合和問題
13.3 &nbsp; 部分和問題
@@ -3490,7 +3490,7 @@
<span class="md-ellipsis">
13.4 &nbsp; Nクイーン問題
13.4 &nbsp; n クイーン問題
@@ -3631,7 +3631,7 @@
<span class="md-ellipsis">
14.1 &nbsp; 動的計画法の初歩
14.1 &nbsp; 動的計画法入門
@@ -3659,7 +3659,7 @@
<span class="md-ellipsis">
14.2 &nbsp; DP 問題特性
14.2 &nbsp; 動的計画法の問題特性
@@ -3687,7 +3687,7 @@
<span class="md-ellipsis">
14.3 &nbsp; DP の解法の考え方
14.3 &nbsp; 動的計画法の問題解決の考え方
@@ -3715,7 +3715,7 @@
<span class="md-ellipsis">
14.4 &nbsp; 0-1ナップサック問題
14.4 &nbsp; 0-1 ナップサック問題
@@ -3908,7 +3908,7 @@
<span class="md-ellipsis">
15.1 &nbsp; 貪欲アルゴリズム
15.1 &nbsp; 貪欲
@@ -4153,7 +4153,7 @@
<span class="md-ellipsis">
16.2 &nbsp; 一緒に作に参加する
16.2 &nbsp; 一緒に作に参加しましょう
@@ -4299,7 +4299,7 @@
<a href="#1" class="md-nav__link">
<span class="md-ellipsis">
1. &nbsp; 重要ポイント
1. &nbsp; 重要ポイントの振り返り
</span>
</a>
@@ -4355,49 +4355,48 @@
<!-- Page content -->
<h1 id="35">3.5 &nbsp; まとめ<a class="headerlink" href="#35" title="Permanent link">&para;</a></h1>
<h3 id="1">1. &nbsp; 重要ポイント<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<h3 id="1">1. &nbsp; 重要ポイントの振り返り<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<ul>
<li>データ構造は論理構造と物理構造の2つの観点から分類できます。論理構造はデータ間の論理的関係を記述し、物理構造はデータがメモリにどのように格納されるかを記述します。</li>
<li>よく使用される論理構造には、線形構造、木、ネットワークがあります。通常、論理構造に基づいてデータ構造を線形(配列、連結リスト、スタック、キュー)と非線形(木、グラフ、ヒープ)に分けます。ハッシュ表の実装は線形と非線形の両方のデータ構造を含む場合があります。</li>
<li>プログラム実行中の際、データはメモリに格納されます。各メモリ空間には対応するアドレスがあり、プログラムはれらのアドレスを通じてデータにアクセスします。</li>
<li>物理構造は連続空間格納(配列)と離散空間格納(連結リスト)に分けることができます。すべてのデータ構造は配列、連結リスト、またはその両方の組み合わせを使用して実装されます。</li>
<li>コンピュータ基本データ型には、整数<code>byte</code><code>short</code><code>int</code><code>long</code>、浮動小数点数<code>float</code><code>double</code>、文字<code>char</code>)、ブール値(<code>bool</code>)が含まれます。データ型の値の範囲は、そのサイズと表現に依存します。</li>
<li>符号絶対値、1の補数、2の補数は、コンピュータで整数をエンコードする3つの方法であり、相互に変換することができます。符号絶対値の最上位ビット符号ビットで、残りのビット数値の値を表します。</li>
<li>整数はコンピュータで2の補数によってエンコードされます。この表現の利点には、(i)コンピュータが正と負の整数の加算を統一できる、(ii)減算用の特別なハードウェア回路を設計する必要がない、(iii)正と負の0の曖昧さがない、がありま</li>
<li>浮動小数点数のエンコーディングは、1つの符号ビット、8つの指数ビット、23の仮数ビットで構成されます。指数ビットのため、浮動小数点数の範囲は整数よりはるかに大きくなりますが、精度犠牲にます。</li>
<li>ASCIIは最初期の英語文字セットで、1バイトの長さで計127文字です。GBKは人気のある中国語文字セットで、2万字以上の中国語文字を含みます。Unicodeは世界の様々な言語の文字を含む完全な文字セット標準を提供することを目的とし、文字エンコーディング方法の不一致による文字化け問題を解決します。</li>
<li>UTF-8は最も人気があり一般的なUnicodeエンコーディング方法です。これは可変長エンコーディング方法で、優れた拡張性と空間効率を持ちます。UTF-16UTF-32は固定長エンコーディング方法です。中国語文字をエンコードする際、UTF-16UTF-8よりも少ない空間を使用します。JavaC#などのプログラミング言語はデフォルトでUTF-16エンコーディングを使用します。</li>
<li>データ構造は論理構造と物理構造という 2 つの観点から分類できます。論理構造はデータ要素間の論理的関係を記述し、物理構造はデータのコンピュータメモリ上での格納方法を記述します。</li>
<li>代表的な論理構造には、線形、木構造、網状構造などがあります。通常、論理構造に基づいてデータ構造を線形(配列、連結リスト、スタック、キュー)と非線形(木、グラフ、ヒープ)の 2 種類に分類します。ハッシュテーブルの実装には、線形データ構造と非線形データ構造が同時に含まれる場合があります。</li>
<li>プログラム実行、データはコンピュータメモリに格納されます。各メモリ空間には対応するメモリアドレスがあり、プログラムはれらのメモリアドレスを通じてデータにアクセスします。</li>
<li>物理構造は主に連続領域への格納(配列)と分散領域への格納(連結リスト)に分けられます。すべてのデータ構造は配列、連結リスト、またはその両方の組み合わせによって実装されます。</li>
<li>コンピュータにおける基本データ型には、整数 <code>byte</code><code>short</code><code>int</code><code>long</code>、浮動小数点数 <code>float</code><code>double</code>、文字 <code>char</code>、真偽値 <code>bool</code> があります。これらの値域は、使用する記憶領域の大きさと表現方式によって決まります。</li>
<li>符号付き絶対値表現、1 の補数、2 の補数は、コンピュータで数値を符号化する 3 つの方法であり、相互に変換できます。整数の符号付き絶対値表現では最上位ビット符号ビットで、残りのビット数値の値す。</li>
<li>整数はコンピュータ内では 2 の補数の形式で格納されます。2 の補数表現では、コンピュータは正数と負数の加算を同じように扱うことができ、減算のために特別なハードウェア回路を別途設計する必要がなく、さらに正負のゼロが重複する問題もありません</li>
<li>浮動小数点数の符号化は、1 ビットの符号部、8 ビットの指数部、23 ビットの仮数部で構成されます。指数部があるため、浮動小数点数の値域は整数よりはるかにくなりますが、その代償として精度犠牲になります。</li>
<li>ASCII コードは最も早く登場した英字文字集合で、長さは 1 バイト、収録文字数は 127 です。GBK 文字集合はよく使われる中国語文字集合で、2 万字以上の漢字を収録しています。Unicode は完全な文字集合標準を提供することを目指しており、世界中のさまざまな言語の文字を収録することで、文字コード方式の不一致によって生じる文字化け問題を解決します。</li>
<li>UTF-8 は最も広く使われている Unicode の符号化方式で、汎用性が非常に高いです。可変長の符号化方式であり、拡張性に優れ、記憶領域の利用効率を効果的に高めます。UTF-16UTF-32 は固定長の符号化方式です。中国語を符号化する場合、UTF-16UTF-8 よりも使用領域が小さくなります。JavaC# などのプログラミング言語はデフォルトで UTF-16 を使用します。</li>
</ul>
<h3 id="2-q-a">2. &nbsp; Q &amp; A<a class="headerlink" href="#2-q-a" title="Permanent link">&para;</a></h3>
<p><strong>Q</strong>: なぜハッシュ表は線形と非線形の両方のデータ構造を含むのですか?</p>
<p>ハッシュ表の基礎構造は配列です。ハッシュ衝突を解決するために「チェイン法」を使用する場合があります(後の節「ハッシュ衝突」で説明):配列の各バケットは連結リストを指し、その長さが特定の閾値より大きくなると木(通常は赤黒木)に変換される可能性があります。
格納の観点から、ハッシュ表の基礎構造は配列で、各バケットには値、連結リスト、または木が含まれる場合があります。したがって、ハッシュは線形データ構造(配列、連結リスト)と非線形データ構造(木)の両方を含む場合があります。</p>
<p><strong>Q</strong>: <code>char</code>型の長さは1バイトですか?</p>
<p><code>char</code>型の長さは、プログラミング言語のエンコーディング方法によって決まります。えば、Java、JavaScript、TypeScript、C#はすべてUTF-16エンコーディングUnicodeコードポイントを保存するため)を使用するため、<code>char</code>型の長さは2バイトです。</p>
<p><strong>Q</strong>: 配列ベースデータ構造を「静的データ構造」と呼ぶことに曖昧はありませんか?スタックもプッシュやポップなどの「動的」操作を実行できます。</p>
<p>スタックは動的なデータ操作を実できますが、データ構造は依然として「静的」です(長さが固定)。配列ベースのデータ構造は動的に要素を追加または削除できますが、その容量は固定されています。スタックサイズが事前に割り当てられたサイズを超え場合、古い配列は新しく作成されたより大きな配列にコピーされます。</p>
<p><strong>Q</strong>: スタック(キュー)を構築する際、そのサイズ指定されていないのに、なぜ「静的データ構造」なのですか?</p>
<p>プログラミング言語では、スタック(キュー)の初期容量を手で指定する必要はありません。このタスクはクラス内で自動的に完了されます。えば、Java<code>ArrayList</code>の初期容量は通常10です。さらに、拡張操作も自動的に完了されます。詳細については、後続の「リスト」の章を参照してください。</p>
<p><strong>Q</strong>: 符号絶対値を2の補数に変換する方法は「最初に否定してから1を加える」ですので、2の補数を符号絶対値に変換することはその逆操作「最初に1を減算してから否定する」であるべきです。
しかし、2の補数も「最初に否定してから1を加える」を通じて符号絶対値に変換できます。なぜですか?</p>
<p><strong>A</strong>: これは、符号絶対値と2の補数間の相互変換が「補数」の計算と等価だからです。まず補数を定義します:<span class="arithmatex">\(a + b = c\)</span>と仮定すると、<span class="arithmatex">\(a\)</span><span class="arithmatex">\(b\)</span><span class="arithmatex">\(c\)</span>に対する補数と言い、逆に<span class="arithmatex">\(b\)</span><span class="arithmatex">\(a\)</span><span class="arithmatex">\(c\)</span>に対する補数と言います。</p>
<p>長さ<span class="arithmatex">\(n = 4\)</span>の二進数<span class="arithmatex">\(0010\)</span>が与えられた場合、この数が符号絶対値(符号ビットを無視)の場合、その2の補数は「最初に否定してから1を加える」ことで得られます:</p>
<p><strong>Q</strong>なぜハッシュテーブルには線形データ構造と非線形データ構造が同時に含まれるのですか?</p>
<p>ハッシュテーブルの基盤は配列であり、ハッシュ衝突を解決するために「チェイン法」(後続の「ハッシュ衝突」の章で説明します)を使うことがあります。配列の各バケットは 1 つの連結リストを指し、その連結リストの長さがある閾値を超えると木(通常は赤黒木)に変換されることもあります。</p>
<p>格納の観点から見ると、ハッシュテーブルの基盤は配列であり、各バケットスロットには値が入ることもあれば、連結リストや木が入ることもあります。したがって、ハッシュテーブルには線形データ構造(配列、連結リスト)と非線形データ構造(木)が同時に含まれる場合があります。</p>
<p><strong>Q</strong><code>char</code> 型の長さは 1 バイトですか?</p>
<p><code>char</code> 型の長さは、プログラミング言語が採用する符号化方式によって決まります。たとえば、Java、JavaScript、TypeScript、C# はいずれも UTF-16 符号化Unicode コードポイントを保持)を採用しているため、<code>char</code> 型の長さは 2 バイトです。</p>
<p><strong>Q</strong>配列ベースで実装されたデータ構造を「静的データ構造」と呼ぶのは曖昧はありませんか? スタックも push や pop などの操作ができ、これらの操作はどれも「動的」です。</p>
<p>スタックは確かに動的なデータ操作を実できますが、データ構造自体は依然として「静的」(長さが不変)です。配列ベースのデータ構造でも要素を動的に追加または削除できますが、その容量は固定です。データ量が事前に確保した大きさを超え場合は、より大きな新しい配列を作成し、古い配列の内容を新しい配列にコピーする必要があります。</p>
<p><strong>Q</strong>スタック(キュー)を構築するときにサイズ指定ていないのに、なぜそれらは「静的データ構造」なのですか?</p>
<p>水準プログラミング言語では、スタック(キュー)の初期容量を手で指定する必要はなく、この作業はクラス内で自動的に行われます。たとえば、Java<code>ArrayList</code> の初期容量は通常 10 です。また、容量拡張も自動的に実装されています。詳しくは後続の「リスト」の章を参照してください。</p>
<p><strong>Q</strong>:符号付き絶対値表現から 2 の補数への変換方法は「先にビット反転してから 1 を加える」です、2 の補数から符号付き絶対値表現への変換は逆演算である「先に 1 を引いてからビット反転する」べきなのに、同じく「先にビット反転してから 1 を加える」でも求められます。これはなぜですか?</p>
<p>これは、符号付き絶対値表現と 2 の補数の相互変換が、実際には「補数」を計算する過程だからです。まず補数の定義を示します。<span class="arithmatex">\(a + b = c\)</span> とすると、<span class="arithmatex">\(a\)</span><span class="arithmatex">\(b\)</span> から <span class="arithmatex">\(c\)</span> への補数と呼び、逆に <span class="arithmatex">\(b\)</span><span class="arithmatex">\(a\)</span> から <span class="arithmatex">\(c\)</span> への補数と呼びます。</p>
<p>長さ <span class="arithmatex">\(n = 4\)</span> ビットの 2 進数 <span class="arithmatex">\(0010\)</span> が与えられたとします。この数を符号付き絶対値表現(符号ビットは考慮しない)とみなすと、その 2 の補数は「先にビット反転してから 1 を加える」ことで得られます。</p>
<div class="arithmatex">\[
0010 \rightarrow 1101 \rightarrow 1110
\]</div>
<p>符号絶対値と2の補数の和<span class="arithmatex">\(0010 + 1110 = 10000\)</span>であることを観察します。つまり、2の補数<span class="arithmatex">\(1110\)</span>は符号絶対値<span class="arithmatex">\(0010\)</span><span class="arithmatex">\(10000\)</span>に対する「補数」です。<strong>これは、上記の「最初に否定してから1を加える」が<span class="arithmatex">\(10000\)</span>に対する補数計算と等価であることを意味します</strong></p>
<p>では、<span class="arithmatex">\(1110\)</span><span class="arithmatex">\(10000\)</span>に対する「補数」はでしょうか?「最初に否定してから1を加える」ことで計算できます</p>
<p>ここで、符号付き絶対値表現と 2 の補数の和<span class="arithmatex">\(0010 + 1110 = 10000\)</span> となります。つまり、2 の補数 <span class="arithmatex">\(1110\)</span> は符号付き絶対値表現 <span class="arithmatex">\(0010\)</span> から <span class="arithmatex">\(10000\)</span> への「補数」です。<strong>これは、上記の「先にビット反転してから 1 を加える」が、実際には <span class="arithmatex">\(10000\)</span> への補数計算する過程であることを意味します</strong></p>
<p>では、2 の補数 <span class="arithmatex">\(1110\)</span> から <span class="arithmatex">\(10000\)</span> への「補数」はいくつでしょうか。これもやはり「先にビット反転してから 1 を加える」ことで求められます</p>
<div class="arithmatex">\[
1110 \rightarrow 0001 \rightarrow 0010
\]</div>
<p>言い換えると、符号絶対値と2の補数は互いに<span class="arithmatex">\(10000\)</span>に対する「補数」であるため、「符号絶対値から2の補数」と「2の補数から符号絶対値」は同じ操作(最初に否定してから1を加える)で実できます。</p>
<p>もちろん、「最初に否定してから1を加える」の逆操作を使用して2の補数<span class="arithmatex">\(1110\)</span>の符号絶対値を求めることもできます。つまり、「最初に1を減算してから否定する」:</p>
<p>言い換えると、符号付き絶対値表現と 2 の補数は互いに相手から <span class="arithmatex">\(10000\)</span> への「補数」なので、「符号付き絶対値表現から 2 の補数への変換」と「2 の補数から符号付き絶対値表現への変換」は同じ操作(先にビット反転してから 1 を加える)で実できます。</p>
<p>もちろん、逆演算を用いて 2 の補数 <span class="arithmatex">\(1110\)</span> の符号付き絶対値表現を求めることもでき、その場合は「先に 1 を引いてからビット反転する」ことになります。</p>
<div class="arithmatex">\[
1110 \rightarrow 1101 \rightarrow 0010
\]</div>
<p>要約すると、「最初に否定してから1を加える」と「最初に1を減算してから否定する」は両方とも<span class="arithmatex">\(10000\)</span>に対する補数を計算しており、等価です。</p>
<p>本質的に、「否定」操作は実際には<span class="arithmatex">\(1111\)</span>に対する補数を求めることです(<code>符号絶対値 + 1の補数 = 1111</code>常に成り立つため)。そして1の補数に1を加えることは<span class="arithmatex">\(10000\)</span>に対する2の補数と等しくなります。</p>
<p>上記では<span class="arithmatex">\(n = 4\)</span>を例に取りましたが、任意の桁数の任意の二進数に一般化できます。</p>
<p>まとめると、「先にビット反転してから 1 を加える」と「先に 1 を引いてからビット反転する」の 2 つの演算は、どちらも <span class="arithmatex">\(10000\)</span> への補数を計算しており、等価です。</p>
<p>本質的に、「ビット反転」という操作は実際には <span class="arithmatex">\(1111\)</span> への補数を求めています(常に <code>符号付き絶対値表現 + 1 の補数 = 1111</code> が成り立つため)。そして、1 の補数にさらに 1 を加えて得られる 2 の補数が、<span class="arithmatex">\(10000\)</span> への補数です。</p>
<p>上記では <span class="arithmatex">\(n = 4\)</span> を例にましたが、この考え方は任意のビット長の 2 進数に一般化できます。</p>
<!-- Source file information -->
@@ -4420,7 +4419,7 @@ aria-label="フッター"
<a
href="../character_encoding/"
class="md-footer__link md-footer__link--prev"
aria-label="前: 3.4 &amp;nbsp; 文字の符号化 *"
aria-label="前: 3.4 &amp;nbsp; 文字エンコーディング *"
rel="prev"
>
<div class="md-footer__button md-icon">
@@ -4432,7 +4431,7 @@ aria-label="フッター"
</span>
<div class="md-ellipsis">
3.4 &nbsp; 文字の符号化 *
3.4 &nbsp; 文字エンコーディング *
</div>
</div>
</a>