142 lines
3.6 KiB
Rust
142 lines
3.6 KiB
Rust
|
use std::{cell::RefCell, rc::Rc};
|
||
|
|
||
|
/// Definition of an element of the list
|
||
|
///
|
||
|
/// Contain one stored item and the previous/next element of the list
|
||
|
struct ListNode<T> {
|
||
|
item: T,
|
||
|
next: Link<T>,
|
||
|
prev: Link<T>,
|
||
|
}
|
||
|
|
||
|
impl<T> ListNode<T> {
|
||
|
fn new(item: T) -> Self {
|
||
|
Self {
|
||
|
item,
|
||
|
next: None,
|
||
|
prev: None,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Link<T> = Option<Rc<RefCell<ListNode<T>>>>;
|
||
|
|
||
|
/// Defintion of the generic linked list
|
||
|
#[derive(Default)]
|
||
|
pub struct DoublyLinkedList<T> {
|
||
|
head: Link<T>,
|
||
|
tail: Link<T>,
|
||
|
size: usize,
|
||
|
}
|
||
|
|
||
|
impl<T> DoublyLinkedList<T> {
|
||
|
|
||
|
pub fn new() -> Self {
|
||
|
Self {
|
||
|
head: None,
|
||
|
tail: None,
|
||
|
size: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn is_empty(&self) -> bool {
|
||
|
self.len() == 0
|
||
|
}
|
||
|
|
||
|
pub fn len(&self) -> usize {
|
||
|
self.size
|
||
|
}
|
||
|
|
||
|
/// Add the item at the end of the list
|
||
|
pub fn push_back(&mut self, item: T) {
|
||
|
let node = Rc::new(RefCell::new(ListNode::new(item)));
|
||
|
if let Some(prev_tail) = self.tail.take() {
|
||
|
prev_tail.borrow_mut().next = Some(Rc::clone(&node));
|
||
|
node.borrow_mut().prev = Some(prev_tail);
|
||
|
self.tail = Some(node);
|
||
|
self.size += 1;
|
||
|
} else {
|
||
|
self.head = Some(Rc::clone(&node));
|
||
|
self.tail = Some(node);
|
||
|
self.size = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Add the item at the start of the list
|
||
|
pub fn push_front(&mut self, item: T) {
|
||
|
let node = Rc::new(RefCell::new(ListNode::new(item)));
|
||
|
if let Some(prev_head) = self.head.take() {
|
||
|
prev_head.borrow_mut().prev = Some(Rc::clone(&node));
|
||
|
node.borrow_mut().next = Some(prev_head);
|
||
|
self.head = Some(node);
|
||
|
self.size += 1;
|
||
|
} else {
|
||
|
self.head = Some(Rc::clone(&node));
|
||
|
self.tail = Some(node);
|
||
|
self.size = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Remove the item at the end of the list
|
||
|
pub fn pop_back(&mut self) -> Option<T> {
|
||
|
self.tail.take().map(|prev_tail| {
|
||
|
self.size -= 1;
|
||
|
match prev_tail.borrow_mut().prev.take() {
|
||
|
Some(node) => {
|
||
|
node.borrow_mut().next = None;
|
||
|
self.tail = Some(node);
|
||
|
}
|
||
|
None => {
|
||
|
self.head.take();
|
||
|
}
|
||
|
}
|
||
|
Rc::try_unwrap(prev_tail).ok().unwrap().into_inner().item
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/// Remove the item at the start of the list
|
||
|
pub fn pop_front(&mut self) -> Option<T> {
|
||
|
self.head.take().map(|prev_head| {
|
||
|
self.size -= 1;
|
||
|
match prev_head.borrow_mut().next.take() {
|
||
|
Some(node) => {
|
||
|
node.borrow_mut().prev = None;
|
||
|
self.head = Some(node);
|
||
|
}
|
||
|
None => {
|
||
|
self.tail.take();
|
||
|
}
|
||
|
}
|
||
|
Rc::try_unwrap(prev_head).ok().unwrap().into_inner().item
|
||
|
})
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
impl<T> Drop for DoublyLinkedList<T> {
|
||
|
/// list destructor, safely desallocate smart pointer Rc
|
||
|
fn drop(&mut self) {
|
||
|
while let Some(node) = self.head.take() {
|
||
|
let _ = node.borrow_mut().prev.take();
|
||
|
self.head = node.borrow_mut().next.take();
|
||
|
}
|
||
|
self.tail.take();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod test {
|
||
|
|
||
|
use super::DoublyLinkedList;
|
||
|
|
||
|
#[test]
|
||
|
fn test_list_push() {
|
||
|
let mut list = DoublyLinkedList::new();
|
||
|
list.push_back(5);
|
||
|
list.push_front(45);
|
||
|
assert_eq!(list.pop_front().unwrap(), 45);
|
||
|
assert_eq!(list.pop_front().unwrap(), 5);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|