数据结构_链表实验

上一篇是根据rust语言圣经的指导写的迭代器版本,这次我想用一种更贴近过程式的风格

实验一:城市链表

城市链表:
    将若干城市的信息,存入一个带头结点的链表中。结点中的城市信息包括:城市名和标进行有关查找、插入、删除、更新等操作。城市的位置坐标。要求能够利用城市名和位置
基本要求:
    ①给定一个城市名,返回其位置坐标:
    ②给定一个位置坐标P和一个距离 D,返回所有与 P的距离小于等于 D 的城市。
测试数据:
    依据软件工程的测试技术自己确定,注意则试边界数据。

首先是定义单链表的结构,不需要实现泛型:

type Link= Option<Rc<RefCell<Node>>>;

struct Head{
    first : Link,
}

#[derive(Clone)]
struct Node{
    city_name : String,
    p_x : f64,
    p_y : f64,
    next : Link,
}

由于不能保证字符串可以在编译期确定,所以这里不能用&str,虽说实验没做要求

先写几个简单的:

impl Head{
    fn new() -> Head{
        let new_list= Head{first: None };
        new_list
    }
    fn insert(&mut self, name: String, x :c_double, y:c_double){
        let mut new_node = Node::new(name, x, y);
        new_node.next = self.first.take();
        self.first = Some(Rc::new(RefCell::new(new_node)));
    }
}

impl Node {
    fn new(name: String, x:c_double, y:c_double) -> Node {
        let new_node = Node{city_name : name, p_x: x, p_y: y, next : Option::None};
        new_node
    }
}

mod test{
    use super::Head;
    #[test]
    fn test_insert(){
        let mut head = Head::new();
        head.insert("beijing".to_string(), 0.0, 0.0);
        head.insert("tianjin".to_string(),4.0,4.5);
    }
}

take可以将option里的东西拿出来,移交所有权并替换为None,用在这里再合适不过。

接下来就要面临所有权的问题:

fn find_with_name(&self, name:String) -> (f64, f64){
    let mut current = &self.first;
    while let Some(node) = current {
        if name == node.borrow().city_name{
            return (node.borrow().p_x, node.borrow().p_y);
        }else {
            current = node.borrow().next;
        }
    }
    (-1f64, -1f64)
}

问题出在current = node.borrow().next;,current明明是一个Link的不可变引用,怎么能把Link直接给它

如果变成current = &node.borrow().next;又有了新的问题,node马上就要离开作用域被释放了,current怎么还能持有其引用

那么问题来到了current的声明let mut current = &self.first;,略作调整 let mut current = self.first.clone();这样current就不再是引用,而拷贝的实际是个指针,没有什么额外开销

fn find_with_name(&self, name:String) -> (f64, f64){
    let mut current = self.first.clone();
    while let Some(node) = current {
        if name == node.borrow().city_name{
            return (node.borrow().p_x, node.borrow().p_y);
        }else {
            current = node.borrow().next.clone();
        }
    }
    (-1f64, -1f64)
}

#[test]
fn test_find_with_name(){
    let mut head = Head::new();
    head.insert("beijing".to_string(),0.0,0.0);
    head.insert("tianjin".to_string(),4.0,4.5);
    assert_eq!(head.find_with_name("beijing".to_string()), (0.0, 0.0));
    assert_eq!(head.find_with_name("tianjin".to_string()).0, 4.0);
    assert_eq!(head.find_with_name("tianjin".to_string()).1, 4.5);
    assert_eq!(head.find_with_name("foobar".to_string()),(-1.0,-1.0));
}

两次clone指针成功解决了所有权问题,测试也是顺利通过

接下来根据距离筛城市,如法炮制:

fn find_with_distance(&self,position:(f64,f64), distance:f64) -> HashMap<String,bool> {
    let mut names = HashMap::new();
    let mut current = self.first.clone();
    while let Some(node) = current {
        if Self::calculate_distance(position,(node.borrow().p_x,node.borrow().p_y)) <= distance{
            names.insert(node.borrow().city_name.clone(),true);
        }
        current = node.borrow().next.clone();
    }
    names
}

fn calculate_distance(center :(f64, f64), city_position:(f64, f64)) -> f64{
    let (x1, y1) = center;
    let (x2, y2) = city_position;

    ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt()
}

