基于Python實現(xiàn)的單詞統(tǒng)計與查找分析工具
一、引言
單詞統(tǒng)計與查找分析工具是一款面向 “單詞統(tǒng)計 + 查找算法對比” 的可視化工具,核心目標是實現(xiàn)文本中單詞的標準化解析、多策略(順序查找 / 快排 + 二分 / 哈希表)統(tǒng)計,并計算關(guān)鍵指標(Total Words、Unique Words、ASL 平均查找長度)。本文將詳細講解該工具的開發(fā)過程以及Python代碼完整實現(xiàn)(原始版 + 擴展版)。
二、第一階段:需求分析與功能規(guī)劃
開發(fā)前先明確工具的核心目標與邊界,確保功能貼合 “數(shù)據(jù)結(jié)構(gòu)與算法實踐” 的場景需求:
1. 核心業(yè)務(wù)需求
- 文本解析與單詞標準化:提取文本中的純字母單詞(跳過數(shù)字 / 符號),統(tǒng)一轉(zhuǎn)為小寫,支持
#作為文本結(jié)束符; - 多查找策略支持:
- 策略 1:基于線性表的順序查找,統(tǒng)計單詞頻次;
- 策略 2:線性表快速排序后執(zhí)行二分查找,統(tǒng)計單詞頻次;
- 策略 3:基于 Time33 哈希函數(shù)的哈希表(鏈地址法)查找,統(tǒng)計單詞頻次;
- 關(guān)鍵指標計算:
- Total Words:解析出的單詞總數(shù)(含重復);
- Unique Words:唯一單詞數(shù);
- ASL(Average Search Length):平均查找長度(算法效率核心指標);
- 可視化交互:GUI 界面操作(無需命令行),實時反饋輸入狀態(tài),清晰展示統(tǒng)計結(jié)果。
2. 非功能需求
- 易用性:實時提示輸入字符數(shù)、
#結(jié)束符檢測;策略切換即時反饋,結(jié)果格式化展示; - 魯棒性:處理空文本、超長單詞、無字母文本等邊界場景,避免崩潰;
- 可對比性:三種策略的核心邏輯解耦,便于對比不同查找算法的效率(ASL);
- 性能適配:哈希表大小固定為 1009(質(zhì)數(shù),減少哈希沖突),線性表最大容量限制為 10000(適配常規(guī)文本解析場景)。
三、第二階段:技術(shù)選型
結(jié)合 “算法實踐 + 可視化交互” 的核心需求,選擇輕量、易理解的技術(shù)棧:
| 技術(shù) / 模塊 | 選型理由 |
|---|---|
| Python 3.8+ | 語法簡潔,數(shù)據(jù)結(jié)構(gòu)(列表、字典)原生支持,算法實現(xiàn)直觀,適合教學 / 實踐場景 |
| tkinter + ttk | Python 內(nèi)置 GUI 庫,無需額外安裝,足夠支撐輕量交互界面;ttk 提升控件美觀度 |
| 原生數(shù)據(jù)結(jié)構(gòu)(列表) | 模擬線性表、哈希表(鏈地址法),無需第三方庫,降低學習 / 使用成本 |
| Time33 哈希函數(shù) | 簡單高效的字符串哈希算法(h = h*33 + ord(char)),分布均勻,沖突率低 |
| 快速排序 + 二分查找 | 經(jīng)典排序 / 查找算法組合,對比順序查找體現(xiàn) “有序表 + 二分” 的效率優(yōu)勢 |
四、第三階段:架構(gòu)設(shè)計
采用 “模塊化 + 面向?qū)ο?rdquo; 混合設(shè)計,核心分為三層,確保邏輯解耦、便于維護:
classDiagram
direction LR
"全局常量層" --> "核心算法層" : 依賴常量配置
"核心算法層" --> "GUI交互層" : 提供算法調(diào)用
"GUI交互層" --> "核心算法層" : 傳遞業(yè)務(wù)數(shù)據(jù)
class 全局常量層 {
MAX_LINEAR_SIZE: int = 10000
HASH_SIZE: int = 1009
MAX_WORD_LEN: int = 50
}
class 核心算法層 {
+normalize(token, pos): (bool, str) # 單詞標準化
+my_hash(word): int # Time33哈希
+linear_search(linear_list, word): (bool, int) # 順序查找
+quick_sort(linear_list, low, high) # 快速排序
+binary_search(linear_list, word): (bool, int) # 二分查找
}
class GUI交互層 {
-root: tk.Tk
-strategy_var: tk.StringVar
+_setup_ui() # 搭建UI
+_on_text_changed(event) # 文本輸入監(jiān)控
+_on_strategy_toggled() # 策略切換
+_on_analyze_clicked() # 分析按鈕邏輯
+_update_result(text, color) # 更新結(jié)果展示
}
HTML代碼:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>單詞統(tǒng)計工具 - 架構(gòu)類圖</title>
<!-- 引入Mermaid CDN -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.16.0/dist/mermaid.min.js"></script>
<style>
body {
font-family: "Microsoft YaHei", Arial, sans-serif;
padding: 20px;
background: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
/* 確保Mermaid代碼保留換行格式 */
.mermaid {
white-space: pre;
font-family: monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>單詞統(tǒng)計與查找分析工具 - 架構(gòu)類圖</h1>
<!-- Mermaid類圖代碼(直接放在mermaid容器中,保留原始格式) -->
<div class="mermaid">
classDiagram
direction LR
%% 層間關(guān)聯(lián)
全局常量層 --> 核心算法層 : 依賴常量配置
核心算法層 --> GUI交互層 : 提供算法調(diào)用
GUI交互層 --> 核心算法層 : 傳遞業(yè)務(wù)數(shù)據(jù)
%% 全局常量層
class 全局常量層 {
MAX_LINEAR_SIZE: int = 10000
HASH_SIZE: int = 1009
MAX_WORD_LEN: int = 50
}
%% 核心算法層
class 核心算法層 {
+normalize(token, pos): (bool, str) // 單詞標準化
+my_hash(word): int // Time33哈希
+linear_search(linear_list, word): (bool, int) // 順序查找
+quick_sort(linear_list, low, high) // 快速排序
+binary_search(linear_list, word): (bool, int) // 二分查找
}
%% GUI交互層
class GUI交互層 {
-root: tk.Tk
-strategy_var: tk.StringVar
+_setup_ui() // 搭建UI
+_on_text_changed(event) // 文本輸入監(jiān)控
+_on_strategy_toggled() // 策略切換
+_on_analyze_clicked() // 分析按鈕邏輯
+_update_result(text, color) // 更新結(jié)果展示
}
</div>
</div>
<script>
// 初始化Mermaid,啟用自動加載
mermaid.initialize({
startOnLoad: true,
theme: 'default',
classDiagram: {
fontSize: 14,
nodeSpacing: 30,
rankSpacing: 40
}
});
</script>
</body>
</html>

核心設(shè)計思路:
- 全局常量層:集中管理配置參數(shù)(如哈希表大小、線性表最大容量),便于統(tǒng)一修改;
- 核心算法層:純函數(shù)式設(shè)計,與 GUI 解耦,可獨立測試 / 復用(比如單獨調(diào)用算法函數(shù)驗證邏輯);
- GUI 交互層:面向?qū)ο蠓庋b,負責界面渲染、事件處理、業(yè)務(wù)邏輯串聯(lián)(調(diào)用算法 + 展示結(jié)果)。
五、第四階段:分模塊詳細開發(fā)
1. 全局常量層開發(fā)
定義工具的核心配置參數(shù),取值需兼顧 “場景適配 + 算法特性”:
| 常量名 | 取值 | 設(shè)計理由 |
|---|---|---|
| MAX_LINEAR_SIZE | 10000 | 限制線性表最大容量,避免極端文本導致內(nèi)存溢出,適配常規(guī)文本解析場景 |
| HASH_SIZE | 1009 | 選擇質(zhì)數(shù)作為哈希表大小(質(zhì)數(shù)能減少哈希沖突,1009 是接近 1000 的質(zhì)數(shù),平衡性能與內(nèi)存) |
| MAX_WORD_LEN | 50 | 限制單詞最大長度,過濾異常超長字符串(如無分隔符的字母串) |
2. 核心算法層開發(fā)
這是工具的 “算法核心”,所有函數(shù)均為純邏輯實現(xiàn),無 GUI 依賴,可獨立調(diào)用測試。
(1)單詞標準化函數(shù)(normalize)
解決 “文本中提取有效單詞” 的問題,核心需求:跳過非字母字符、提取連續(xù)字母、統(tǒng)一轉(zhuǎn)小寫。
def normalize(token: str, pos: list) -> tuple[bool, str]:
# 跳過非字母字符(如數(shù)字、符號、空格)
while pos[0] < len(token) and not token[pos[0]].isalpha():
pos[0] += 1
if pos[0] >= len(token):
return False, ""
# 提取連續(xù)字母并轉(zhuǎn)小寫(保證單詞格式統(tǒng)一)
word = []
while pos[0] < len(token) and token[pos[0]].isalpha():
word.append(token[pos[0]].lower())
pos[0] += 1
return True, ''.join(word)
關(guān)鍵設(shè)計點:
pos參數(shù)用列表傳引用:Python 中整數(shù)是不可變類型,用列表包裹可實現(xiàn) “跨循環(huán)修改位置”(避免用全局變量);- 分步處理:先跳過無效字符,再提取有效字母,確保只保留純字母單詞;
- 統(tǒng)一轉(zhuǎn)小寫:避免 “Apple” 和 “apple” 被識別為不同單詞。
(2)哈希函數(shù)(my_hash)
選擇經(jīng)典的 Time33 算法,兼顧效率與哈希分布均勻性:
def my_hash(word: str) -> int:
h = 0
for char in word:
h = (h << 5) + h + ord(char) # 等價于h = h*33 + ord(char)(左移5位=乘32,加h=乘33)
return h % HASH_SIZE # 映射到哈希表的桶索引
設(shè)計理由:
- Time33 是工業(yè)界常用的輕量哈希算法(如 URL 哈希),實現(xiàn)簡單且沖突率低;
- 最終取模
HASH_SIZE:將哈希值映射到 [0,1008] 的范圍,適配哈希表的桶數(shù)量。
(3)順序查找函數(shù)(linear_search)
實現(xiàn)線性表的順序查找,返回 “是否找到 + 比較次數(shù)”(為 ASL 計算提供數(shù)據(jù)):
def linear_search(linear_list: list, word: str) -> tuple[bool, int]:
cmp_count = 0
for item in linear_list:
cmp_count += 1 # 每比較1次,計數(shù)+1
if item["word"] == word:
return True, cmp_count
return False, cmp_count
核心邏輯:遍歷線性表,逐個比較單詞,統(tǒng)計比較次數(shù)(ASL 的核心計算依據(jù))。
(4)快速排序相關(guān)函數(shù)
為二分查找做準備(二分查找要求線性表有序),實現(xiàn) “按單詞字典序排序”:
def swap_linear_node(linear_list: list, i: int, j: int):
"""交換線性表元素,封裝成函數(shù)便于復用"""
linear_list[i], linear_list[j] = linear_list[j], linear_list[i]
def partition(linear_list: list, low: int, high: int) -> int:
"""快排分區(qū):以最后一個元素為基準,劃分小于/大于基準的區(qū)域"""
pivot = linear_list[high]["word"] # 基準值(單詞字典序)
i = low - 1 # 小于基準的區(qū)域邊界
for j in range(low, high):
if linear_list[j]["word"] < pivot:
i += 1
swap_linear_node(linear_list, i, j)
swap_linear_node(linear_list, i + 1, high) # 基準值歸位
return i + 1
def quick_sort(linear_list: list, low: int, high: int):
"""快排主函數(shù):遞歸劃分區(qū)域并排序"""
if low < high:
pi = partition(linear_list, low, high)
quick_sort(linear_list, low, pi - 1) # 排序左半?yún)^(qū)
quick_sort(linear_list, pi + 1, high) # 排序右半?yún)^(qū)
設(shè)計點:
- 排序依據(jù)是
word字段的字典序:確保二分查找的有序性; - 封裝
swap_linear_node:避免重復寫交換邏輯,提升代碼可讀性。
(5)二分查找函數(shù)(binary_search)
基于有序線性表實現(xiàn)二分查找,統(tǒng)計比較次數(shù):
def binary_search(linear_list: list, word: str) -> tuple[bool, int]:
cmp_count = 0
low, high = 0, len(linear_list) - 1
while low <= high:
cmp_count += 1 # 每輪比較計數(shù)+1
mid = (low + high) // 2
mid_word = linear_list[mid]["word"]
if mid_word == word:
return True, cmp_count
elif mid_word > word:
high = mid - 1 # 目標在左半?yún)^(qū)
else:
low = mid + 1 # 目標在右半?yún)^(qū)
return False, cmp_count
核心邏輯:通過 “折半” 縮小查找范圍,對比順序查找可顯著減少比較次數(shù)(體現(xiàn)算法優(yōu)勢)。
3. GUI 交互層開發(fā)(WordAnalyzerApp 類)
封裝所有界面交互邏輯,分為 “UI 搭建”“事件綁定”“核心業(yè)務(wù)邏輯” 三部分。
(1)初始化與 UI 搭建(init+ _setup_ui)
def __init__(self, root):
self.root = root
self.root.title("單詞統(tǒng)計與查找分析工具")
self.root.geometry("800x600")
self.strategy_var = tk.StringVar(value="1") # 默認策略1
self._setup_ui()
def _setup_ui(self):
# 主容器(統(tǒng)一內(nèi)邊距,提升美觀度)
main_frame = ttk.Frame(self.root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 1. 文本輸入?yún)^(qū):帶實時提示
input_label = ttk.Label(main_frame, text="請輸入待分析文本(輸入#結(jié)束):")
input_label.pack(anchor=tk.W)
self.text_input = tk.Text(main_frame, height=10, wrap=tk.WORD)
self.text_input.pack(fill=tk.X, pady=(0, 5))
self.input_tip_label = ttk.Label(main_frame, text="", foreground="#666666")
self.input_tip_label.pack(anchor=tk.W)
self.text_input.bind("<<Modified>>", self._on_text_changed) # 綁定實時監(jiān)控事件
self.text_input_modified = False # 防止事件重復觸發(fā)
# 2. 策略選擇區(qū):單選按鈕組
strategy_frame = ttk.LabelFrame(main_frame, text="查找策略選擇", padding="10")
strategy_frame.pack(fill=tk.X, pady=(10, 5))
ttk.Radiobutton(strategy_frame, text="策略1:順序查找", variable=self.strategy_var, value="1", command=self._on_strategy_toggled).pack(side=tk.LEFT, padx=10)
ttk.Radiobutton(strategy_frame, text="策略2:快速排序+二分查找", variable=self.strategy_var, value="2", command=self._on_strategy_toggled).pack(side=tk.LEFT, padx=10)
ttk.Radiobutton(strategy_frame, text="策略3:哈希表查找", variable=self.strategy_var, value="3", command=self._on_strategy_toggled).pack(side=tk.LEFT, padx=10)
# 3. 分析按鈕
analyze_btn = ttk.Button(main_frame, text="開始分析", command=self._on_analyze_clicked)
analyze_btn.pack(pady=(10, 10))
# 4. 結(jié)果顯示區(qū):禁用手動編輯,僅展示結(jié)果
result_label = ttk.Label(main_frame, text="分析結(jié)果:")
result_label.pack(anchor=tk.W)
self.result_display = tk.Text(main_frame, height=10, wrap=tk.WORD, state=tk.DISABLED)
self.result_display.pack(fill=tk.BOTH, expand=True)
UI 設(shè)計思路:
- 按 “輸入→選擇→操作→輸出” 的流程布局,符合用戶操作習慣;
- 文本輸入?yún)^(qū)啟用
wrap=tk.WORD:按單詞換行,提升長文本可讀性; - 結(jié)果區(qū)設(shè)置為
tk.DISABLED:防止用戶誤編輯結(jié)果,僅通過代碼更新。
(2)事件綁定與反饋
① 文本輸入實時監(jiān)控(_on_text_changed)
解決 “用戶輸入過程中無反饋” 的問題,實時提示字符數(shù)和#檢測狀態(tài):
def _on_text_changed(self, event):
if self.text_input_modified:
return # 防止重復觸發(fā)
self.text_input_modified = True
# 獲取輸入文本,統(tǒng)計字符數(shù),檢測#
input_text = self.text_input.get("1.0", tk.END).strip()
char_count = len(input_text)
has_sharp = "#" in input_text
# 生成提示文本,區(qū)分顏色(有#時橙色提示)
tip = f"實時狀態(tài):已輸入 {char_count} 個字符 | "
if has_sharp:
tip += "檢測到#,分析時會截斷#后的內(nèi)容"
self.input_tip_label.config(foreground="#E67E22")
else:
tip += "未檢測到#,將分析全部文本"
self.input_tip_label.config(foreground="#666666")
self.input_tip_label.config(text=tip)
# 重置modified標志,避免循環(huán)觸發(fā)
self.text_input.edit_modified(False)
self.text_input_modified = False
關(guān)鍵優(yōu)化:
- 用
self.text_input_modified標志防止<<Modified>>事件重復觸發(fā)(tkinter 的該事件會多次觸發(fā)); - 顏色區(qū)分提示:橙色提示
#截斷規(guī)則,提升用戶感知。
② 策略切換反饋(_on_strategy_toggled)
def _on_strategy_toggled(self):
strategy_id = self.strategy_var.get()
strategy_names = {"1": "順序查找", "2": "快速排序+二分查找", "3": "哈希表查找"}
strategy_name = strategy_names.get(strategy_id, "未知")
self._update_result(f"已切換為【{strategy_name}】策略,請點擊「開始分析」重新計算結(jié)果", "#3498DB")
設(shè)計點:策略切換后即時清空結(jié)果區(qū)并提示,引導用戶重新分析,避免結(jié)果與策略不匹配。
③ 結(jié)果更新封裝(_update_result)
def _update_result(self, text: str, color: str = "#000000"):
self.result_display.config(state=tk.NORMAL) # 臨時啟用編輯
self.result_display.delete("1.0", tk.END) # 清空原有內(nèi)容
self.result_display.insert(tk.END, text) # 插入新結(jié)果
self.result_display.config(foreground=color, state=tk.DISABLED) # 禁用編輯,設(shè)置顏色
封裝理由:結(jié)果更新邏輯復用(策略切換、分析完成都需調(diào)用),減少重復代碼。
(3)核心業(yè)務(wù)邏輯(_on_analyze_clicked)
這是 GUI 與算法層的 “橋梁”,實現(xiàn) “文本解析→策略執(zhí)行→指標計算→結(jié)果展示” 的全流程:
def _on_analyze_clicked(self):
# 1. 輸入預(yù)處理:獲取文本,截斷#后的內(nèi)容
input_text = self.text_input.get("1.0", tk.END).strip()
strategy = int(self.strategy_var.get())
if "#" in input_text:
input_text = input_text.split("#")[0]
# 2. 初始化數(shù)據(jù)結(jié)構(gòu)
linear_list = [] # 策略1/2用
hash_table = [[] for _ in range(HASH_SIZE)] # 策略3用(鏈地址法)
total_words = 0
unique_words = 0
total_cmp = 0
# 3. 解析并統(tǒng)計單詞(調(diào)用normalize函數(shù))
pos = [0]
token = input_text
while True:
has_word, word = normalize(token, pos)
if not has_word:
break
total_words += 1
# 策略1/2:線性表統(tǒng)計
if strategy in (1, 2):
found, _ = linear_search(linear_list, word)
if found:
for item in linear_list:
if item["word"] == word:
item["count"] += 1
break
else:
linear_list.append({"word": word, "count": 1})
# 策略3:哈希表統(tǒng)計(鏈地址法解決沖突)
elif strategy == 3:
h = my_hash(word)
found = False
for item in hash_table[h]:
if item["word"] == word:
item["count"] += 1
found = True
break
if not found:
hash_table[h].append({"word": word, "count": 1})
# 4. 計算Unique Words和ASL(核心指標)
if strategy == 1:
# 順序查找:遍歷線性表,累加比較次數(shù)
unique_words = len(linear_list)
for item in linear_list:
_, cmp = linear_search(linear_list, item["word"])
total_cmp += cmp
asl = total_cmp / unique_words if unique_words > 0 else 0.0
elif strategy == 2:
# 快排+二分:先排序,再二分查找統(tǒng)計比較次數(shù)
unique_words = len(linear_list)
if unique_words > 0:
quick_sort(linear_list, 0, unique_words - 1)
for item in linear_list:
_, cmp = binary_search(linear_list, item["word"])
total_cmp += cmp
asl = total_cmp / unique_words if unique_words > 0 else 0.0
elif strategy == 3:
# 哈希表:統(tǒng)計桶中元素數(shù)(Unique Words),累加鏈表位置作為比較次數(shù)
unique_words = 0
for bucket in hash_table:
unique_words += len(bucket)
for bucket in hash_table:
for idx, item in enumerate(bucket):
total_cmp += (idx + 1) # 鏈表位置從1開始
asl = total_cmp / unique_words if unique_words > 0 else 0.0
# 5. 格式化展示結(jié)果
result = (
f"Total Words: {total_words}\n"
f"Unique Words: {unique_words}\n"
f"ASL: {asl:.2f}\n\n"
"測試自動換行:這是一段很長的文本,用于驗證結(jié)果區(qū)的自動換行功能是否生效,當文本長度超過控件寬度時,會自動換行顯示,不需要手動添加換行符。"
)
self._update_result(result)
核心設(shè)計點:
- 數(shù)據(jù)結(jié)構(gòu)適配:策略 1/2 復用線性表,策略 3 用 “列表嵌套列表” 模擬哈希表的鏈地址法(解決哈希沖突);
- ASL 計算邏輯:
- 順序查找:每個單詞的比較次數(shù) = 其在列表中的位置;
- 二分查找:基于有序表的折半比較次數(shù);
- 哈希表:比較次數(shù) = 單詞在哈希桶鏈表中的位置(鏈地址法的查找次數(shù));
- 邊界處理:
unique_words=0時 ASL 設(shè)為 0,避免除以 0 錯誤。
六、第五階段:測試與優(yōu)化
開發(fā)完成后,覆蓋多場景測試并優(yōu)化體驗,確保工具穩(wěn)定可用:
1. 核心測試場景
| 測試場景 | 測試目的 | 預(yù)期結(jié)果 |
|---|---|---|
| 輸入空文本 | 驗證邊界處理,避免崩潰 | Total Words=0,Unique Words=0,ASL=0.00 |
| 輸入帶 #的文本(如 “Hi#Hello”) | 驗證 #截斷邏輯 | 僅解析 “Hi”,Total Words=1 |
| 輸入含非字母的文本(如 “Hi! 123 Apple”) | 驗證 normalize 函數(shù)的標準化邏輯 | 僅提取 “hi”“apple”,統(tǒng)一轉(zhuǎn)小寫 |
| 三種策略對比(相同文本) | 驗證算法效率差異 | 策略 1 ASL 最大,策略 3 ASL 最?。üP首罡撸?/td> |
| 超長單詞(50 + 字母) | 驗證 MAX_WORD_LEN 的限制(代碼中隱含處理) | 正常提取,無內(nèi)存溢出 |
2. 關(guān)鍵優(yōu)化點
- 防止事件重復觸發(fā):通過
self.text_input_modified標志解決<<Modified>>事件多次觸發(fā)的問題; - 實時反饋優(yōu)化:輸入?yún)^(qū)提示字符數(shù)和 #檢測狀態(tài),策略切換即時提示;
- 結(jié)果格式化:ASL 保留 2 位小數(shù),結(jié)果區(qū)啟用自動換行,提升可讀性;
- 異常防護:處理
unique_words=0的情況,避免除以 0 錯誤; - 代碼解耦:算法函數(shù)與 GUI 完全分離,可單獨測試算法邏輯(如直接調(diào)用
linear_search驗證查找次數(shù))。
七、單詞統(tǒng)計與查找分析工具的Python代碼完整實現(xiàn)
import tkinter as tk
from tkinter import ttk, messagebox
import re
import sys
# ===================== 全局常量 =====================
MAX_LINEAR_SIZE = 10000 # 線性表最大容量
HASH_SIZE = 1009 # 哈希表大?。◤娭?009)
MAX_WORD_LEN = 50 # 單詞最大長度
# ===================== 核心算法函數(shù) =====================
def normalize(token: str, pos: list) -> tuple[bool, str]:
"""
標準化單詞:跳過非字母字符,提取連續(xù)字母并轉(zhuǎn)小寫
:param token: 待處理字符串
:param pos: 當前處理位置(用列表傳引用,Python整數(shù)不可變)
:return: (是否提取到單詞, 標準化后的單詞)
"""
# 跳過非字母字符
while pos[0] < len(token) and not token[pos[0]].isalpha():
pos[0] += 1
if pos[0] >= len(token):
return False, ""
# 提取連續(xù)字母并轉(zhuǎn)小寫
word = []
while pos[0] < len(token) and token[pos[0]].isalpha():
word.append(token[pos[0]].lower())
pos[0] += 1
return True, ''.join(word)
def my_hash(word: str) -> int:
"""Time33哈希函數(shù)"""
h = 0
for char in word:
h = (h << 5) + h + ord(char) # h = h*33 + 字符ASCII
return h % HASH_SIZE
def linear_search(linear_list: list, word: str) -> tuple[bool, int]:
"""
順序查找
:return: (是否找到, 比較次數(shù))
"""
cmp_count = 0
for item in linear_list:
cmp_count += 1
if item["word"] == word:
return True, cmp_count
return False, cmp_count
# 快速排序相關(guān)函數(shù)(按單詞字典序)
def swap_linear_node(linear_list: list, i: int, j: int):
"""交換線性表中的兩個元素"""
linear_list[i], linear_list[j] = linear_list[j], linear_list[i]
def partition(linear_list: list, low: int, high: int) -> int:
"""快排分區(qū)函數(shù)"""
pivot = linear_list[high]["word"]
i = low - 1 # 小于基準的區(qū)域邊界
for j in range(low, high):
if linear_list[j]["word"] < pivot:
i += 1
swap_linear_node(linear_list, i, j)
swap_linear_node(linear_list, i + 1, high)
return i + 1
def quick_sort(linear_list: list, low: int, high: int):
"""快速排序主函數(shù)"""
if low < high:
pi = partition(linear_list, low, high)
quick_sort(linear_list, low, pi - 1)
quick_sort(linear_list, pi + 1, high)
def binary_search(linear_list: list, word: str) -> tuple[bool, int]:
"""
二分查找(需先排序)
:return: (是否找到, 比較次數(shù))
"""
cmp_count = 0
low, high = 0, len(linear_list) - 1
while low <= high:
cmp_count += 1
mid = (low + high) // 2
mid_word = linear_list[mid]["word"]
if mid_word == word:
return True, cmp_count
elif mid_word > word:
high = mid - 1
else:
low = mid + 1
return False, cmp_count
# ===================== GUI主類 =====================
class WordAnalyzerApp:
def __init__(self, root):
self.root = root
self.root.title("單詞統(tǒng)計與查找分析工具")
self.root.geometry("800x600")
# 策略選擇變量
self.strategy_var = tk.StringVar(value="1")
# 初始化UI
self._setup_ui()
def _setup_ui(self):
"""構(gòu)建GUI界面"""
# 主容器
main_frame = ttk.Frame(self.root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 1. 文本輸入?yún)^(qū)域
input_label = ttk.Label(main_frame, text="請輸入待分析文本(輸入#結(jié)束):")
input_label.pack(anchor=tk.W)
self.text_input = tk.Text(main_frame, height=10, wrap=tk.WORD)
self.text_input.pack(fill=tk.X, pady=(0, 5))
# 實時提示標簽
self.input_tip_label = ttk.Label(main_frame, text="", foreground="#666666")
self.input_tip_label.pack(anchor=tk.W)
# 綁定文本實時監(jiān)控事件
self.text_input.bind("<<Modified>>", self._on_text_changed)
self.text_input_modified = False # 防止重復觸發(fā)
# 2. 策略選擇區(qū)域
strategy_frame = ttk.LabelFrame(main_frame, text="查找策略選擇", padding="10")
strategy_frame.pack(fill=tk.X, pady=(10, 5))
# 策略單選按鈕
ttk.Radiobutton(
strategy_frame, text="策略1:順序查找",
variable=self.strategy_var, value="1",
command=self._on_strategy_toggled
).pack(side=tk.LEFT, padx=10)
ttk.Radiobutton(
strategy_frame, text="策略2:快速排序+二分查找",
variable=self.strategy_var, value="2",
command=self._on_strategy_toggled
).pack(side=tk.LEFT, padx=10)
ttk.Radiobutton(
strategy_frame, text="策略3:哈希表查找",
variable=self.strategy_var, value="3",
command=self._on_strategy_toggled
).pack(side=tk.LEFT, padx=10)
# 3. 分析按鈕
analyze_btn = ttk.Button(
main_frame, text="開始分析",
command=self._on_analyze_clicked
)
analyze_btn.pack(pady=(10, 10))
# 4. 結(jié)果顯示區(qū)域
result_label = ttk.Label(main_frame, text="分析結(jié)果:")
result_label.pack(anchor=tk.W)
self.result_display = tk.Text(main_frame, height=10, wrap=tk.WORD, state=tk.DISABLED)
self.result_display.pack(fill=tk.BOTH, expand=True)
def _on_text_changed(self, event):
"""文本輸入實時監(jiān)控"""
if self.text_input_modified:
return
self.text_input_modified = True
# 獲取輸入文本
input_text = self.text_input.get("1.0", tk.END).strip()
char_count = len(input_text)
has_sharp = "#" in input_text
# 更新提示文本
tip = f"實時狀態(tài):已輸入 {char_count} 個字符 | "
if has_sharp:
tip += "檢測到#,分析時會截斷#后的內(nèi)容"
self.input_tip_label.config(foreground="#E67E22")
else:
tip += "未檢測到#,將分析全部文本"
self.input_tip_label.config(foreground="#666666")
self.input_tip_label.config(text=tip)
# 重置modified標志
self.text_input.edit_modified(False)
self.text_input_modified = False
def _on_strategy_toggled(self):
"""策略切換反饋"""
strategy_id = self.strategy_var.get()
strategy_names = {
"1": "順序查找",
"2": "快速排序+二分查找",
"3": "哈希表查找"
}
strategy_name = strategy_names.get(strategy_id, "未知")
# 清空結(jié)果區(qū)并提示
self._update_result(f"已切換為【{strategy_name}】策略,請點擊「開始分析」重新計算結(jié)果", "#3498DB")
def _update_result(self, text: str, color: str = "#000000"):
"""更新結(jié)果顯示區(qū)"""
self.result_display.config(state=tk.NORMAL)
self.result_display.delete("1.0", tk.END)
self.result_display.insert(tk.END, text)
self.result_display.config(foreground=color, state=tk.DISABLED)
def _on_analyze_clicked(self):
"""分析按鈕點擊事件"""
# 1. 獲取輸入和策略
input_text = self.text_input.get("1.0", tk.END).strip()
strategy = int(self.strategy_var.get())
# 處理#結(jié)束符
if "#" in input_text:
input_text = input_text.split("#")[0]
# 2. 初始化數(shù)據(jù)結(jié)構(gòu)
linear_list = [] # 策略1/2:存儲{"word": xxx, "count": xxx}
hash_table = [[] for _ in range(HASH_SIZE)] # 策略3:哈希表(列表模擬鏈表)
total_words = 0
unique_words = 0
total_cmp = 0
# 3. 解析并統(tǒng)計單詞
pos = [0] # 用列表傳引用
token = input_text
while True:
has_word, word = normalize(token, pos)
if not has_word:
break
total_words += 1
# 策略1/2:線性表處理
if strategy in (1, 2):
found, _ = linear_search(linear_list, word)
if found:
# 單詞已存在,計數(shù)+1
for item in linear_list:
if item["word"] == word:
item["count"] += 1
break
else:
# 新增單詞
linear_list.append({"word": word, "count": 1})
# 策略3:哈希表處理
elif strategy == 3:
h = my_hash(word)
found = False
# 遍歷哈希桶中的單詞
for item in hash_table[h]:
if item["word"] == word:
item["count"] += 1
found = True
break
if not found:
hash_table[h].append({"word": word, "count": 1})
# 4. 計算Unique Words和ASL
if strategy == 1:
# 順序查找策略
unique_words = len(linear_list)
for item in linear_list:
_, cmp = linear_search(linear_list, item["word"])
total_cmp += cmp
asl = total_cmp / unique_words if unique_words > 0 else 0.0
elif strategy == 2:
# 快排+二分查找策略
unique_words = len(linear_list)
if unique_words > 0:
quick_sort(linear_list, 0, unique_words - 1)
for item in linear_list:
_, cmp = binary_search(linear_list, item["word"])
total_cmp += cmp
asl = total_cmp / unique_words if unique_words > 0 else 0.0
elif strategy == 3:
# 哈希表策略
# 統(tǒng)計唯一單詞數(shù)
unique_words = 0
for bucket in hash_table:
unique_words += len(bucket)
# 統(tǒng)計總比較次數(shù)
for bucket in hash_table:
for idx, item in enumerate(bucket):
total_cmp += (idx + 1) # 鏈表位置從1開始
asl = total_cmp / unique_words if unique_words > 0 else 0.0
# 5. 顯示結(jié)果
result = (
f"Total Words: {total_words}\n"
f"Unique Words: {unique_words}\n"
f"ASL: {asl:.2f}\n\n"
"測試自動換行:這是一段很長的文本,用于驗證結(jié)果區(qū)的自動換行功能是否生效,當文本長度超過控件寬度時,會自動換行顯示,不需要手動添加換行符。"
)
self._update_result(result)
# ===================== 主函數(shù) =====================
if __name__ == "__main__":
root = tk.Tk()
app = WordAnalyzerApp(root)
root.mainloop()
八、程序運行截圖展示








