有没有一种简单直接的方法来在线性时间内在&[T]
和Vec<T>
中插入或替换多个元素,放置在Vec的中间或开头?
我只能找到std::vec::Vec::insert
, 但这只能在O(n)时间内插入一个单独的元素,所以显然我不能在循环中调用它。
我可以在那个索引处进行split_off
, 将新元素扩展到拆分的左半部分,然后将第二半部分扩展到第一半部分, 但还有更好的方法吗?
有没有一种简单直接的方法来在线性时间内在&[T]
和Vec<T>
中插入或替换多个元素,放置在Vec的中间或开头?
我只能找到std::vec::Vec::insert
, 但这只能在O(n)时间内插入一个单独的元素,所以显然我不能在循环中调用它。
我可以在那个索引处进行split_off
, 将新元素扩展到拆分的左半部分,然后将第二半部分扩展到第一半部分, 但还有更好的方法吗?
Vec::splice
已经可用,并允许在任何位置插入,包括完全前置:let mut vec = vec![1, 5];
let slice = &[2, 3, 4];
vec.splice(1..1, slice.iter().cloned());
println!("{:?}", vec); // [1, 2, 3, 4, 5]
文档声明:
Note 4: 当且仅当以下条件之一成立时,这是最优的选择:
- 尾部(指向区间后的元素)为空
- 或者
replace_with
返回的元素数量小于区间长度- 或其
size_hint()
的下限是准确的。
在这种情况下,切片迭代器的下限应该是准确的,因此它只需要执行一次内存移动。
splice
更强大一些,因为它允许你删除一段值(第一个参数),插入新值(第二个参数),并可选择获取旧值(调用结果)。
替换一组项
let mut vec = vec![0, 1, 5];
let slice = &[2, 3, 4];
vec.splice(..2, slice.iter().cloned());
println!("{:?}", vec); // [2, 3, 4, 5]
获取之前的值
let mut vec = vec![0, 1, 2, 3, 4];
let slice = &[9, 8, 7];
let old: Vec<_> = vec.splice(3.., slice.iter().cloned()).collect();
println!("{:?}", vec); // [0, 1, 2, 9, 8, 7]
println!("{:?}", old); // [3, 4]
好的,Vec接口中似乎没有适当的方法(据我所见)。但我们总是可以自己实现相同的功能。
当T是Copy时,可能最明显的方法是移动内存,就像这样:
fn push_all_at<T>(v: &mut Vec<T>, offset: usize, s: &[T]) where T: Copy {
match (v.len(), s.len()) {
(_, 0) => (),
(current_len, _) => {
v.reserve_exact(s.len());
unsafe {
v.set_len(current_len + s.len());
let to_move = current_len - offset;
let src = v.as_mut_ptr().offset(offset as isize);
if to_move > 0 {
let dst = src.offset(s.len() as isize);
std::ptr::copy_memory(dst, src, to_move);
}
std::ptr::copy_nonoverlapping_memory(src, s.as_ptr(), s.len());
}
},
}
}
如果T不是可复制的,但它实现了Clone,我们可以将给定的切片附加到Vec的末尾,并使用线性时间内的swap将其移动到所需位置:
fn push_all_at<T>(v: &mut Vec<T>, mut offset: usize, s: &[T]) where T: Clone + Default {
match (v.len(), s.len()) {
(_, 0) => (),
(0, _) => { v.push_all(s); },
(_, _) => {
assert!(offset <= v.len());
let pad = s.len() - ((v.len() - offset) % s.len());
v.extend(repeat(Default::default()).take(pad));
v.push_all(s);
let total = v.len();
while total - offset >= s.len() {
for i in 0 .. s.len() { v.swap(offset + i, total - s.len() + i); }
offset += s.len();
}
v.truncate(total - pad);
},
}
}
也许最好的选择是不对 Vec 进行任何修改。比如,如果你打算通过迭代器来访问结果,我们可以从我们的块中构建迭代器链:
let v: &[usize] = &[0, 1, 2];
let s: &[usize] = &[3, 4, 5, 6];
let offset = 2;
let chain = v.iter().take(offset).chain(s.iter()).chain(v.iter().skip(offset));
let result: Vec<_> = chain.collect();
println!("Result: {:?}", result);
split_at
而不是 skip
。 - Shepmaster我正在尝试在Rust中预先添加向量,并发现链接到此处的此关闭问题,(尽管这个问题是关于同时预先添加和插入以及效率的。我认为我的答案作为对那个更准确的问题的回答会更好,因为我无法证明效率),但以下代码帮助我预先添加(和相反)。[我相信其他两个答案更有效率,但是按照我的学习方式,我喜欢有可以剪切粘贴的答案和演示答案应用的示例。]
pub trait Unshift<T> { fn unshift(&mut self, s: &[T]) -> (); }
pub trait UnshiftVec<T> { fn unshift_vec(&mut self, s: Vec<T>) -> (); }
pub trait UnshiftMemoryHog<T> { fn unshift_memory_hog(&mut self, s: Vec<T>) -> (); }
pub trait Shift<T> { fn shift(&mut self) -> (); }
pub trait ShiftN<T> { fn shift_n(&mut self, s: usize) -> (); }
impl<T: std::clone::Clone> ShiftN<T> for Vec<T> {
fn shift_n(&mut self, s: usize) -> ()
// where
// T: std::clone::Clone,
{
self.drain(0..s);
}
}
impl<T: std::clone::Clone> Shift<T> for Vec<T> {
fn shift(&mut self) -> ()
// where
// T: std::clone::Clone,
{
self.drain(0..1);
}
}
impl<T: std::clone::Clone> Unshift<T> for Vec<T> {
fn unshift(&mut self, s: &[T]) -> ()
// where
// T: std::clone::Clone,
{
self.splice(0..0, s.to_vec());
}
}
impl<T: std::clone::Clone> UnshiftVec<T> for Vec<T> {
fn unshift_vec(&mut self, s: Vec<T>) -> ()
where
T: std::clone::Clone,
{
self.splice(0..0, s);
}
}
impl<T: std::clone::Clone> UnshiftMemoryHog<T> for Vec<T> {
fn unshift_memory_hog(&mut self, s: Vec<T>) -> ()
where
T: std::clone::Clone,
{
let mut tmp: Vec<_> = s.to_owned();
//let mut tmp: Vec<_> = s.clone(); // this also works for some data types
/*
let local_s: Vec<_> = self.clone(); // explicit clone()
tmp.extend(local_s); // to vec is possible
*/
tmp.extend(self.clone());
*self = tmp;
//*self = (*tmp).to_vec(); // Just because it compiles, doesn't make it right.
}
}
// this works for: v = unshift(v, &vec![8]);
// (If you don't want to impl Unshift for Vec<T>)
#[allow(dead_code)]
fn unshift_fn<T>(v: Vec<T>, s: &[T]) -> Vec<T>
where
T: Clone,
{
// create a mutable vec and fill it
// with a clone of the array that we want
// at the start of the vec.
let mut tmp: Vec<_> = s.to_owned();
// then we add the existing vector to the end
// of the temporary vector.
tmp.extend(v);
// return the tmp vec that is identitcal
// to unshift-ing the original vec.
tmp
}
/*
N.B. It is sometimes (often?) more memory efficient to reverse
the vector and use push/pop, rather than splice/drain;
Especially if you create your vectors in "stack order" to begin with.
*/
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3];
println!("Before push:\t {:?}", v);
v.push(0);
println!("After push:\t {:?}", v);
v.pop();
println!("popped:\t\t {:?}", v);
v.drain(0..1);
println!("drain(0..1)\t {:?}", v);
/*
// We could use a function
let c = v.clone();
v = unshift_fn(c, &vec![0]);
*/
v.splice(0..0, vec![0]);
println!("splice(0..0, vec![0]) {:?}", v);
v.shift_n(1);
println!("shift\t\t {:?}", v);
v.unshift_memory_hog(vec![8, 16, 31, 1]);
println!("MEMORY guzzler unshift {:?}", v);
//v.drain(0..3);
v.drain(0..=2);
println!("back to the start: {:?}", v);
v.unshift_vec(vec![0]);
println!("zerothed with unshift: {:?}", v);
let mut w = vec![4, 5, 6];
/*
let prepend_this = &[1, 2, 3];
w.unshift_vec(prepend_this.to_vec());
*/
w.unshift(&[1, 2, 3]);
assert_eq!(&w, &[1, 2, 3, 4, 5, 6]);
println!("{:?} == {:?}", &w, &[1, 2, 3, 4, 5, 6]);
}
unshift
中,我认为使用s.iter().cloned()
会更好,因为这样可以避免分配中间向量。 - Raph Leviensplice
,而已经在被接受的回答中提到了,那么这个回答的点在哪里呢?当你已经可以编写.splice(..0, v)
时,你真的需要编写.unshift_vec(v)
吗? - trent
split_off
的时间复杂度为O(n),就像insert
一样。据我所知,对于Vec来说,在任意位置添加元素的所有方法都是O(n)的。更高效的解决“将索引i设置为此项,将j+1设置为j≥i处的项”的方法依赖于BTreeMap,其时间复杂度为O(log n)。 - Thaddee Tyl