"STL คือคลังโปรแกรมมาตรฐานของ C++ ที่รวบรวม โครงสร้างข้อมูล และ อัลกอริทึม ที่ผ่านการปรับแต่งประสิทธิภาพมาอย่างดีเยี่ยม"
ใช้สำหรับเก็บข้อมูล (เช่น vector, set, map)
ตัวชี้ตำแหน่งเพื่อเข้าถึงข้อมูลใน Container
ฟังก์ชันสำเร็จรูป (เช่น sort, binary_search)
"อาเรย์ที่มีความยืดหยุ่น สามารถปรับขนาดได้เองระหว่างการทำงาน"
การเพิ่มหรือลบข้อมูลตรงกลางทำได้ช้า (\(O(N)\)) เพราะต้องขยับข้อมูลตัวอื่น
"เราต้องระบุประเภทข้อมูลภายในเครื่องหมาย < > เสมอ"
push_back(x)
เพิ่มค่า x ต่อท้ายเวกเตอร์
pop_back()
ลบค่าตัวสุดท้ายทิ้ง
size()
คืนค่าจำนวนสมาชิกปัจจุบัน
empty()
เช็คว่าเวกเตอร์ว่างหรือไม่ (true/false)
การเรียก v[i] ที่อยู่นอกเหนือช่วงที่มีอยู่ จะทำให้เกิดพฤติกรรมที่ไม่คาดคิด (Undefined Behavior)
วิธีที่สั้นและปลอดภัยที่สุดในการเข้าถึงข้อมูลทุกตัวในเวกเตอร์
Keyword auto จะช่วยเดาประเภทข้อมูลในเวกเตอร์ให้เราโดยอัตโนมัติ
- ไม่รู้จำนวนข้อมูลล่วงหน้า
- ต้องการส่งเวกเตอร์ไปในฟังก์ชันได้ง่าย
- ต้องการใช้ฟังก์ชัน v.clear() เพื่อใช้ซ้ำ
- รู้ขนาดที่แน่นอนและข้อมูลใหญ่มาก (เช่น 10^7)
- ทำงานเร็วกว่านิดหน่อยเนื่องจากไม่มี overhead
- ประกาศแบบ Global เพื่อกัน Stack Overflow
LIFO Concept
"Last-In First-Out: สิ่งที่เข้าทีหลังสุด จะได้ออกก่อนเสมอ"
นึกถึงการวาง "จาน" ที่วางทับซ้อนกันอยู่
push(x): เพิ่มข้อมูลที่หัวแถวpop(): ลบข้อมูลที่หัวแถวทิ้งtop(): ดูข้อมูลที่หัวแถว\[O(1)\]
ทุกการดำเนินการทำงานด้วยความเร็วคงที่
FIFO Concept
"First-In First-Out: สิ่งที่เข้ามาก่อน จะได้ออกไปก่อน"
นึกถึงการ "เข้าแถว" ซื้อของในชีวิตจริง
push(x): เพิ่มข้อมูลที่ท้ายแถวpop(): ลบข้อมูลที่หน้าแถวทิ้งfront(): ดูข้อมูลตัวหน้าสุด\(O(1)\)
มีประสิทธิภาพเท่ากับ Stack
"เหมือน Queue แต่สมาชิกที่ถูกดึงออกมาจะเป็นตัวที่มี ความสำคัญ (ค่า) สูงสุด เสมอ"
มักเรียกว่า "Heap"
เป็น Max-Heap (เอาค่ามากสุดขึ้นก่อน)
\[O(\log N)\]
\[O(1)\]
การทำงานเบื้องหลังคือ Binary Heap
หากต้องการดึง ค่าน้อยที่สุด ออกมาก่อน ต้องประกาศแบบนี้:
"พบบ่อยมากใน Dijkstra's Algorithm และ Prim's Algorithm"
| Container | Insert (Average) | Access |
|---|---|---|
| Vector | \(O(1)\) amortized | \(O(1)\) |
| Stack / Queue | \(O(1)\) | \(O(1)\) (เฉพาะตัวหัว/ท้าย) |
| Priority Queue | \(O(\log N)\) | \(O(1)\) (เฉพาะค่าสูงสุด) |
1. ต้องการหาข้อมูลตามตำแหน่ง Index?
\(\rightarrow\) ใช้ std::vector
2. ต้องการดึงค่าที่เพิ่งใส่เข้าไปล่าสุด?
\(\rightarrow\) ใช้ std::stack
3. ต้องการจัดการข้อมูลตามลำดับก่อน-หลัง?
\(\rightarrow\) ใช้ std::queue
ถ้าข้อมูลมีขนาด 1,000,000 ตัว การเรียกใช้ pq.push() จะใช้การเปรียบเทียบข้อมูลสูงสุดกี่ครั้ง?
คำใบ้: \(\log_2(10^6) \approx 20\)
ลดความยาวของโค้ดเมื่อใช้ Container ที่ซับซ้อน
"ช่วยประหยัดเวลาพิมพ์ได้มหาศาลในการแข่งขัน"
เหนือกว่า Sequential Containers คือ...
Associative Containers
เจาะลึก std::set และ std::map เพื่อการค้นหาใน \(O(\log N)\)
พัก 15 นาที ก่อนเข้าสู่ส่วนที่ 2 ครับ!
"Iterator คือวัตถุที่ทำหน้าที่ 'ชี้ตำแหน่ง' ของสมาชิกในโครงสร้างข้อมูล "
{ 3, 4, 6, 8, 12, 13, 14, 17 }
↑ ↑
s.begin() s.end()
ชี้ที่ สมาชิกตัวแรก ของ Container
ชี้ที่ ตำแหน่งถัดจากตัวสุดท้าย (ว่างเปล่า)
it++ หรือ ++it
เลื่อนไปสมาชิกถัดไป
it-- หรือ --it
ถอยกลับไปสมาชิกก่อนหน้า
it + n
กระโดดไป n ตำแหน่ง (ใช้ได้กับ vector เท่านั้น)
โครงสร้างข้อมูลที่เก็บสมาชิกแบบ เรียงลำดับ (Sorted) และ ไม่มีตัวซ้ำ (Unique)
ข้อมูลจะถูกพิมพ์ออกมาแบบ เรียงจากน้อยไปมาก เสมอ
"หมายเหตุ: set ไม่สามารถใช้ [index] เข้าถึงข้อมูลได้เหมือน vector "
การใช้ s.find(x) เร็วกว่า std::find(begin, end, x) มากเพราะใช้โครงสร้างต้นไม้ช่วยค้นหา (\(O(\log N)\) vs \(O(N)\))
ชี้ที่ตัวแรกที่มีค่า \(\ge x\)
ชี้ที่ตัวแรกที่มีค่า \(> x\)
หากไม่มีตัวที่เข้าเงื่อนไข ทั้งคู่จะคืนค่าเป็น s.end()
"เหมือน set ทุกอย่าง แต่สามารถมีค่าที่ ซ้ำกันได้ "
จะลบเลข 5 "ทุกตัว" ทิ้งไป
จะลบเลข 5 เพียง "ตัวเดียว" (ตัวที่ iterator ชี้)
"อาเรย์แบบทั่วไปที่ใช้ข้อมูลอะไรก็ได้เป็น Index (Key) เพื่อเก็บข้อมูล (Value) "
ภายในเป็น Balanced BST เหมือน set
pair<Key, Value>"ระวัง: การเรียก m[key] ที่ไม่มีอยู่จริง จะทำให้ map จองที่และใส่ค่า default ให้ทันที"
เมื่อวนลูป สมาชิกแต่ละตัวจะเป็น pair:
"ทำงานด้วย Hash Table แทนที่จะเป็น Tree "
เร็วมาก! ค่าเฉลี่ยคือ \(O(1)\)
ข้อมูลไม่เรียงลำดับ และ Worst-case อาจเป็น \(O(N)\)
| Container | Data Structure | Time Complexity |
|---|---|---|
| Set / Map | Red-Black Tree | \(O(\log N)\) |
| Unordered Set/Map | Hash Table | \(O(1)\) (Avg) |
| Priority Queue | Heap | \(O(\log N)\) |
| Vector | Dynamic Array | \(O(1)\) amortized |
1. ต้องการความเร็วในการค้นหา \(O(1)\) และไม่สนลำดับ?
\(\rightarrow\) ใช้ unordered_set / map
2. ต้องการข้อมูลที่เรียงลำดับตลอดเวลา?
\(\rightarrow\) ใช้ set / map
3. ต้องการรับสมาชิกที่มีค่าซ้ำกันได้และเรียงลำดับ?
\(\rightarrow\) ใช้ multiset
เก็บข้อมูล 2 ตัว (มักเป็น Key, Value)
pair<int, string> p = {1, "hi"};
เก็บข้อมูลกี่ตัวก็ได้ (C++11+)
tuple<int, int, int> t = {1, 2, 5};
"STL Sort สามารถเรียงลำดับ pair/tuple ได้ทันทีโดยเปรียบเทียบจากตัวแรกไปตัวหลัง "
ทุกตัวรับพารามิเตอร์เป็น Iterator ช่วง [begin, end)
มีประโยชน์มากในโจทย์ Complete Search ที่ต้องลองทุกรูปแบบการสลับ
"ระวังการแก้ไข Container (เช่น push_back หรือ insert) ขณะที่ยังมี Iterator ชี้อยู่"
การขยายขนาดของ vector อาจทำให้ memory ถูกย้ายตำแหน่ง
ซึ่งจะทำให้ Iterator ตัวเดิม "เสียไป" (Invalidated) และโปรแกรมอาจแครชได้
หากต้องการลบเลข 5 เพียง 1 ตัวออกจาก multiset s จะต้องใช้คำสั่งใด?