九、打包項目
步驟 1:創(chuàng)建并激活純凈虛擬環(huán)境
打開 Anaconda Prompt,執(zhí)行以下命令(復制粘貼即可):
# 1. 創(chuàng)建僅包含Python 3.10的純凈環(huán)境(兼容性最佳,適配tkinter/matplotlib/pandas) conda create -n word_analyzer_tool python=3.10 -y # 2. 激活該環(huán)境 conda activate word_analyzer_tool # 3. 僅安裝必需依賴(清華源加速,包含matplotlib可視化依賴) pip install pandas matplotlib openpyxl pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple/
步驟 2:切換目錄并快速打包
# 切換到你的單詞統(tǒng)計工具代碼所在文件夾(替換為實際路徑) cd C:\Users\你的用戶名\PycharmProjects\WordAnalyzerTool # 用目錄模式打包(-D),速度快、體積小、穩(wěn)定性高;-w隱藏控制臺(GUI工具必備);--collect-data 確保matplotlib資源被打包 pyinstaller -w -n 單詞統(tǒng)計與查找分析工具 -D --collect-data matplotlib 單詞統(tǒng)計與查找分析工具.py
額外參數(shù)說明:--collect-data matplotlib 是為了確保 matplotlib 的字體、配置等資源被正確打包,避免可視化功能運行報錯。
步驟 3:驗證結(jié)果
- 打包時間:僅需 2-3 分鐘(因包含 matplotlib,略長于基礎(chǔ)工具);
- exe 位置:
dist\單詞統(tǒng)計與查找分析工具\目錄下的單詞統(tǒng)計與查找分析工具.exe; - 體積:約 120-180MB(因包含 matplotlib 依賴,對比全局環(huán)境打包的 2GB+,大幅精簡);
- 功能驗證:雙擊 exe,測試文本導入 / 輸入、策略切換、ASL 可視化、單詞統(tǒng)計等核心功能,和原代碼完全一致。
十、單詞統(tǒng)計與查找分析工具(增強版)
(一)可擴展方向
- 增加 “單詞頻次排行榜”:展示出現(xiàn)次數(shù)最多的前 N 個單詞;
- 支持文件導入:解析文本文件(.txt),而非僅手動輸入;
- 算法對比可視化:用柱狀圖展示三種策略的 ASL 差異;
- 自定義哈希表大?。涸试S用戶調(diào)整 HASH_SIZE,觀察沖突率變化;
- 增加單詞長度統(tǒng)計:分析文本中單詞的平均長度、最長單詞等指標。
(二)單詞統(tǒng)計與查找分析工具擴展后的Python代碼完整實現(xiàn)
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import re
import sys
import matplotlib
# 設(shè)置matplotlib后端為TkAgg,適配tkinter
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
# ===================== 全局常量(基礎(chǔ)默認值) =====================
MAX_LINEAR_SIZE = 10000 # 線性表最大容量
DEFAULT_HASH_SIZE = 1009 # 默認哈希表大小
MAX_WORD_LEN = 50 # 單詞最大長度
DEFAULT_RANKING_N = 10 # 默認排行榜展示前10個單詞
# ===================== 核心算法函數(shù)(兼容自定義哈希表大小+性能優(yōu)化) =====================
def normalize(token: str, pos: list) -> tuple[bool, str]:
"""
標準化單詞:跳過非字母字符,提取連續(xù)字母并轉(zhuǎn)小寫(性能優(yōu)化版)
:param token: 待處理字符串
:param pos: 當前處理位置(用列表傳引用,Python整數(shù)不可變)
:return: (是否提取到單詞, 標準化后的單詞)
"""
start = pos[0]
# 跳過非字母字符(優(yōu)化循環(huán)邏輯)
while start < len(token) and not token[start].isalpha():
start += 1
if start >= len(token):
pos[0] = start
return False, ""
# 提取連續(xù)字母(一次性切片,減少循環(huán)次數(shù))
end = start
while end < len(token) and token[end].isalpha():
end += 1
pos[0] = end
return True, token[start:end].lower()
def my_hash(word: str, hash_size: int) -> int:
"""Time33哈希函數(shù)(適配自定義哈希表大?。?""
h = 0
for char in word:
h = (h << 5) + h + ord(char) # h = h*33 + 字符ASCII
return h % hash_size
def linear_search(linear_list: list, word: str) -> tuple[bool, int]:
"""
順序查找
:return: (是否找到, 比較次數(shù))
"""
cmp_count = 0
for item in linear_list:
cmp_count += 1
if item["word"] == word:
return True, cmp_count
return False, cmp_count
# 快速排序相關(guān)函數(shù)(按單詞字典序)
def swap_linear_node(linear_list: list, i: int, j: int):
"""交換線性表中的兩個元素"""
linear_list[i], linear_list[j] = linear_list[j], linear_list[i]
def partition(linear_list: list, low: int, high: int) -> int:
"""快排分區(qū)函數(shù)"""
pivot = linear_list[high]["word"]
i = low - 1 # 小于基準的區(qū)域邊界
for j in range(low, high):
if linear_list[j]["word"] < pivot:
i += 1
swap_linear_node(linear_list, i, j)
swap_linear_node(linear_list, i + 1, high)
return i + 1
def quick_sort(linear_list: list, low: int, high: int):
"""快速排序主函數(shù)"""
if low < high:
pi = partition(linear_list, low, high)
quick_sort(linear_list, low, pi - 1)
quick_sort(linear_list, pi + 1, high)
def binary_search(linear_list: list, word: str) -> tuple[bool, int]:
"""
二分查找(需先排序)
:return: (是否找到, 比較次數(shù))
"""
cmp_count = 0
low, high = 0, len(linear_list) - 1
while low <= high:
cmp_count += 1
mid = (low + high) // 2
mid_word = linear_list[mid]["word"]
if mid_word == word:
return True, cmp_count
elif mid_word > word:
high = mid - 1
else:
low = mid + 1
return False, cmp_count
# ===================== 新增工具函數(shù) =====================
def calculate_word_length_stats(word_list: list) -> dict:
"""計算單詞長度統(tǒng)計指標"""
if not word_list:
return {
"avg_length": 0.0,
"max_len_word": "",
"max_length": 0,
"min_len_word": "",
"min_length": 0
}
lengths = [len(word["word"]) for word in word_list]
avg_length = sum(lengths) / len(lengths)
max_idx = lengths.index(max(lengths))
min_idx = lengths.index(min(lengths))
return {
"avg_length": round(avg_length, 2),
"max_len_word": word_list[max_idx]["word"],
"max_length": max(lengths),
"min_len_word": word_list[min_idx]["word"],
"min_length": min(lengths)
}
def get_word_ranking(word_list: list, top_n: int) -> list:
"""生成單詞頻次排行榜(降序)"""
if not word_list:
return []
# 按頻次降序排序,頻次相同按單詞字典序升序
sorted_list = sorted(word_list, key=lambda x: (-x["count"], x["word"]))
# 處理top_n超過列表長度的情況
top_n = min(top_n, len(sorted_list))
return sorted_list[:top_n]
def analyze_all_strategies(input_text: str, hash_size: int) -> dict:
"""批量分析三種策略的ASL,用于可視化"""
# 第一步:解析文本得到所有單詞
pos = [0]
token = input_text
all_words = []
while True:
has_word, word = normalize(token, pos)
if not has_word:
break
all_words.append(word)
if not all_words:
return {"strategy1_asl": 0, "strategy2_asl": 0, "strategy3_asl": 0}
# 策略1:順序查找
linear_list1 = []
for word in all_words:
found, _ = linear_search(linear_list1, word)
if found:
for item in linear_list1:
if item["word"] == word:
item["count"] += 1
break
else:
linear_list1.append({"word": word, "count": 1})
unique1 = len(linear_list1)
total_cmp1 = sum([linear_search(linear_list1, item["word"])[1] for item in linear_list1])
strategy1_asl = total_cmp1 / unique1 if unique1 > 0 else 0
# 策略2:快排+二分
linear_list2 = linear_list1.copy()
quick_sort(linear_list2, 0, len(linear_list2) - 1)
total_cmp2 = sum([binary_search(linear_list2, item["word"])[1] for item in linear_list2])
strategy2_asl = total_cmp2 / unique1 if unique1 > 0 else 0
# 策略3:哈希表
hash_table = [[] for _ in range(hash_size)]
for word in all_words:
h = my_hash(word, hash_size)
found = False
for item in hash_table[h]:
if item["word"] == word:
item["count"] += 1
found = True
break
if not found:
hash_table[h].append({"word": word, "count": 1})
unique3 = sum([len(bucket) for bucket in hash_table])
total_cmp3 = sum([idx + 1 for bucket in hash_table for idx, item in enumerate(bucket)])
strategy3_asl = total_cmp3 / unique3 if unique3 > 0 else 0
return {
"strategy1_asl": round(strategy1_asl, 2),
"strategy2_asl": round(strategy2_asl, 2),
"strategy3_asl": round(strategy3_asl, 2)
}
# ===================== GUI主類(集成所有新增功能) =====================
class WordAnalyzerApp:
def __init__(self, root):
self.root = root
self.root.title("單詞統(tǒng)計與查找分析工具(增強版)")
self.root.geometry("1000x800") # 擴大窗口適配新功能
# 添加窗口關(guān)閉事件處理(優(yōu)雅退出)
self.root.protocol("WM_DELETE_WINDOW", self._on_window_close)
# 核心變量
self.strategy_var = tk.StringVar(value="1")
self.hash_size_var = tk.StringVar(value=str(DEFAULT_HASH_SIZE))
self.ranking_n_var = tk.StringVar(value=str(DEFAULT_RANKING_N))
self.all_strategies_asl = {"strategy1_asl": 0, "strategy2_asl": 0, "strategy3_asl": 0}
# 初始化UI
self._setup_ui()
def _setup_ui(self):
"""構(gòu)建GUI界面(新增功能組件)"""
# 主容器
main_frame = ttk.Frame(self.root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 1. 文件導入 + 文本輸入?yún)^(qū)域
file_frame = ttk.Frame(main_frame)
file_frame.pack(fill=tk.X, pady=(0, 5))
# 導入文件按鈕
import_btn = ttk.Button(file_frame, text="導入文本文件(.txt)", command=self._import_file)
import_btn.pack(side=tk.LEFT, padx=(0, 10))
# 哈希表大小設(shè)置
hash_label = ttk.Label(file_frame, text="哈希表大?。?)
hash_label.pack(side=tk.LEFT, padx=(10, 5))
hash_entry = ttk.Entry(file_frame, textvariable=self.hash_size_var, width=10)
hash_entry.pack(side=tk.LEFT, padx=(0, 10))
ttk.Label(file_frame, text="(建議輸入質(zhì)數(shù))").pack(side=tk.LEFT)
# 文本輸入?yún)^(qū)域
input_label = ttk.Label(main_frame, text="請輸入待分析文本(輸入#結(jié)束):")
input_label.pack(anchor=tk.W)
self.text_input = tk.Text(main_frame, height=8, wrap=tk.WORD)
self.text_input.pack(fill=tk.X, pady=(0, 5))
# 實時提示標簽
self.input_tip_label = ttk.Label(main_frame, text="", foreground="#666666")
self.input_tip_label.pack(anchor=tk.W)
self.text_input.bind("<<Modified>>", self._on_text_changed)
self.text_input_modified = False
# 2. 策略選擇 + 排行榜設(shè)置區(qū)域
setting_frame = ttk.LabelFrame(main_frame, text="分析設(shè)置", padding="10")
setting_frame.pack(fill=tk.X, pady=(10, 5))
# 策略單選按鈕
strategy_subframe = ttk.Frame(setting_frame)
strategy_subframe.pack(side=tk.LEFT, padx=10)
ttk.Radiobutton(
strategy_subframe, text="策略1:順序查找",
variable=self.strategy_var, value="1",
command=self._on_strategy_toggled
).pack(side=tk.LEFT, padx=5)
ttk.Radiobutton(
strategy_subframe, text="策略2:快速排序+二分查找",
variable=self.strategy_var, value="2",
command=self._on_strategy_toggled
).pack(side=tk.LEFT, padx=5)
ttk.Radiobutton(
strategy_subframe, text="策略3:哈希表查找",
variable=self.strategy_var, value="3",
command=self._on_strategy_toggled
).pack(side=tk.LEFT, padx=5)
# 排行榜設(shè)置
ranking_subframe = ttk.Frame(setting_frame)
ranking_subframe.pack(side=tk.RIGHT, padx=10)
ttk.Label(ranking_subframe, text="頻次排行榜前N個單詞:").pack(side=tk.LEFT, padx=5)
ranking_entry = ttk.Entry(ranking_subframe, textvariable=self.ranking_n_var, width=5)
ranking_entry.pack(side=tk.LEFT, padx=5)
# 3. 功能按鈕區(qū)域
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(fill=tk.X, pady=(10, 10))
# 開始分析按鈕
analyze_btn = ttk.Button(btn_frame, text="開始分析", command=self._on_analyze_clicked)
analyze_btn.pack(side=tk.LEFT, padx=(0, 10))
# ASL可視化按鈕
asl_chart_btn = ttk.Button(btn_frame, text="ASL算法對比可視化", command=self._show_asl_chart)
asl_chart_btn.pack(side=tk.LEFT)
# 4. 結(jié)果顯示區(qū)域
result_label = ttk.Label(main_frame, text="分析結(jié)果:")
result_label.pack(anchor=tk.W)
self.result_display = tk.Text(main_frame, height=12, wrap=tk.WORD, state=tk.DISABLED)
self.result_display.pack(fill=tk.BOTH, expand=True)
def _import_file(self):
"""導入txt文本文件"""
file_path = filedialog.askopenfilename(
title="選擇文本文件",
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
)
if not file_path:
return
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# 清空原有內(nèi)容,插入文件內(nèi)容
self.text_input.delete("1.0", tk.END)
self.text_input.insert(tk.END, content)
messagebox.showinfo("成功", f"已導入文件:{file_path}")
except Exception as e:
messagebox.showerror("錯誤", f"文件導入失?。簕str(e)}")
def _on_text_changed(self, event):
"""文本輸入實時監(jiān)控"""
if self.text_input_modified:
return
self.text_input_modified = True
input_text = self.text_input.get("1.0", tk.END).strip()
char_count = len(input_text)
has_sharp = "#" in input_text
tip = f"實時狀態(tài):已輸入 {char_count} 個字符 | "
if has_sharp:
tip += "檢測到#,分析時會截斷#后的內(nèi)容"
self.input_tip_label.config(foreground="#E67E22")
else:
tip += "未檢測到#,將分析全部文本"
self.input_tip_label.config(foreground="#666666")
self.input_tip_label.config(text=tip)
self.text_input.edit_modified(False)
self.text_input_modified = False
def _on_strategy_toggled(self):
"""策略切換反饋"""
strategy_id = self.strategy_var.get()
strategy_names = {
"1": "順序查找",
"2": "快速排序+二分查找",
"3": "哈希表查找"
}
strategy_name = strategy_names.get(strategy_id, "未知")
self._update_result(f"已切換為【{strategy_name}】策略,請點擊「開始分析」重新計算結(jié)果", "#3498DB")
def _update_result(self, text: str, color: str = "#000000"):
"""更新結(jié)果顯示區(qū)"""
self.result_display.config(state=tk.NORMAL)
self.result_display.delete("1.0", tk.END)
self.result_display.insert(tk.END, text)
self.result_display.config(foreground=color, state=tk.DISABLED)
def _show_asl_chart(self):
"""繪制ASL算法對比柱狀圖"""
input_text = self.text_input.get("1.0", tk.END).strip()
if "#" in input_text:
input_text = input_text.split("#")[0]
if not input_text.strip():
messagebox.showwarning("提示", "請先輸入/導入文本內(nèi)容!")
return
# 驗證哈希表大小輸入
try:
hash_size = int(self.hash_size_var.get())
if hash_size <= 0:
raise ValueError
except ValueError:
messagebox.showerror("錯誤", "哈希表大小必須為正整數(shù)!")
return
# 批量分析三種策略的ASL
self.all_strategies_asl = analyze_all_strategies(input_text, hash_size)
# 創(chuàng)建圖表窗口
chart_window = tk.Toplevel(self.root)
chart_window.title("ASL算法對比柱狀圖")
chart_window.geometry("600x400")
# 關(guān)鍵修改:字體設(shè)置移到創(chuàng)建圖表之前,確保生效
plt.rcParams["font.family"] = ["SimHei", "Microsoft YaHei", "WenQuanYi Micro Hei"]
plt.rcParams["axes.unicode_minus"] = False # 解決負號顯示問題
# 繪制柱狀圖
fig, ax = plt.subplots(figsize=(8, 5))
strategies = ["順序查找", "快排+二分查找", "哈希表查找"]
asl_values = [
self.all_strategies_asl["strategy1_asl"],
self.all_strategies_asl["strategy2_asl"],
self.all_strategies_asl["strategy3_asl"]
]
ax.bar(strategies, asl_values, color=['#FF6B6B', '#4ECDC4', '#45B7D1'])
# 優(yōu)化y軸刻度(解決數(shù)據(jù)差異過大導致小值被壓縮的問題)
ax.set_yscale("log") # 改用對數(shù)刻度,適配ASL的數(shù)量級差異
ax.set_xlabel("查找策略")
ax.set_ylabel("平均查找長度(ASL,對數(shù)刻度)")
ax.set_title("不同查找策略的ASL對比")
ax.grid(axis='y', linestyle='--', alpha=0.7)
# 調(diào)整數(shù)值標簽位置(適配對數(shù)刻度)
for i, v in enumerate(asl_values):
# 數(shù)值標簽顯示在柱子頂部,避免重疊
ax.text(i, v * 1.1, f"{v:.2f}", ha='center', va='bottom', fontsize=10)
# 將圖表嵌入tkinter窗口
canvas = FigureCanvasTkAgg(fig, master=chart_window)
canvas.draw()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
def _on_analyze_clicked(self):
"""分析按鈕點擊事件(集成所有新增統(tǒng)計)"""
# 1. 輸入驗證與預(yù)處理
input_text = self.text_input.get("1.0", tk.END).strip()
strategy = int(self.strategy_var.get())
if "#" in input_text:
input_text = input_text.split("#")[0]
if not input_text.strip():
self._update_result("請輸入/導入待分析的文本內(nèi)容!", "#E74C3C")
return
# 驗證哈希表大小輸入
try:
hash_size = int(self.hash_size_var.get())
if hash_size <= 0:
raise ValueError
except ValueError:
messagebox.showerror("錯誤", "哈希表大小必須為正整數(shù)!")
return
# 驗證排行榜N值輸入
try:
ranking_n = int(self.ranking_n_var.get())
if ranking_n <= 0:
raise ValueError
except ValueError:
messagebox.showerror("錯誤", "排行榜N值必須為正整數(shù)!")
return
# 2. 初始化數(shù)據(jù)結(jié)構(gòu)
linear_list = []
hash_table = [[] for _ in range(hash_size)]
total_words = 0
unique_words = 0
total_cmp = 0
word_length_records = [] # 存儲所有單詞(用于長度統(tǒng)計)
# 3. 解析并統(tǒng)計單詞
pos = [0]
token = input_text
while True:
has_word, word = normalize(token, pos)
if not has_word:
break
total_words += 1
# 策略1/2:線性表處理
if strategy in (1, 2):
found, _ = linear_search(linear_list, word)
if found:
for item in linear_list:
if item["word"] == word:
item["count"] += 1
break
else:
linear_list.append({"word": word, "count": 1})
word_length_records = linear_list # 同步到長度統(tǒng)計列表
# 策略3:哈希表處理
elif strategy == 3:
h = my_hash(word, hash_size)
found = False
for item in hash_table[h]:
if item["word"] == word:
item["count"] += 1
found = True
break
if not found:
hash_table[h].append({"word": word, "count": 1})
# 整理哈希表數(shù)據(jù)用于長度統(tǒng)計
word_length_records = []
for bucket in hash_table:
word_length_records.extend(bucket)
# 4. 計算核心指標
asl = 0.0
if strategy == 1:
unique_words = len(linear_list)
total_cmp = sum([linear_search(linear_list, item["word"])[1] for item in linear_list])
asl = total_cmp / unique_words if unique_words > 0 else 0.0
elif strategy == 2:
unique_words = len(linear_list)
if unique_words > 0:
quick_sort(linear_list, 0, unique_words - 1)
total_cmp = sum([binary_search(linear_list, item["word"])[1] for item in linear_list])
asl = total_cmp / unique_words if unique_words > 0 else 0.0
elif strategy == 3:
unique_words = sum([len(bucket) for bucket in hash_table])
total_cmp = sum([idx + 1 for bucket in hash_table for idx, item in enumerate(bucket)])
asl = total_cmp / unique_words if unique_words > 0 else 0.0
# 5. 計算新增統(tǒng)計指標
length_stats = calculate_word_length_stats(word_length_records)
ranking_list = get_word_ranking(word_length_records, ranking_n)
# 6. 格式化結(jié)果展示
result = f"===== 基礎(chǔ)統(tǒng)計 =====\n"
result += f"Total Words(總單詞數(shù)): {total_words}\n"
result += f"Unique Words(唯一單詞數(shù)): {unique_words}\n"
result += f"ASL(平均查找長度): {asl:.2f}\n\n"
result += f"===== 單詞長度統(tǒng)計 =====\n"
result += f"平均單詞長度: {length_stats['avg_length']}\n"
result += f"最長單詞: {length_stats['max_len_word']}(長度:{length_stats['max_length']})\n"
result += f"最短單詞: {length_stats['min_len_word']}(長度:{length_stats['min_length']})\n\n"
result += f"===== 單詞頻次排行榜(前{ranking_n}個)=====\n"
if ranking_list:
for idx, item in enumerate(ranking_list, 1):
result += f"{idx}. {item['word']} - 出現(xiàn)次數(shù):{item['count']}\n"
else:
result += "暫無單詞數(shù)據(jù)\n"
self._update_result(result)
def _on_window_close(self):
"""優(yōu)雅關(guān)閉窗口,避免強制退出導致的中斷報錯"""
self.root.quit()
self.root.destroy()
# ===================== 主函數(shù)(捕獲中斷+優(yōu)雅退出) =====================
if __name__ == "__main__":
try:
root = tk.Tk()
app = WordAnalyzerApp(root)
root.mainloop()
except KeyboardInterrupt:
print("程序已被用戶手動終止,正在優(yōu)雅退出...")
root.quit()
root.destroy()
except Exception as e:
print(f"程序運行出錯:{str(e)}")
sys.exit(1)
(三)核心新增功能說明
1. 自定義哈希表大小
- 在 UI 中新增哈希表大小輸入框,默認值為 1009,支持用戶自定義;
- 增加輸入驗證(必須為正整數(shù)),避免無效輸入;
- 核心哈希函數(shù)
my_hash新增hash_size參數(shù),適配自定義大小。
2. 文件導入功能
- 新增 “導入文本文件 (.txt)” 按鈕,支持選擇本地 txt 文件;
- 處理文件讀取異常(編碼、文件不存在等),給出友好提示;
- 導入后自動將文件內(nèi)容填充到文本輸入框。
3. 單詞長度統(tǒng)計
- 新增
calculate_word_length_stats函數(shù),計算:- 平均單詞長度;
- 最長單詞(及長度);
- 最短單詞(及長度);
- 分析結(jié)果中單獨展示長度統(tǒng)計模塊,數(shù)據(jù)格式化輸出。
4. 單詞頻次排行榜
- 新增排行榜 N 值輸入框(默認前 10),支持自定義展示數(shù)量;
- 新增
get_word_ranking函數(shù),按頻次降序排序(頻次相同按單詞字典序); - 處理 N 值超過唯一單詞數(shù)的邊界情況,避免報錯。
5. ASL 算法對比可視化
- 集成 matplotlib,適配 tkinter 后端;
- 新增
analyze_all_strategies函數(shù),批量計算三種策略的 ASL; - 點擊 “ASL 算法對比可視化” 按鈕,彈出獨立窗口展示柱狀圖,直觀對比三種策略的 ASL 差異;
- 柱狀圖添加數(shù)值標簽、網(wǎng)格線,提升可讀性。
(四)使用注意事項
- 運行前需安裝 matplotlib:
pip install matplotlib; - 哈希表大小建議輸入質(zhì)數(shù)(如 1009、997 等),可減少哈希沖突;
- 可視化功能需先執(zhí)行文本分析,確保有有效數(shù)據(jù);
- 支持的文本文件編碼為 UTF-8,若導入其他編碼文件(如 GBK),可手動修改
_import_file函數(shù)中的編碼參數(shù)。
(五)程序運行截圖展示









(六)打包項目
根據(jù)本文的打包項目的方法,擴展版和原始版的代碼必須在同一目錄下,可以直接運行下面命令:
pyinstaller -w -n 單詞統(tǒng)計與查找分析工具(增強版) -D --collect-data matplotlib 單詞統(tǒng)計與查找分析工具擴展版.py


十一、總結(jié)
本文介紹了一款單詞統(tǒng)計與查找分析工具的設(shè)計與實現(xiàn)。該工具支持三種查找策略(順序查找、快速排序+二分查找、哈希表查找),可計算文本中的單詞總數(shù)、唯一單詞數(shù)和平均查找長度(ASL)。工具采用Python開發(fā),包含核心算法層和GUI交互層,具有文本標準化處理、實時狀態(tài)提示和多策略對比功能。擴展版新增了文件導入、單詞長度統(tǒng)計、頻次排行榜和ASL可視化等功能,支持自定義哈希表大小。文章詳細闡述了工具的技術(shù)選型、架構(gòu)設(shè)計、核心算法實現(xiàn)和測試優(yōu)化過程,并提供了完整的Python代碼實現(xiàn)和打包方法。
以上就是基于Python實現(xiàn)的單詞統(tǒng)計與查找分析工具的詳細內(nèi)容,更多關(guān)于Python單詞統(tǒng)計與查找工具的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實現(xiàn)Word文檔的圖片插入與排版技巧
在創(chuàng)建專業(yè)文檔時,圖片是傳達信息、增強視覺效果的重要元素,本文將詳細介紹如何使用 Python 在 Word 文檔中插入圖片并進行各種高級處理,感興趣的小伙伴可以了解下2026-03-03
Django--權(quán)限Permissions的例子
今天小編就為大家分享一篇Django--權(quán)限Permissions的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08
Playwright的wait funtion測試的實現(xiàn)
本文主要介紹了Playwright的wait funtion測試的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2026-04-04
django-celery-beat搭建定時任務(wù)的實現(xiàn)
本文主要介紹了django-celery-beat搭建定時任務(wù)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-03-03

