#rust #linked-list
#Ржавчина #связанный список
Вопрос:
Я хочу создать средство проверки палиндромов в Rust, ограничение составляет O (n) времени с 0 (1) дополнительным пространством. Мои идеи:
- Получить середину связанного списка.
- Переверните вторую половину связанного списка.
- Проверьте, идентичны ли первая и вторая половины.
fn isListPalindrome(l: ListNode<i32>) -> bool {
let mut fastPointer = amp;l;
let mut slowPointer = amp;l;
let mut midIndex = 0;
// here first step
loop {
match fastPointer {
Some(n) => {
if let Some(nn) = amp;n.next {
fastPointer = amp;nn.next;
} else {
break;
}
midIndex = 1;
}
None => break,
}
match slowPointer {
Some(n) => slowPointer = amp;n.next,
None => break,
}
}
if let Some(_fp) = amp;fastPointer {
slowPointer = amp;fastPointer;
}
// here is second step to reverse list
let mut prev = None;
while let Some(n) = slowPointer {
let next = amp;n.next;
n.next = prev.take();
println!("{:?}", n);
prev = amp;n;
slowPointer = next;
}
true
}
Проблема возникла, когда я хочу попробовать отменить вторую половину связанного списка. Ошибка компиляции в коде prev = amp;n;
ошибка :
примечание: ожидаемая ссылка
amp;std::option::Option<std::boxed::Box<List<i32>>>
найдена ссылкаamp;amp;std::boxed::Box<List<i32>>
Как изменить список во второй половине связанного списка для проверки палиндрома выше?
Ответ №1:
У вас довольно много проблем в вашем коде, не похоже, что у вас есть очень хорошее представление о том, какими типами вы хотите видеть каждую переменную. Когда вы объявляете let mut prev = None;
, вы делаете его принадлежащим значению ( Option<T>
для некоторых T
), но затем вы присваиваете ему prev = amp;n
, что делает его amp;T
для некоторых T
. Тип принадлежащего значения и заимствованного значения не пересекаются, поэтому эти две строки сами по себе конфликтуют друг с другом. Я создал реализацию обратного алгоритма ниже, и я постарался, чтобы определения типов были как можно более похожи на ваши. Было бы полезно, если бы вы включили их в свой вопрос.
use std::mem::swap;
#[derive(Debug)]
pub struct List {
pub val: i32,
pub next: Option<Box<List>>,
}
type ListNode = Option<Box<List>>;
fn reverse(head: amp;mut ListNode) {
let tail = amp;mut None;
while head.is_some() {
swap(amp;mut head.as_mut().unwrap().next, tail);
swap(head, tail);
}
swap(head, tail);
}
fn main() {
let mut list = Some(Box::new(List {
val: 0,
next: Some(Box::new(List {
val: 1,
next: Some(Box::new(List {
val: 2,
next: Some(Box::new(List {
val: 3,
next: None,
})),
})),
})),
}));
println!("{:?}", list);
reverse(amp;mut list);
println!("{:?}", list);
}
Принцип работы алгоритма заключается в том, что на каждой итерации он превращается
head=(H1, H2...), tail=(T...)
в
head=(H2...), tail=(H1, T...)
сначала поменяв H2...
местами с T...
, а затем поменяв head
местами и tail
. В конечном итоге head
это приводит к тому, что он становится пустым и tail
содержит обратный список. Затем выполняется одна последняя замена, которая head
содержит список.
Если вы хотите отладить свой собственный код, первым шагом будет убедиться, что вы понимаете разницу между T
, amp;T
, и amp;mut T
. T
является принадлежащим значением, что означает, что оно будет освобождено в конце его области видимости. amp;T
это неизменяемая ссылка на него, которая похожа на указатель в C, за исключением того, что вы не можете изменить значение, на которое он указывает. У вас может быть столько из них, сколько вы хотите, но вы ничего не можете сделать с принадлежащим значением, пока не закончится заимствование. amp;mut T
это изменяемое заимствование T
, которое снова похоже на указатель, но на этот раз оно позволит вам изменять местоположение, на которое оно указывает. Вы можете иметь один из них одновременно. В контексте связанного списка вам нужно подумать о том, какие типы должна иметь каждая переменная, потому что вы не можете поместить a amp;mut T
или a amp;T
как next
в список, вы можете поместить туда только собственное значение. Вы можете получить принадлежащее значение, используя take()
, swap()
, и т.д. replace()
Возможно, я неправильно понимаю ваш код, и вы можете попробовать что-то другое, но вам почти наверняка не понадобятся какие-либо неизменяемые заимствования ( amp;T
) в вашем обратном алгоритме.
Комментарии:
1. могу ли я выполнить реверс без amp;mut в переменном параметре? потому что в родительской функции l (l: ListNode<i32>) не является изменяемым