คลาส TrieNode: แทรกและค้นหา
สร้าง TrieNode ที่มีพจนานุกรม children และแฟล็ก is_end นำการแทรกและการค้นหาแบบตรงทั้งหมดไปใช้งาน และวิเคราะห์ว่าแต่ละการดำเนินการใช้เวลา O(m) เมื่อ m คือความยาวคำ
คลาส TrieNode: แทรกและค้นหา เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
Trie คืออะไร
Trie (ต้นไม้คำนำหน้า) เป็นโครงสร้างข้อมูลรูปต้นไม้ที่แต่ละโหนดแทนอักขระหนึ่งตัว คำต่าง ๆ ถูกจัดเก็บโดยเชื่อมอักขระจากรากไปยังใบ รากแทนสตริงว่าง เส้นทางแต่ละเส้นจากรากไปยังโหนด is_end = True จะแสดงเป็นคำที่จัดเก็บไว้ Trie เหมาะอย่างยิ่งสำหรับ การสืบค้นตามคำนำหน้า เช่น autocomplete การตรวจการสะกด และการกำหนดเส้นทาง IP โดยทำงานได้ดีกว่าตารางแฮชสำหรับกรณีเหล่านี้
การออกแบบคลาส TrieNode
TrieNode มีฟิลด์สองรายการ ได้แก่ children — พจนานุกรมที่จับคู่อักขระกับ TrieNode ของโหนดลูก และ is_end — ค่าบูลีนที่ระบุว่าโหนดนี้เป็นจุดสิ้นสุดของคำที่จัดเก็บไว้หรือไม่ การใช้พจนานุกรมแทนอาร์เรย์อักขระ 26 ตำแหน่งแบบตายตัวทำให้รองรับชุดอักขระใด ๆ ได้ทั่วไป และประหยัดหน่วยความจำสำหรับ Trie ที่มีข้อมูลเบาบาง โหนดแต่ละโหนดใน Trie แทนตำแหน่งอักขระหนึ่งตำแหน่งในคำที่อยู่ด้านล่างโหนดนั้นโดยพอดี
class TrieNode:
def __init__(self):
self.children = {} # char -> TrieNode
self.is_end = False # True if a word ends here
class Trie:
def __init__(self):
self.root = TrieNode()
def __repr__(self):
return f'Trie(root with {len(self.root.children)} children)'
t = Trie()
print(t) # Trie(root with 0 children)การดำเนินการ insert
หากต้องการ insert คำ ให้เริ่มเดินจากราก และสร้าง TrieNode ใหม่สำหรับอักขระแต่ละตัวที่ยังไม่มีอยู่ในโหนดปัจจุบัน หลังจากประมวลผลอักขระทั้งหมดแล้ว ให้กำหนดโหนดสุดท้ายเป็นจุดสิ้นสุดของคำ การ insert คำว่า 'แอปเปิล' และ 'แอป' จะสร้างเส้นทางร่วมตามอักขระของคำแรก โดยโหนดสุดท้ายของ 'แอปเปิล' และโหนดปลายของ 'แอป' จะถูกทำเครื่องหมายว่าเป็นจุดสิ้นสุดของคำที่เกี่ยวข้อง
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = True
t = Trie()
t.insert('apple')
t.insert('app')
print('Inserted apple and app')
print('app is_end:', t.root.children['a'].children['p'].children['p'].is_end)การดำเนินการ search
หากต้องการค้นหาคำแบบตรงทั้งหมด ให้เดินไปตาม Trie โดยใช้อักขระแต่ละตัว หากอักขระตัวใดไม่มีอยู่ในโหนดปัจจุบัน ให้ส่งกลับค่าเท็จ หากพบอักขระครบทั้งหมด ให้ส่งกลับ node.is_end ซึ่งจะเป็นค่าจริงก็ต่อเมื่อมีคำสิ้นสุดตรงตำแหน่งนี้ ไม่ใช่เพียงมีคำนำหน้ามาถึงตำแหน่งนี้ ความแตกต่างระหว่าง 'มีคำนำหน้าอยู่' กับ 'มีคำตรงทั้งหมดอยู่' เป็นสิ่งสำคัญและมักถูกนำไปทดสอบ
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for c in word:
if c not in node.children:
node.children[c] = TrieNode()
node = node.children[c]
node.is_end = True
def search(self, word):
node = self.root
for c in word:
if c not in node.children:
return False
node = node.children[c]
return node.is_end # must be a complete word
t = Trie()
t.insert('apple')
print(t.search('apple')) # True
print(t.search('app')) # False (app not inserted)
print(t.search('orange')) # Falseการขึ้นต้นด้วย (การค้นหาคำนำหน้า)
เมธอด starts_with ตรวจสอบว่ามีคำที่ insert ไว้คำใดขึ้นต้นด้วยคำนำหน้าที่กำหนดหรือไม่ เมธอดนี้เดินตามเส้นทางเดียวกับ search แต่แทนที่จะตรวจสอบ is_end จะส่งกลับค่าจริงทันทีที่เดินตามอักขระของคำนำหน้าครบทั้งหมด ซึ่งหมายความว่าเส้นทางของคำนำหน้านั้นมีอยู่ใน Trie
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for c in word:
if c not in node.children:
node.children[c] = TrieNode()
node = node.children[c]
node.is_end = True
def search(self, word):
node = self.root
for c in word:
if c not in node.children: return False
node = node.children[c]
return node.is_end
def starts_with(self, prefix):
node = self.root
for c in prefix:
if c not in node.children: return False
node = node.children[c]
return True # prefix path exists
t = Trie()
t.insert('apple')
print(t.starts_with('app')) # True
print(t.starts_with('ape')) # False
print(t.search('app')) # False (not inserted)ความซับซ้อนด้านเวลาและพื้นที่
การดำเนินการแต่ละอย่างของ Trie ได้แก่ insert, search และ starts_with ใช้เวลา O(m) โดย m คือความยาวของคำ เพราะเราเดินผ่านโหนดมากที่สุด m โหนด ส่วนพื้นที่ใช้ O(ALPHABET_SIZE × N × M) โดย N คือจำนวนคำ และ M คือความยาวคำโดยเฉลี่ย ในทางปฏิบัติ คำนำหน้าที่ใช้ร่วมกันช่วยลดการใช้พื้นที่ได้มาก พจนานุกรม children ที่อาศัยตารางแฮชใช้พื้นที่น้อยกว่าอาร์เรย์ 26 ตำแหน่งแบบตายตัวสำหรับ Trie ที่มีข้อมูลเบาบาง แต่แลกกับค่าใช้จ่ายคงที่ต่อการค้นหาแต่ละครั้งที่สูงขึ้นเล็กน้อย
การใช้อาร์เรย์แทนพจนานุกรม
สำหรับอักษรภาษาอังกฤษตัวพิมพ์เล็กเท่านั้น ให้ใช้อาร์เรย์ขนาดคงที่ children = [None] * 26 โดยใช้ดัชนี ord(c) - ord('a') วิธีนี้เร็วกว่า เพราะการค้นหาโหนดลูกใช้เวลา O(1) เมื่อเทียบกับตารางแฮช และมีผังการใช้หน่วยความจำที่คาดเดาได้ ควรใช้แบบพจนานุกรมเมื่อชุดอักขระมีขนาดใหญ่หรือไม่ทราบแน่ชัด เช่น Unicode และใช้แบบอาร์เรย์สำหรับโจทย์การแข่งขันที่มีเฉพาะอักษรภาษาอังกฤษตัวพิมพ์เล็ก
class TrieNodeArray:
def __init__(self):
self.children = [None] * 26
self.is_end = False
class TrieArray:
def __init__(self):
self.root = TrieNodeArray()
def insert(self, word):
node = self.root
for c in word:
idx = ord(c) - ord('a')
if node.children[idx] is None:
node.children[idx] = TrieNodeArray()
node = node.children[idx]
node.is_end = True
def search(self, word):
node = self.root
for c in word:
idx = ord(c) - ord('a')
if node.children[idx] is None: return False
node = node.children[idx]
return node.is_end
t = TrieArray()
t.insert('cat')
print(t.search('cat')) # True
print(t.search('car')) # Falseการดำเนินการลบ
การลบจาก Trie ต้องรองรับสามกรณี ได้แก่ (1) ไม่มีคำอยู่ — ไม่ต้องทำอะไร (2) มีคำอยู่แต่คำนั้นเป็นคำนำหน้าของอีกคำ — ยกเลิกการตั้งค่า is_end เท่านั้น (3) มีคำอยู่และคำนั้นไม่ใช่คำนำหน้า — ลบโหนดจากล่างขึ้นบน และหยุดเมื่อโหนดมีโหนดลูกอื่นหรือเป็นจุดสิ้นสุดของอีกคำหนึ่ง การลบมักไม่ค่อยถูกทดสอบในการสัมภาษณ์ แต่ควรทำความเข้าใจแนวคิดนี้ไว้
การนับคำด้วยคำนำหน้า
เพิ่มฟิลด์ count ให้แต่ละโหนด และเพิ่มค่าฟิลด์นี้ทุกครั้งที่มีการเดินผ่านโหนดระหว่าง insert หากต้องการนับคำที่มีคำนำหน้าที่กำหนด ให้เดินไปยังโหนดปลายของคำนำหน้าแล้วส่งกลับค่าการนับ วิธีนี้ทำให้การสืบค้น autocomplete ใช้เวลา O(m) โดยไม่ต้องเดินผ่านโหนดลูกทั้งหมด และเป็นส่วนขยายที่มีประโยชน์สำหรับระบบ autocomplete ในโลกจริง
class TrieNodeCount:
def __init__(self):
self.children = {}
self.is_end = False
self.count = 0 # words passing through this node
class TrieCount:
def __init__(self):
self.root = TrieNodeCount()
def insert(self, word):
node = self.root
for c in word:
if c not in node.children:
node.children[c] = TrieNodeCount()
node = node.children[c]
node.count += 1 # increment on each level
node.is_end = True
def count_with_prefix(self, prefix):
node = self.root
for c in prefix:
if c not in node.children: return 0
node = node.children[c]
return node.count
t = TrieCount()
for w in ['apple','app','application','apply']:
t.insert(w)
print(t.count_with_prefix('app')) # 4
print(t.count_with_prefix('appl')) # 3การเปรียบเทียบ Trie กับตารางแฮช
ตารางแฮช สามารถค้นหาแบบตรงทั้งหมดได้ในเวลาเฉลี่ย O(m) แต่ไม่สามารถตอบการสืบค้นคำนำหน้าได้อย่างมีประสิทธิภาพ เพราะต้องตรวจสอบคีย์ทั้งหมด Trie ตอบการสืบค้นคำนำหน้าได้ในเวลา O(p) โดย p คือความยาวคำนำหน้า จัดกลุ่มคำที่ใช้คำนำหน้าร่วมกันโดยธรรมชาติ และไม่จำเป็นต้องใช้การแฮช ควรใช้ Trie เมื่อมีการสืบค้นคำนำหน้าบ่อย ต้องการ autocomplete หรือการตรวจการสะกด และควรใช้ตารางแฮชเมื่อจำเป็นต้องค้นหาแบบตรงทั้งหมดเท่านั้น
Trie ในระบบจริง
การใช้งาน Trie ในโลกจริง ได้แก่ autocomplete (คำแนะนำการค้นหาของ Google) เครื่องมือตรวจการสะกด (ค้นหาคำที่ตรงกันมากที่สุด) การกำหนดเส้นทาง IP (การจับคู่คำนำหน้าที่ยาวที่สุดในเราเตอร์) ข้อความคาดเดาแบบ T9 (แยกความหมายของอักขระ) และ ตัวแก้ไข DNS (ค้นหาชื่อโดเมนตามลำดับชั้น) ในแต่ละกรณี การแลกเปลี่ยนระหว่างเวลา O(m) ต่อการดำเนินการกับพื้นที่ O(ALPHABET × จำนวนโหนด) ของ Trie ทำให้ Trie เป็นเครื่องมือที่เหมาะสมสำหรับการค้นหาที่รวดเร็วและรองรับคำนำหน้าในระบบขนาดใหญ่
ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูล & อัลกอริทึม — การเตรียมตัวสัมภาษณ์การเขียนโปรแกรมจากบทเรียนนี้
สรุปบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า TrieNode มีพจนานุกรมโหนดลูกและค่าบูลีนที่ระบุจุดสิ้นสุด insert จะเดินผ่านอักขระทีละตัว สร้างโหนดเมื่อจำเป็น และกำหนดจุดสิ้นสุดเมื่อถึงท้ายคำ และ search จะตรวจสอบจุดสิ้นสุด ขณะที่ starts_with ตรวจสอบเพียงว่าเส้นทางคำนำหน้ามีอยู่หรือไม่ ต่อไปเราจะเพิ่ม autocomplete ตามคำนำหน้าและศึกษาเมธอด starts_with ในรายละเอียดมากขึ้น
คำถามที่พบบ่อย
บทเรียน “คลาส TrieNode: แทรกและค้นหา” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “คลาส TrieNode: แทรกและค้นหา” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “คลาส TrieNode: แทรกและค้นหา”
สร้าง TrieNode ที่มีพจนานุกรม children และแฟล็ก is_end นำการแทรกและการค้นหาแบบตรงทั้งหมดไปใช้งาน และวิเคราะห์ว่าแต่ละการดำเนินการใช้เวลา O(m) เมื่อ m คือความยาวคำ คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “คลาส TrieNode: แทรกและค้นหา” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม
ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- คลาส TrieNode: แทรกและค้นหา
- การค้นหาคำนำหน้าและ Starts-With
- การค้นหาแบบไวลด์การ์ดและนิพจน์ประจำใน Trie
- การค้นหาคำ II: Trie + การย้อนกลับบนกริด