mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-26 18:17:13 +00:00
build
This commit is contained in:
@@ -28,7 +28,7 @@ VS Code 擁有強大的擴展包生態系統,支持大多數程式語言的執
|
||||
|
||||
### 2. C/C++ 環境
|
||||
|
||||
1. Windows 系統需要安裝 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241));MacOS 自帶 Clang ,無須安裝。
|
||||
1. Windows 系統需要安裝 [MinGW](https://sourceforge.net/projects/mingw-w64/files/)([配置教程](https://blog.csdn.net/qq_33698226/article/details/129031241));macOS 自帶 Clang ,無須安裝。
|
||||
2. 在 VS Code 的擴充功能市場中搜索 `c++` ,安裝 C/C++ Extension Pack 。
|
||||
3. (可選)開啟 Settings 頁面,搜尋 `Clang_format_fallback Style` 程式碼格式化選項,設定為 `{ BasedOnStyle: Microsoft, BreakBeforeBraces: Attach }` 。
|
||||
|
||||
@@ -39,7 +39,7 @@ VS Code 擁有強大的擴展包生態系統,支持大多數程式語言的執
|
||||
|
||||
### 4. C# 環境
|
||||
|
||||
1. 下載並安裝 [.Net 8.0](https://dotnet.microsoft.com/en-us/download) 。
|
||||
1. 下載並安裝 [.NET 8.0](https://dotnet.microsoft.com/en-us/download) 。
|
||||
2. 在 VS Code 的擴充功能市場中搜索 `C# Dev Kit` ,安裝 C# Dev Kit ([配置教程](https://code.visualstudio.com/docs/csharp/get-started))。
|
||||
3. 也可使用 Visual Studio([安裝教程](https://learn.microsoft.com/zh-cn/visualstudio/install/install-visual-studio?view=vs-2022))。
|
||||
|
||||
|
||||
@@ -86,7 +86,9 @@ comments: true
|
||||
| level-order traversal | 層序走訪 |
|
||||
| breadth-first traversal | 廣度優先走訪 |
|
||||
| depth-first traversal | 深度優先走訪 |
|
||||
| binary search tree | 二元搜尋樹 |
|
||||
| pre-order traversal | 前序走訪 |
|
||||
| in-order traversal | 中序走訪 |
|
||||
| post-order traversal | 後序走訪 |
|
||||
| balanced binary search tree | 平衡二元搜尋樹 |
|
||||
| balance factor | 平衡因子 |
|
||||
| heap | 堆積 |
|
||||
|
||||
@@ -172,7 +172,7 @@ comments: true
|
||||
// 建構子
|
||||
class ListNode(x: Int) {
|
||||
val _val: Int = x // 節點值
|
||||
val next: ListNode? = null // 指向下一個節點的引用
|
||||
var next: ListNode? = null // 指向下一個節點的引用
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1421,8 +1421,8 @@ comments: true
|
||||
// 建構子
|
||||
class ListNode(x: Int) {
|
||||
val _val: Int = x // 節點值
|
||||
val next: ListNode? = null // 指向後繼節點的引用
|
||||
val prev: ListNode? = null // 指向前驅節點的引用
|
||||
var next: ListNode? = null // 指向後繼節點的引用
|
||||
var prev: ListNode? = null // 指向前驅節點的引用
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ comments: true
|
||||
|
||||
為了實現每個元素只被選擇一次,我們考慮引入一個布林型陣列 `selected` ,其中 `selected[i]` 表示 `choices[i]` 是否已被選擇,並基於它實現以下剪枝操作。
|
||||
|
||||
- 在做出選擇 `choice[i]` 後,我們就將 `selected[i]` 賦值為 $\text{True}$ ,代表它已被選擇。
|
||||
- 在做出選擇 `choices[i]` 後,我們就將 `selected[i]` 賦值為 $\text{True}$ ,代表它已被選擇。
|
||||
- 走訪選擇串列 `choices` 時,跳過所有已被選擇的節點,即剪枝。
|
||||
|
||||
如圖 13-6 所示,假設我們第一輪選擇 1 ,第二輪選擇 3 ,第三輪選擇 2 ,則需要在第二輪剪掉元素 1 的分支,在第三輪剪掉元素 1 和元素 3 的分支。
|
||||
|
||||
@@ -14,7 +14,7 @@ comments: true
|
||||
- 原碼、一補數和二補數是在計算機中編碼數字的三種方法,它們之間可以相互轉換。整數的原碼的最高位是符號位,其餘位是數字的值。
|
||||
- 整數在計算機中是以二補數的形式儲存的。在二補數表示下,計算機可以對正數和負數的加法一視同仁,不需要為減法操作單獨設計特殊的硬體電路,並且不存在正負零歧義的問題。
|
||||
- 浮點數的編碼由 1 位符號位、8 位指數位和 23 位分數位構成。由於存在指數位,因此浮點數的取值範圍遠大於整數,代價是犧牲了精度。
|
||||
- ASCII 碼是最早出現的英文字元集,長度為 1 位元組,共收錄 127 個字元。GBK 字元集是常用的中文字元集,共收錄兩萬多個漢字。Unicode 致力於提供一個完整的字元集標準,收錄世界上各種語言的字元,從而解決由於字元編碼方法不一致而導致的亂碼問題。
|
||||
- ASCII 碼是最早出現的英文字元集,長度為 1 位元組,共收錄 128 個字元。GBK 字元集是常用的中文字元集,共收錄兩萬多個漢字。Unicode 致力於提供一個完整的字元集標準,收錄世界上各種語言的字元,從而解決由於字元編碼方法不一致而導致的亂碼問題。
|
||||
- UTF-8 是最受歡迎的 Unicode 編碼方法,通用性非常好。它是一種變長的編碼方法,具有很好的擴展性,有效提升了儲存空間的使用效率。UTF-16 和 UTF-32 是等長的編碼方法。在編碼中文時,UTF-16 佔用的空間比 UTF-8 更小。Java 和 C# 等程式語言預設使用 UTF-16 編碼。
|
||||
|
||||
### 2. Q & A
|
||||
|
||||
@@ -27,12 +27,12 @@ comments: true
|
||||
根據定義,`preorder` 和 `inorder` 都可以劃分為三個部分。
|
||||
|
||||
- 前序走訪:`[ 根節點 | 左子樹 | 右子樹 ]` ,例如圖 12-5 的樹對應 `[ 3 | 9 | 2 1 7 ]` 。
|
||||
- 中序走訪:`[ 左子樹 | 根節點 | 右子樹 ]` ,例如圖 12-5 的樹對應 `[ 9 | 3 | 1 2 7 ]` 。
|
||||
- 中序走訪:`[ 左子樹 | 根節點 | 右子樹 ]` ,例如圖 12-5 的樹對應 `[ 9 | 3 | 1 2 7 ]` 。
|
||||
|
||||
以上圖資料為例,我們可以透過圖 12-6 所示的步驟得到劃分結果。
|
||||
|
||||
1. 前序走訪的首元素 3 是根節點的值。
|
||||
2. 查詢根節點 3 在 `inorder` 中的索引,利用該索引可將 `inorder` 劃分為 `[ 9 | 3 | 1 2 7 ]` 。
|
||||
2. 查詢根節點 3 在 `inorder` 中的索引,利用該索引可將 `inorder` 劃分為 `[ 9 | 3 | 1 2 7 ]` 。
|
||||
3. 根據 `inorder` 的劃分結果,易得左子樹和右子樹的節點數量分別為 1 和 3 ,從而可將 `preorder` 劃分為 `[ 3 | 9 | 2 1 7 ]` 。
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -10,7 +10,7 @@ comments: true
|
||||
- 動態規劃也對問題進行遞迴分解,但與分治演算法的主要區別是,動態規劃中的子問題是相互依賴的,在分解過程中會出現許多重疊子問題。
|
||||
- 回溯演算法在嘗試和回退中窮舉所有可能的解,並透過剪枝避免不必要的搜尋分支。原問題的解由一系列決策步驟構成,我們可以將每個決策步驟之前的子序列看作一個子問題。
|
||||
|
||||
實際上,動態規劃常用來求解最最佳化問題,它們不僅包含重疊子問題,還具有另外兩大特性:最優子結構、無後效性。
|
||||
實際上,動態規劃常用來求解最佳化問題,它們不僅包含重疊子問題,還具有另外兩大特性:最優子結構、無後效性。
|
||||
|
||||
## 14.2.1 最優子結構
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ comments: true
|
||||
|
||||
在此基礎上,動態規劃問題還有一些判斷的“加分項”。
|
||||
|
||||
- 問題包含最大(小)或最多(少)等最最佳化描述。
|
||||
- 問題包含最大(小)或最多(少)等最佳化描述。
|
||||
- 問題的狀態能夠使用一個串列、多維矩陣或樹來表示,並且一個狀態與其周圍的狀態存在遞推關係。
|
||||
|
||||
相應地,也存在一些“減分項”。
|
||||
|
||||
@@ -43,7 +43,7 @@ comments: true
|
||||
|
||||
狀態 $[i, j]$ 對應的子問題:**將 $s$ 的前 $i$ 個字元更改為 $t$ 的前 $j$ 個字元所需的最少編輯步數**。
|
||||
|
||||
至此,得到一個尺寸為 $(i+1) \times (j+1)$ 的二維 $dp$ 表。
|
||||
至此,得到一個尺寸為 $(n+1) \times (m+1)$ 的二維 $dp$ 表。
|
||||
|
||||
**第二步:找出最優子結構,進而推導出狀態轉移方程**
|
||||
|
||||
|
||||
@@ -1110,7 +1110,7 @@ $$
|
||||
|
||||
### 4. 空間最佳化
|
||||
|
||||
由於每個狀態都只與其上一行的狀態有關,因此我們可以使用兩個陣列滾動前進,將空間複雜度從 $O(n^2)$ 降至 $O(n)$ 。
|
||||
由於每個狀態都只與其上一行的狀態有關,因此我們可以使用兩個陣列滾動前進,將空間複雜度從 $O(n \times cap)$ 降至 $O(cap)$ 。
|
||||
|
||||
進一步思考,我們能否僅用一個陣列實現空間最佳化呢?觀察可知,每個狀態都是由正上方或左上方的格子轉移過來的。假設只有一個陣列,當開始走訪第 $i$ 行時,該陣列儲存的仍然是第 $i-1$ 行的狀態。
|
||||
|
||||
|
||||
@@ -25,5 +25,5 @@ comments: true
|
||||
**編輯距離問題**
|
||||
|
||||
- 編輯距離(Levenshtein 距離)用於衡量兩個字串之間的相似度,其定義為從一個字串到另一個字串的最少編輯步數,編輯操作包括新增、刪除、替換。
|
||||
- 編輯距離問題的狀態定義為將 $s$ 的前 $i$ 個字元更改為 $t$ 的前 $j$ 個字元所需的最少編輯步數。當 $s[i] \ne t[j]$ 時,具有三種決策:新增、刪除、替換,它們都有相應的剩餘子問題。據此便可以找出最優子結構與構建狀態轉移方程。而當 $s[i] = t[j]$ 時,無須編輯當前字元。
|
||||
- 編輯距離問題的狀態定義為將 $s$ 的前 $i$ 個字元更改為 $t$ 的前 $j$ 個字元所需的最少編輯步數。當 $s[i-1] \ne t[j-1]$ 時,具有三種決策:新增、刪除、替換,它們都有相應的剩餘子問題。據此便可以找出最優子結構與構建狀態轉移方程。而當 $s[i-1] = t[j-1]$ 時,無須編輯當前字元。
|
||||
- 在編輯距離中,狀態依賴其正上方、正左方、左上方的狀態,因此空間最佳化後正序或倒序走訪都無法正確地進行狀態轉移。為此,我們利用一個變數暫存左上方狀態,從而轉化到與完全背包問題等價的情況,可以在空間最佳化後進行正序走訪。
|
||||
|
||||
@@ -536,7 +536,7 @@ comments: true
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=class%20Item%3A%0A%20%20%20%20%22%22%22%E7%89%A9%E5%93%81%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20w%3A%20int%2C%20v%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.w%20%3D%20w%20%20%23%20%E7%89%A9%E5%93%81%E9%87%8D%E9%87%8F%0A%20%20%20%20%20%20%20%20self.v%20%3D%20v%20%20%23%20%E7%89%A9%E5%93%81%E5%83%B9%E5%80%BC%0A%0Adef%20fractional_knapsack%28wgt%3A%20list%5Bint%5D%2C%20val%3A%20list%5Bint%5D%2C%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E5%88%86%E6%95%B8%E8%83%8C%E5%8C%85%EF%BC%9A%E8%B2%AA%E5%A9%AA%22%22%22%0A%20%20%20%20%23%20%E5%BB%BA%E7%AB%8B%E7%89%A9%E5%93%81%E4%B8%B2%E5%88%97%EF%BC%8C%E5%8C%85%E5%90%AB%E5%85%A9%E5%80%8B%E5%B1%AC%E6%80%A7%EF%BC%9A%E9%87%8D%E9%87%8F%E3%80%81%E5%83%B9%E5%80%BC%0A%20%20%20%20items%20%3D%20%5BItem%28w%2C%20v%29%20for%20w%2C%20v%20in%20zip%28wgt%2C%20val%29%5D%0A%20%20%20%20%23%20%E6%8C%89%E7%85%A7%E5%96%AE%E4%BD%8D%E5%83%B9%E5%80%BC%20item.v%20/%20item.w%20%E5%BE%9E%E9%AB%98%E5%88%B0%E4%BD%8E%E9%80%B2%E8%A1%8C%E6%8E%92%E5%BA%8F%0A%20%20%20%20items.sort%28key%3Dlambda%20item%3A%20item.v%20/%20item.w%2C%20reverse%3DTrue%29%0A%20%20%20%20%23%20%E8%BF%B4%E5%9C%88%E8%B2%AA%E5%A9%AA%E9%81%B8%E6%93%87%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20for%20item%20in%20items%3A%0A%20%20%20%20%20%20%20%20if%20item.w%20%3C%3D%20cap%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8B%A5%E5%89%A9%E9%A4%98%E5%AE%B9%E9%87%8F%E5%85%85%E8%B6%B3%EF%BC%8C%E5%89%87%E5%B0%87%E7%95%B6%E5%89%8D%E7%89%A9%E5%93%81%E6%95%B4%E5%80%8B%E8%A3%9D%E9%80%B2%E8%83%8C%E5%8C%85%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20item.v%0A%20%20%20%20%20%20%20%20%20%20%20%20cap%20-%3D%20item.w%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8B%A5%E5%89%A9%E9%A4%98%E5%AE%B9%E9%87%8F%E4%B8%8D%E8%B6%B3%EF%BC%8C%E5%89%87%E5%B0%87%E7%95%B6%E5%89%8D%E7%89%A9%E5%93%81%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%E8%A3%9D%E9%80%B2%E8%83%8C%E5%8C%85%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20%28item.v%20/%20item.w%29%20%2A%20cap%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%B7%B2%E7%84%A1%E5%89%A9%E9%A4%98%E5%AE%B9%E9%87%8F%EF%BC%8C%E5%9B%A0%E6%AD%A4%E8%B7%B3%E5%87%BA%E8%BF%B4%E5%9C%88%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10%2C%2020%2C%2030%2C%2040%2C%2050%5D%0A%20%20%20%20val%20%3D%20%5B50%2C%20120%2C%20150%2C%20210%2C%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20%E8%B2%AA%E5%A9%AA%E6%BC%94%E7%AE%97%E6%B3%95%0A%20%20%20%20res%20%3D%20fractional_knapsack%28wgt%2C%20val%2C%20cap%29%0A%20%20%20%20print%28f%22%E4%B8%8D%E8%B6%85%E9%81%8E%E8%83%8C%E5%8C%85%E5%AE%B9%E9%87%8F%E7%9A%84%E6%9C%80%E5%A4%A7%E7%89%A9%E5%93%81%E5%83%B9%E5%80%BC%E7%82%BA%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=8&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=class%20Item%3A%0A%20%20%20%20%22%22%22%E7%89%A9%E5%93%81%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20w%3A%20int%2C%20v%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.w%20%3D%20w%20%20%23%20%E7%89%A9%E5%93%81%E9%87%8D%E9%87%8F%0A%20%20%20%20%20%20%20%20self.v%20%3D%20v%20%20%23%20%E7%89%A9%E5%93%81%E5%83%B9%E5%80%BC%0A%0Adef%20fractional_knapsack%28wgt%3A%20list%5Bint%5D%2C%20val%3A%20list%5Bint%5D%2C%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E5%88%86%E6%95%B8%E8%83%8C%E5%8C%85%EF%BC%9A%E8%B2%AA%E5%A9%AA%22%22%22%0A%20%20%20%20%23%20%E5%BB%BA%E7%AB%8B%E7%89%A9%E5%93%81%E4%B8%B2%E5%88%97%EF%BC%8C%E5%8C%85%E5%90%AB%E5%85%A9%E5%80%8B%E5%B1%AC%E6%80%A7%EF%BC%9A%E9%87%8D%E9%87%8F%E3%80%81%E5%83%B9%E5%80%BC%0A%20%20%20%20items%20%3D%20%5BItem%28w%2C%20v%29%20for%20w%2C%20v%20in%20zip%28wgt%2C%20val%29%5D%0A%20%20%20%20%23%20%E6%8C%89%E7%85%A7%E5%96%AE%E4%BD%8D%E5%83%B9%E5%80%BC%20item.v%20/%20item.w%20%E5%BE%9E%E9%AB%98%E5%88%B0%E4%BD%8E%E9%80%B2%E8%A1%8C%E6%8E%92%E5%BA%8F%0A%20%20%20%20items.sort%28key%3Dlambda%20item%3A%20item.v%20/%20item.w%2C%20reverse%3DTrue%29%0A%20%20%20%20%23%20%E8%BF%B4%E5%9C%88%E8%B2%AA%E5%A9%AA%E9%81%B8%E6%93%87%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20for%20item%20in%20items%3A%0A%20%20%20%20%20%20%20%20if%20item.w%20%3C%3D%20cap%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8B%A5%E5%89%A9%E9%A4%98%E5%AE%B9%E9%87%8F%E5%85%85%E8%B6%B3%EF%BC%8C%E5%89%87%E5%B0%87%E7%95%B6%E5%89%8D%E7%89%A9%E5%93%81%E6%95%B4%E5%80%8B%E8%A3%9D%E9%80%B2%E8%83%8C%E5%8C%85%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20item.v%0A%20%20%20%20%20%20%20%20%20%20%20%20cap%20-%3D%20item.w%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%8B%A5%E5%89%A9%E9%A4%98%E5%AE%B9%E9%87%8F%E4%B8%8D%E8%B6%B3%EF%BC%8C%E5%89%87%E5%B0%87%E7%95%B6%E5%89%8D%E7%89%A9%E5%93%81%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%E8%A3%9D%E9%80%B2%E8%83%8C%E5%8C%85%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20%28item.v%20/%20item.w%29%20%2A%20cap%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%B7%B2%E7%84%A1%E5%89%A9%E9%A4%98%E5%AE%B9%E9%87%8F%EF%BC%8C%E5%9B%A0%E6%AD%A4%E8%B7%B3%E5%87%BA%E8%BF%B4%E5%9C%88%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10%2C%2020%2C%2030%2C%2040%2C%2050%5D%0A%20%20%20%20val%20%3D%20%5B50%2C%20120%2C%20150%2C%20210%2C%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20%E8%B2%AA%E5%A9%AA%E6%BC%94%E7%AE%97%E6%B3%95%0A%20%20%20%20res%20%3D%20fractional_knapsack%28wgt%2C%20val%2C%20cap%29%0A%20%20%20%20print%28f%22%E4%B8%8D%E8%B6%85%E9%81%8E%E8%83%8C%E5%8C%85%E5%AE%B9%E9%87%8F%E7%9A%84%E6%9C%80%E5%A4%A7%E7%89%A9%E5%93%81%E5%83%B9%E5%80%BC%E7%82%BA%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=8&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">全螢幕觀看 ></a></div>
|
||||
|
||||
內建排序演算法的時間複雜度通常為 $O(\log n)$ ,空間複雜度通常為 $O(\log n)$ 或 $O(n)$ ,取決於程式語言的具體實現。
|
||||
內建排序演算法的時間複雜度通常為 $O(n \log n)$ ,空間複雜度通常為 $O(\log n)$ 或 $O(n)$ ,取決於程式語言的具體實現。
|
||||
|
||||
除排序之外,在最差情況下,需要走訪整個物品串列,**因此時間複雜度為 $O(n)$** ,其中 $n$ 為物品數量。
|
||||
|
||||
|
||||
@@ -405,4 +405,4 @@ comments: true
|
||||
- **分數背包問題**:給定一組物品和一個載重量,你的目標是選擇一組物品,使得總重量不超過載重量,且總價值最大。如果每次都選擇價效比最高(價值 / 重量)的物品,那麼貪婪演算法在一些情況下可以得到最優解。
|
||||
- **股票買賣問題**:給定一組股票的歷史價格,你可以進行多次買賣,但如果你已經持有股票,那麼在賣出之前不能再買,目標是獲取最大利潤。
|
||||
- **霍夫曼編碼**:霍夫曼編碼是一種用於無損資料壓縮的貪婪演算法。透過構建霍夫曼樹,每次選擇出現頻率最低的兩個節點合併,最後得到的霍夫曼樹的帶權路徑長度(編碼長度)最小。
|
||||
- **Dijkstra 演算法**:它是一種解決給定源頂點到其餘各頂點的最短路徑問題的貪婪演算法。
|
||||
- **Dijkstra 演算法**:在所有邊的權重均為非負數的圖中,它是一種解決給定源頂點到其餘各頂點的最短路徑問題的貪婪演算法。
|
||||
|
||||
@@ -61,7 +61,7 @@ $$
|
||||
1. 輸入整數 $n$ ,從其不斷地切分出因子 $3$ ,直至餘數為 $0$、$1$、$2$ 。
|
||||
2. 當餘數為 $0$ 時,代表 $n$ 是 $3$ 的倍數,因此不做任何處理。
|
||||
3. 當餘數為 $2$ 時,不繼續劃分,保留。
|
||||
4. 當餘數為 $1$ 時,由於 $2 \times 2 > 1 \times 3$ ,因此應將最後一個 $3$ 替換為 $2$ 。
|
||||
4. 當餘數為 $1$ 時,由於 $2 \times 2 > 1 \times 3$ ,因此應將最後一個 $3$ 和餘數 $1$ 替換為兩個 $2$ 。
|
||||
|
||||
### 2. 程式碼實現
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ comments: true
|
||||
|
||||
### 1. 重點回顧
|
||||
|
||||
- 貪婪演算法通常用於解決最最佳化問題,其原理是在每個決策階段都做出區域性最優的決策,以期獲得全域性最優解。
|
||||
- 貪婪演算法通常用於解決最佳化問題,其原理是在每個決策階段都做出區域性最優的決策,以期獲得全域性最優解。
|
||||
- 貪婪演算法會迭代地做出一個又一個的貪婪選擇,每輪都將問題轉化成一個規模更小的子問題,直到問題被解決。
|
||||
- 貪婪演算法不僅實現簡單,還具有很高的解題效率。相比於動態規劃,貪婪演算法的時間複雜度通常更低。
|
||||
- 在零錢兌換問題中,對於某些硬幣組合,貪婪演算法可以保證找到最優解;對於另外一些硬幣組合則不然,貪婪演算法可能找到很差的解。
|
||||
|
||||
@@ -761,7 +761,7 @@ $$
|
||||
size_t hashStr = hash<string>()(str);
|
||||
// 字串“Hello 演算法”的雜湊值為 15466937326284535026
|
||||
|
||||
// 在 C++ 中,內建 std:hash() 僅提供基本資料型別的雜湊值計算
|
||||
// 在 C++ 中,內建 std::hash() 僅提供基本資料型別的雜湊值計算
|
||||
// 陣列、物件的雜湊值計算需要自行實現
|
||||
```
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ comments: true
|
||||
- 負載因子定義為雜湊表中元素數量除以桶數量,反映了雜湊衝突的嚴重程度,常用作觸發雜湊表擴容的條件。
|
||||
- 鏈式位址透過將單個元素轉化為鏈結串列,將所有衝突元素儲存在同一個鏈結串列中。然而,鏈結串列過長會降低查詢效率,可以透過進一步將鏈結串列轉換為紅黑樹來提高效率。
|
||||
- 開放定址透過多次探測來處理雜湊衝突。線性探查使用固定步長,缺點是不能刪除元素,且容易產生聚集。多次雜湊使用多個雜湊函式進行探測,相較線性探查更不易產生聚集,但多個雜湊函式增加了計算量。
|
||||
- 不同程式語言採取了不同的雜湊表實現。例如,Java 的 `HashMap` 使用鏈式位址,而 Python 的 `Dict` 採用開放定址。
|
||||
- 不同程式語言採取了不同的雜湊表實現。例如,Java 的 `HashMap` 使用鏈式位址,而 Python 的 `dict` 採用開放定址。
|
||||
- 在雜湊表中,我們希望雜湊演算法具有確定性、高效率和均勻分佈的特點。在密碼學中,雜湊演算法還應該具備抗碰撞性和雪崩效應。
|
||||
- 雜湊演算法通常採用大質數作為模數,以最大化地保證雜湊值均勻分佈,減少雜湊衝突。
|
||||
- 常見的雜湊演算法包括 MD5、SHA-1、SHA-2 和 SHA-3 等。MD5 常用於校驗檔案完整性,SHA-2 常用於安全應用與協議。
|
||||
|
||||
@@ -331,7 +331,7 @@ comments: true
|
||||
|
||||
下面,我們來嘗試推算第二種建堆積方法的時間複雜度。
|
||||
|
||||
- 假設完全二元樹的節點數量為 $n$ ,則葉節點數量為 $(n + 1) / 2$ ,其中 $/$ 為向下整除。因此需要堆積化的節點數量為 $(n - 1) / 2$ 。
|
||||
- 假設完全二元樹的節點數量為 $n$ ,則葉節點數量為 $(n + 1) / 2$ ,其中 $/$ 為向下整除。因此需要堆積化的節點數量為 $n / 2$ 。
|
||||
- 在從頂至底堆積化的過程中,每個節點最多堆積化到葉節點,因此最大迭代次數為二元樹高度 $\log n$ 。
|
||||
|
||||
將上述兩者相乘,可得到建堆積過程的時間複雜度為 $O(n \log n)$ 。**但這個估算結果並不準確,因為我們沒有考慮到二元樹底層節點數量遠多於頂層節點的性質**。
|
||||
|
||||
@@ -486,7 +486,7 @@ comments: true
|
||||
|
||||
### 2. 轉化為查詢元素
|
||||
|
||||
我們知道,當陣列不包含 `target` 時,最終 $i$ 和 $j$ 會分別指向首個大於、小於 `target` 的元素。
|
||||
我們知道,當陣列不包含 `target` 時,最終 $i$ 和 $j$ 會分別指向首個大於 `target` 的元素和最右一個小於 `target` 的元素。
|
||||
|
||||
因此,如圖 10-8 所示,我們可以構造一個陣列中不存在的元素,用於查詢左右邊界。
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ comments: true
|
||||
|
||||
進一步思考二分搜尋過程:當 `nums[m] < target` 時 $i$ 移動,這意味著指標 $i$ 在向大於等於 `target` 的元素靠近。同理,指標 $j$ 始終在向小於等於 `target` 的元素靠近。
|
||||
|
||||
因此二分結束時一定有:$i$ 指向首個大於 `target` 的元素,$j$ 指向首個小於 `target` 的元素。**易得當陣列不包含 `target` 時,插入索引為 $i$** 。程式碼如下所示:
|
||||
因此二分結束時一定有:$i$ 指向首個大於 `target` 的元素,$j$ 指向最右一個小於 `target` 的元素。**易得當陣列不包含 `target` 時,插入索引為 $i$** 。程式碼如下所示:
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -344,7 +344,7 @@ comments: true
|
||||
- 當 `nums[m] < target` 或 `nums[m] > target` 時,說明還沒有找到 `target` ,因此採用普通二分搜尋的縮小區間操作,**從而使指標 $i$ 和 $j$ 向 `target` 靠近**。
|
||||
- 當 `nums[m] == target` 時,說明小於 `target` 的元素在區間 $[i, m - 1]$ 中,因此採用 $j = m - 1$ 來縮小區間,**從而使指標 $j$ 向小於 `target` 的元素靠近**。
|
||||
|
||||
迴圈完成後,$i$ 指向最左邊的 `target` ,$j$ 指向首個小於 `target` 的元素,**因此索引 $i$ 就是插入點**。
|
||||
迴圈完成後,$i$ 指向最左邊的 `target` ,$j$ 指向最右一個小於 `target` 的元素,**因此索引 $i$ 就是插入點**。
|
||||
|
||||
=== "<1>"
|
||||
{ class="animation-figure" }
|
||||
@@ -389,7 +389,7 @@ comments: true
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # target 在區間 [i, m-1] 中
|
||||
else:
|
||||
j = m - 1 # 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1 # 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
# 返回插入點 i
|
||||
return i
|
||||
```
|
||||
@@ -407,7 +407,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -428,7 +428,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -449,7 +449,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -474,7 +474,7 @@ comments: true
|
||||
// target 在區間 [i, m-1] 中
|
||||
j = m - 1
|
||||
} else {
|
||||
// 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
// 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1
|
||||
}
|
||||
}
|
||||
@@ -498,7 +498,7 @@ comments: true
|
||||
} else if nums[m] > target {
|
||||
j = m - 1 // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1 // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1 // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -520,7 +520,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -542,7 +542,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -563,7 +563,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -584,7 +584,7 @@ comments: true
|
||||
} else if nums[m as usize] > target {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -605,7 +605,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1; // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1; // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1; // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -627,7 +627,7 @@ comments: true
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1 // target 在區間 [i, m-1] 中
|
||||
} else {
|
||||
j = m - 1 // 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1 // 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
}
|
||||
}
|
||||
// 返回插入點 i
|
||||
@@ -652,7 +652,7 @@ comments: true
|
||||
elsif nums[m] > target
|
||||
j = m - 1 # target 在區間 [i, m-1] 中
|
||||
else
|
||||
j = m - 1 # 首個小於 target 的元素在區間 [i, m-1] 中
|
||||
j = m - 1 # 最右一個小於 target 的元素在區間 [i, m-1] 中
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ comments: true
|
||||
|
||||
# 11.8 桶排序
|
||||
|
||||
前述幾種排序演算法都屬於“基於比較的排序演算法”,它們透過比較元素間的大小來實現排序。此類排序演算法的時間複雜度無法超越 $O(n \log n)$ 。接下來,我們將探討幾種“非比較排序演算法”,它們的時間複雜度可以達到線性階。
|
||||
前述幾種排序演算法都屬於“基於比較的排序演算法”,它們透過比較元素間的大小來實現排序。此類排序演算法在最壞情況下的時間複雜度下界為 $\Omega(n \log n)$ 。接下來,我們將探討幾種“非比較排序演算法”,它們的時間複雜度可以達到線性階。
|
||||
|
||||
<u>桶排序(bucket sort)</u>是分治策略的一個典型應用。它透過設定一些具有大小順序的桶,每個桶對應一個數據範圍,將資料平均分配到各個桶中;然後,在每個桶內部分別執行排序;最終按照桶的順序將所有資料合併。
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ comments: true
|
||||
|
||||
## 11.2.1 演算法特性
|
||||
|
||||
- **時間複雜度為 $O(n^2)$、非自適應排序**:外迴圈共 $n - 1$ 輪,第一輪的未排序區間長度為 $n$ ,最後一輪的未排序區間長度為 $2$ ,即各輪外迴圈分別包含 $n$、$n - 1$、$\dots$、$3$、$2$ 輪內迴圈,求和為 $\frac{(n - 1)(n + 2)}{2}$ 。
|
||||
- **時間複雜度為 $O(n^2)$、非自適應排序**:外迴圈共 $n - 1$ 輪,第一輪內迴圈執行 $n - 1$ 次,最後一輪執行 $1$ 次,即各輪內迴圈分別執行 $n - 1$、$n - 2$、$\dots$、$2$、$1$ 次,求和為 $\frac{n(n - 1)}{2}$ 。
|
||||
- **空間複雜度為 $O(1)$、原地排序**:指標 $i$ 和 $j$ 使用常數大小的額外空間。
|
||||
- **非穩定排序**:如圖 11-3 所示,元素 `nums[i]` 有可能被交換至與其相等的元素的右邊,導致兩者的相對順序發生改變。
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ comments: true
|
||||
|
||||
**自適應性**:<u>自適應排序</u>能夠利用輸入資料已有的順序資訊來減少計算量,達到更優的時間效率。自適應排序演算法的最佳時間複雜度通常優於平均時間複雜度。
|
||||
|
||||
**是否基於比較**:<u>基於比較的排序</u>依賴比較運算子($<$、$=$、$>$)來判斷元素的相對順序,從而排序整個陣列,理論最優時間複雜度為 $O(n \log n)$ 。而<u>非比較排序</u>不使用比較運算子,時間複雜度可達 $O(n)$ ,但其通用性相對較差。
|
||||
**是否基於比較**:<u>基於比較的排序</u>依賴比較運算子($<$、$=$、$>$)來判斷元素的相對順序,從而排序整個陣列,其最壞時間複雜度的下界為 $\Omega(n \log n)$ 。而<u>非比較排序</u>不使用比較運算子,時間複雜度可達 $O(n)$ ,但其通用性相對較差。
|
||||
|
||||
## 11.1.2 理想排序演算法
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ comments: true
|
||||
|
||||
/* 元素出列 */
|
||||
// 由於是陣列,因此 removeFirst 的複雜度為 O(n)
|
||||
let pool = queue.removeFirst()
|
||||
let pop = queue.removeFirst()
|
||||
|
||||
/* 獲取佇列的長度 */
|
||||
let size = queue.count
|
||||
@@ -254,7 +254,7 @@ comments: true
|
||||
|
||||
```dart title="queue.dart"
|
||||
/* 初始化佇列 */
|
||||
// 在 Dart 中,佇列類別 Qeque 是雙向佇列,也可作為佇列使用
|
||||
// 在 Dart 中,佇列類別 Queue 是雙向佇列,也可作為佇列使用
|
||||
Queue<int> queue = Queue();
|
||||
|
||||
/* 元素入列 */
|
||||
|
||||
@@ -213,9 +213,9 @@ AVL 樹既是二元搜尋樹,也是平衡二元樹,同時滿足這兩類二
|
||||
```kotlin title=""
|
||||
/* AVL 樹節點類別 */
|
||||
class TreeNode(val _val: Int) { // 節點值
|
||||
val height: Int = 0 // 節點高度
|
||||
val left: TreeNode? = null // 左子節點
|
||||
val right: TreeNode? = null // 右子節點
|
||||
var height: Int = 0 // 節點高度
|
||||
var left: TreeNode? = null // 左子節點
|
||||
var right: TreeNode? = null // 右子節點
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -185,8 +185,8 @@ comments: true
|
||||
```kotlin title=""
|
||||
/* 二元樹節點類別 */
|
||||
class TreeNode(val _val: Int) { // 節點值
|
||||
val left: TreeNode? = null // 左子節點引用
|
||||
val right: TreeNode? = null // 右子節點引用
|
||||
var left: TreeNode? = null // 左子節點引用
|
||||
var right: TreeNode? = null // 右子節點引用
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user