Rust में एल्गोरिदम और डेटा संरचनाएँ: यह क्यों महत्वपूर्ण है
Rust एक आधुनिक सिस्टम प्रोग्रामिंग भाषा है, जो C++ के प्रदर्शन को स्वामित्व (ownership) प्रणाली द्वारा प्रदान की गई मेमोरी सुरक्षा के साथ जोड़ती है। Rust में एल्गोरिदम और डेटा संरचनाओं का अध्ययन करने से न केवल कंप्यूटर विज्ञान की शास्त्रीय अवधारणाओं में महारत हासिल होती है, बल्कि भाषा की अनूठी विशेषताओं: उधार (borrowing), जीवनकाल (lifetimes) और अमूर्तता पर शून्य लागत (zero-cost abstractions) की गहरी समझ भी विकसित होती है।
इस लेख में, हम तीन मौलिक डेटा संरचनाओं (स्टैक, क्यू और बाइनरी सर्च ट्री) और एक शास्त्रीय एल्गोरिदम (क्विक सॉर्ट) पर चर्चा करेंगे, जो Rust में कार्यान्वित हैं। प्रत्येक उदाहरण कोड और स्पष्टीकरण के साथ आता है जो आपको सिद्धांत से व्यवहार की ओर बढ़ने में मदद करेगा।
1. Rust में स्टैक: Vec के माध्यम से कार्यान्वयन
स्टैक एक डेटा संरचना है जो LIFO (लास्ट इन, फर्स्ट आउट) सिद्धांत पर काम करती है। Rust में, मानक Vec के आधार पर स्टैक को लागू करना सबसे आसान है, जो सभी आवश्यक विधियाँ प्रदान करता है: जोड़ने के लिए push और निकालने के लिए pop।
हालाँकि, Rust की सुरक्षा प्रदर्शित करने के लिए, हम स्पष्ट त्रुटि प्रबंधन के साथ एक रैपर बनाएंगे:
pub struct Stack<T> { elements: Vec<T>,}
impl<T> Stack<T> { pub fn new() -> Self { Stack { elements: Vec::new() } }
pub fn push(&mut self, item: T) { self.elements.push(item); }
pub fn pop(&mut self) -> Option<T> { self.elements.pop() }
pub fn peek(&self) -> Option<&T> { self.elements.last() }
pub fn is_empty(&self) -> bool { self.elements.is_empty() }}
fn main() { let mut stack = Stack::new(); stack.push(10); stack.push(20); stack.push(30);
println!("शीर्ष तत्व: {:?}", stack.peek()); // Some(30) println!("निकाल रहे हैं: {:?}", stack.pop()); // Some(30) println!("निकाल रहे हैं: {:?}", stack.pop()); // Some(20) println!("क्या स्टैक खाली है? {}", stack.is_empty()); // false}यहाँ Rust का मुख्य लाभ इसकी प्रकार प्रणाली है। यदि आप खाली स्टैक से कोई तत्व निकालने का प्रयास करते हैं, तो कंपाइलर आपको Option को संभालने के लिए बाध्य करेगा, जो C/C++ में सामान्य शून्य सूचक (null pointer) डीरेफरेंसिंग त्रुटियों को समाप्त करता है।
2. क्यू: सर्कुलर बफर के साथ मैन्युअल कार्यान्वयन
क्यू (FIFO) स्टैक की तुलना में अधिक जटिल है, क्योंकि इसे शुरुआत से कुशलतापूर्वक हटाने की आवश्यकता होती है। Rust गारंटीकृत प्रदर्शन के साथ कोई तैयार दो-तरफा क्यू प्रदान नहीं करता है, इसलिए हम एक निश्चित आकार का सर्कुलर बफर (circular buffer) लागू करेंगे।
यह unsafe कोड और कच्चे पॉइंटर्स को प्रदर्शित करने का एक उत्कृष्ट अवसर है, लेकिन हम Vec और इंडेक्स का उपयोग करके सुरक्षित क्षेत्र में रहेंगे:
pub struct Queue<T> { buffer: Vec<Option<T>>, head: usize, tail: usize, capacity: usize,}
impl<T> Queue<T> { pub fn new(capacity: usize) -> Self { let mut buffer = Vec::with_capacity(capacity); buffer.resize_with(capacity, || None); Queue { buffer, head: 0, tail: 0, capacity, } }
pub fn enqueue(&mut self, item: T) -> Result<(), &str> { if (self.tail + 1) % self.capacity == self.head { return Err("क्यू भरा हुआ है"); } self.buffer[self.tail] = Some(item); self.tail = (self.tail + 1) % self.capacity; Ok(()) }
pub fn dequeue(&mut self) -> Option<T> { if self.head == self.tail { return None; } let item = self.buffer[self.head].take(); self.head = (self.head + 1) % self.capacity; item }
pub fn is_empty(&self) -> bool { self.head == self.tail }}
fn main()