在单链表中实现add_all_at_index

时间:2018-01-30 16:02:58

标签: rust

我正在实施一个单链表。我想添加一个方法add_all_at_index,它将获取一个新列表和一个索引,在指定的索引处插入新列表,并在新列表后重新排列当前列表的尾部。

假设当前列表是[1,2,3,4,5]。使用列表add_all_at_index2位置拨打[8,9,10]会产生[1,2,8,9,10,3,4,5]

我特别难以在新列表之后分配列表的旧尾部。我不知道如何将[3,4,5]附加到列表中值为10的节点上。

我想要实现的算法是

  • 将(index - 1)节点的当前下一个节点保存在名为current_next
  • 的变量中
  • 将(index - 1)的下一个节点设置为列表的头部
  • 迭代到新添加的列表的最后一个,并将其设置为current_next旁边的。

我无法完成最后一步。以下是我提出的代码:

use std::fmt::*;

fn main() {
    let list: List<i32> = List::new();
}

#[derive(PartialEq, Debug)]
pub struct Node<T: Debug> {
    pub element: T,
    pub next: Option<Box<Node<T>>>,
}

#[derive(PartialEq, Debug)]
pub struct List<T: Debug> {
    pub head: Option<Node<T>>,
}

impl<T: Debug> List<T> {
    pub fn new() -> Self {
        List { head: None }
    }

    pub fn add_all_at_index(&mut self, list_to_add: List<T>, index: usize) {
        if index > 0 {
            let nth_node = self.get_nth_node_mut(index).take(); // Gets a mutable reference to node at index
            nth_node.map(|node| {
                let current_next = node.next.take(); // I store a reference to the next of nth node,
                node.next = list_to_add.head.map(|node| Box::new(node));

                // The 3rd step in the algorithm I mentioned above.
                let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
                last_node.map(|node| node.next = current_next);
            });
        } else {
            self.head = list_to_add.head
        }
    }

    fn get_nth_node_mut(&mut self, n: usize) -> Option<&mut Node<T>> {
        let mut nth_node = self.head.as_mut();
        for _ in 0..n {
            nth_node = match nth_node {
                None => return None,
                Some(node) => node.next.as_mut().map(|node| &mut **node),
            }
        }
        nth_node
    }

    pub fn length(&self) -> usize {
        let mut count = 0;
        let mut current_node = self.head.as_ref();
        while let Some(node) = current_node {
            count = count + 1;
            current_node = node.next.as_ref().map(|node| &**node)
        }
        count
    }
}

我得到的错误是

warning: unused variable: `list`
 --> src/main.rs:4:9
  |
4 |     let list: List<i32> = List::new();
  |         ^^^^
  |
  = note: #[warn(unused_variables)] on by default
  = note: to avoid this warning, consider using `_list` instead

error[E0500]: closure requires unique access to `self` but `*self` is already borrowed
  --> src/main.rs:26:26
   |
25 |             let nth_node = self.get_nth_node_mut(index).take(); // Gets a mutable reference to node at index
   |                            ---- borrow occurs here
26 |             nth_node.map(|node| {
   |                          ^^^^^^ closure construction occurs here
...
31 |                 let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
   |                                 ---- borrow occurs due to use of `self` in closure
...
34 |         } else {
   |         - borrow ends here

error[E0502]: cannot borrow `**self` as immutable because it is also borrowed as mutable
  --> src/main.rs:31:55
   |
31 |                 let last_node = self.get_nth_node_mut(self.length()); // This line does not compile. Getting multiple errors in this line
   |                                 ----                  ^^^^ immutable borrow occurs here
   |                                 |
   |                                 mutable borrow occurs here
32 |                 last_node.map(|node| node.next = current_next);
33 |             });
   |             - mutable borrow ends here

这是否是实施add_all_at_index的正确方法?

我甚至尝试实现一个迭代器,它返回一个节点的可变引用,但我也无法做到这一点。我在https://gist.github.com/hardvain/32fca033bb61a5e3bf8bbeeb32fbbd5e

的要点中粘贴了整个代码

1 个答案:

答案 0 :(得分:2)

首先,解决方案:

pub fn add_all_at_index(&mut self, list_to_add: List<T>, index: usize) {
    if index > 0 {
        let tail = {
            let nth_node = self.get_nth_node_mut(index).take();
            nth_node.map(|node| {
                let current_next = node.next.take();
                node.next = list_to_add.head.map(|node| Box::new(node));
                current_next
            })
        };

        if let Some(current_next) = tail {
            let n = self.length();
            let last_node = self.get_nth_node_mut(n);
            last_node.map(|node| node.next = current_next);
        }
    } else {
        self.head = list_to_add.head
    }
}

丑陋,对吗?对。要实现这一目标需要进行一些更改:

  • 我在传递给map的闭包之外移动了第3步(重新附加列表的尾部),以便nth_node(借用self)不会当你试图再次借用self以获得长度时,你仍然活着。

  • 因此我必须保存current_next,所以我让闭包返回它,并将map的结果存储在一个名为tail的新变量中。因此tailOption<Box<Node<T>>>

  • 我将尾部重新附加的部分包裹在if let中以解构tail并让current_next退出。

  • 然后,我将self.get_nth_node_mut(self.length())分成两个语句来解决剩余的借用错误。

一些后续建议:

  • 使用.map()进行副作用,然后忽略返回值是不明显的。使用if letOption

  • 的内容上运行代码
  • 您忽略了所有None个案例。如果您尝试使用超出范围的add_all_at_index来致电index,则self没有任何结果,list_to_add就会丢失。该函数应该返回某种Result,或者bool

  • .take()用于从Option<T>中获取&mut Option<T>。它对Option<&mut T>没有任何用处。

  • |node| Box::new(node)只是Box::new

  • 因为第一个节点是盒装的,但其他节点都没有,所以你必须编写很多特殊的案例代码,并且节点和列表之间不是透明的转换。

  • get_nth_node_mut的实现中使用add_all_at_index几乎会强制您遍历列表两次。由于它是在List而非Node上实施的,因此您无法轻松获得对列表最后一个元素的引用,因此您最终会调用length() (使遍历的总数为3)然后再次get_nth_node_mut来挖掘最后一个元素。

通过仔细的界面设计可以减轻一些丑陋 - 例如,如果List具有split_at_index方法,这种方法会变得更加清晰 - 但其中一些方法只是因为链接列表很难看。 特别在Rust中丑陋,因为语言禁止共享可变引用,甚至是暂时的。你必须使用unsafe在Rust中编写许多链表操作,就像在C中一样。

如果您还没有,请阅读Learning Rust With Entirely Too Many Linked Lists。本书解决了当您尝试在Rust中实现链接列表时出现的许多细微之处。