#[test]
fn test_find_with_distance(){
    let mut head = Head::new();
    head.insert("beijing".to_string(),0.0,0.0);
    head.insert("tianjin".to_string(),4.0,4.5);
    head.insert("xianggang".to_string(),10.0,10.0);
    let test1 = head.find_with_distance((0.0,4.0),5.0);
    assert_eq!(test1.get("beijing"), Some(true).as_ref());
    assert_eq!(test1.get("tianjin"), Some(true).as_ref());
    assert_eq!(head.find_with_distance((0.0,4.0),0.0).get("beijing"), None);
}

接下来是删除,事情没有那么简单,需要根据下一项的值确定具体操作,先写一下第一版:

fn remove_with_name(&mut self, name: String) {
    let mut current = self.first.clone();

    while let Some(node) = current {
        let next = node.borrow().next.clone();

        if next.unwrap().borrow().city_name == name{
            node.borrow().next = next.unwrap().borrow().next.clone();
        }else {
            current = next;
        }
    }
}

以上代码写得一点都不锈,不仅到处是红红的报错,而且用到了unwrap,这意味着如果值为None会直接panic,同时实际上跳过了first节点的检查

fn remove_with_name(&mut self, name: String) {
    let current = self.first.clone();
    if let None = current {
        return;
    }
    if let Some(node) = current {
        if node.borrow().city_name == name {
            self.first = node.borrow_mut().next.take(); // 删除头结点
            return;
        }
    }
    
    let mut current = self.first.clone();
    while let Some(node) = current {
        let mut next = node.borrow().next.clone();

        if let Some(next_node) = next {
            if name == next_node.borrow().city_name{
                node.borrow_mut().next = next_node.borrow_mut().next.take();
                return;
            }else {
                current = node.borrow().next.clone();
            }
        }else {
            break;
        }
    }
}

#[test]
fn test_remove_with_name(){
    let mut head = Head::new();
    head.insert("beijing".to_string(),0.0,0.0);
    head.insert("tianjin".to_string(),4.0,4.5);
    head.insert("xianggang".to_string(),10.0,10.0);
    head.remove_with_name("beijing".to_string());
    head.remove_with_name("xianggang".to_string());
    assert_eq!(head.find_with_name("beijing".to_string()) ,(-1.0,-1.0));
    assert_eq!(head.find_with_name("tianjin".to_string()), (4.0, 4.5));
    assert_eq!(head.find_with_name("xianggang".to_string()), (-1.0,-1.0));
}

这一版至少可以顺利通过测试了

最后一部分,根据name修改坐标:

fn update_with_name(&mut self, name: String, p_x: f64, p_y: f64) {
    let mut current = self.first.clone();
    while let Some(node) = current {
        if name == node.borrow().city_name{
            node.borrow_mut().p_x = p_x;
            node.borrow_mut().p_y = p_y;
            return;
        }else {
            current = node.borrow().next.clone();
        }
    }
}

#[test]
fn test_update_with_name(){
    let mut head = Head::new();
    head.insert("beijing".to_string(),0.0,0.0);
    head.insert("tianjin".to_string(),4.0,4.5);
    head.update_with_name("tianjin".to_string(), 1.0,2.5);
    assert_eq!(head.find_with_name("tianjin".to_string()), (1.0, 2.5));
}

实验二:约瑟夫环

[问题描述
约瑟夫(Joseph)问题的一种描述是:编号为 1、2、…、n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值 m,从第1个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m 的人出列,将 TA 的密码作为新的 m 值,从 TA在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
[基本要求]
利用单向循环链表存储结构模拟此过程,按照出列的顺序打印出各人的编号
[测试数据]
m 的初值为 20:密码:3,1,7,2,4,8,4(正确的结果应为6,1,4,7,2,3,5)
[实现提示]
程序运行后首先要求用户指定初始报数上限值,然后读取各人的密码。设n≤30。

依旧是先定义链表结构:

type Link = Option<Rc<RefCell<Node>>>;

struct Node{
    index : usize,
    key : usize,
    next: Link,
}

index记录编号(可省略),key记录密码

循环链表就需要循环引用,在rust里并不是一件容易实现的事,比如直接来:

fn new(num : usize) -> Node {
    let mut new_node = Node{index : num, key : 0, next: None};
    new_node.next = Option::Some(Rc::new(RefCell::new(new_node)));
    new_node
}

new_node的所有权已经转移到了next字段,怎么还能返回呢?利用智能指针的封装可以避免这个问题,同时为了后续操作方便,增加一个头节点,保留循环链表的尾指针:

use std::rc::Rc;
use std::cell::RefCell;

struct Node {
    index : usize,
    key : usize,
    next: Option<Rc<RefCell<Node>>>,
}

struct CircularList {
    len : usize,
    tail: Option<Rc<RefCell<Node>>>,
}

impl CircularList {
    fn new() -> Self {
        CircularList {
            len : 0,
            tail: None,
        }
    }

    // 创建单节点循环链表
    fn new_single_node(key: usize) -> Self {
        let node = Rc::new(RefCell::new(Node {
            index: 1,
            key,
            next: None,
        }));

        // 关键:让节点的 next 指向自己
        node.borrow_mut().next = Some(node.clone());

        CircularList {
            len : 1,
            tail: Some(node),
        }
    }

    // 遍历链表
    fn traverse(&self) {
        if let Some(head) = &self.tail {
            let mut current = head.clone();
            loop {
                println!("Data: {}", current.borrow().index);
                let next = current.borrow().next.clone().unwrap();
                current = next;
            }
        }
    }
}

fn main() {

}
#[cfg(test)]
mod tests {
    use crate::CircularList;

    #[test]
    fn test_new() {
        let mut list = CircularList::new_single_node(1);

        // 遍历链表
        list.traverse(); // 输出死循环的1
    }
}

接下来实现push操作:

fn push_back(&mut self, key: usize) {
    self.len += 1;
    let new_node = Rc::new(RefCell::new(Node {
        index : self.len,
        key,
        next: None,
    }));

    match &self.tail {
        None => {
            panic!("should new_single_node() first");
        }
        Some(tail) => {
            let head = tail.borrow().next.clone().unwrap();
            new_node.borrow_mut().next = Some(head);
            tail.borrow_mut().next = Some(new_node.clone());
            self.tail = Some(new_node);
        }
    }
}

#[test]
fn test_push_back() {
    let mut list = CircularList::new_single_node(1);
    list.push_back(2);
    list.push_back(3);
    list.push_back(4);
    list.traverse();
}

接下来做删除,返回index和key的元组,分为两个部分,移动尾指针到指定位置和删除尾指针节点

fn m0ve(&mut  self, count: usize){
    if self.tail.is_none() || self.len == 0 {
        return;
    }

    let mut current = self.tail.clone().unwrap();

    for _ in 0..count {
        let next = current.borrow().next.clone().unwrap();
        current = next;
    }

    self.tail = Some(current);
}

fn delete(&mut self, count: usize) -> (usize, usize) {
    if self.tail.is_none() || self.len == 0 {
        panic!("Cannot delete from empty list");
    }
    // 先移动尾指针到指定位置
    self.m0ve(count);

    let tail_node = self.tail.clone().unwrap();

    if self.len == 1 {
        // 如果只有一个节点,删除后链表为空
        let deleted_index = tail_node.borrow().index;
        let deleted_key = tail_node.borrow().key;
        self.tail = None;
        self.len = 0;
        return (deleted_index, deleted_key);
    }

    // 删除尾节点的下一个节点(即头节点)
    let head_node = tail_node.borrow().next.clone().unwrap();
    let deleted_index = head_node.borrow().index;
    let deleted_key = head_node.borrow().key;

    // 获取头节点的下一个节点
    let new_head = head_node.borrow().next.clone().unwrap();

    // 让尾节点直接指向新的头节点,跳过要删除的节点
    tail_node.borrow_mut().next = Some(new_head);

    self.len -= 1;

    (deleted_index, deleted_key)
}

fn test_move_and_delete() {
    let mut list = CircularList::new_single_node(1);
    list.push_back(2);
    list.push_back(3);
    list.push_back(4);

    assert_eq!(list.delete(3), (4,4));
    //list.traverse();
}

move并不是一个可用的函数名,所以这里用了m0ve

准备完毕,接下来就可以写约瑟夫环的运行函数了:

fn run(keys : Vec<usize>, m:usize) {
    //构建环
    let len = keys.len();
    let mut bind = CircularList::new_single_node(keys[0]);
    for i in 1..len {
        bind.push_back(keys[i]);
    }
    //输出
    let mut num = m;
    for _ in 0..len {
        let (index, new_num)= bind.delete(num - 1);//退一个
        print!("{} ",index);
        num = new_num;
    }
}

#[test]
fn test_run() {
    let test1 : Vec<usize> = vec![3,1,7,2,4,8,4];
    CircularList::run(test1.clone(), 20);
}

观察输出:

running 4 tests
6 1 4 7 2 3 5 test tests::test_move_and_delete ... ok
test tests::test_new ... ok
test tests::test_run ... ok
test tests::test_push_back ... ok

完美