如何对Vec中按切片划分的序列进行字典序排序?
原地排序Vec中固定大小的分块元素(编译期未知块大小)
你之前的思路里,Chunk结构体是对原切片的引用,排序Chunk对象只会改变引用的顺序,完全不会影响底层的Vec数据——这就是问题所在。要实现对原数据的排序,不能直接排序引用,得通过排序块的索引来间接调整原数据的位置,或者用临时存储重新构建排序后的Vec。
方法一:原地置换(内存高效)
这种方法通过处理置换环来原地调整块的位置,不需要额外分配与原数据等大的内存,适合大数据量场景:
#[derive(PartialEq, Eq)] struct Chunk<'a> { slice: &'a [usize], } impl<'a> PartialOrd for Chunk<'a> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { self.slice.partial_cmp(other.slice) } } impl<'a> Ord for Chunk<'a> { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.slice.cmp(other.slice) } } impl<'a> Chunk<'a> { /// 将chunks视为长度为chunk_size的切片集合,原地排序该集合,保持每个切片完整。若chunks长度不是chunk_size的倍数会panic。 pub fn sort_slice_of_chunks(chunks: &mut Vec<usize>, chunk_size: usize) { let n_slices = chunks.len() / chunk_size; assert_eq!(chunks.len(), n_slices * chunk_size, "chunks长度必须是chunk_size的整数倍"); // 生成所有块的索引,并按块的字典序排序索引 let mut indices: Vec<_> = (0..n_slices).collect(); indices.sort_unstable_by(|&i, &j| { let chunk_i = &chunks[i * chunk_size..(i+1)*chunk_size]; let chunk_j = &chunks[j * chunk_size..(j+1)*chunk_size]; chunk_i.cmp(chunk_j) }); // 通过置换环原地调整块的位置 let mut visited = vec![false; n_slices]; for i in 0..n_slices { if visited[i] { continue; } let mut current = i; while !visited[current] { visited[current] = true; let target = indices[current]; if current != target { // 交换两个块的全部元素 chunks.swap_ranges( current * chunk_size..(current+1)*chunk_size, target * chunk_size..(target+1)*chunk_size, ); } current = target; } } } } // 测试示例 fn main() { let mut data = vec![1, 2, 4, 1, 2, 3, 2, 2, 1]; Chunk::sort_slice_of_chunks(&mut data, 3); assert_eq!(data, vec![1, 2, 3, 1, 2, 4, 2, 2, 1]); println!("排序成功: {:?}", data); }
方法二:临时存储(代码简洁)
如果内存不是瓶颈,这种方法更简单:先按排序后的索引收集所有块,再替换原Vec:
impl<'a> Chunk<'a> { /// 简洁版实现:使用临时存储,代码更易读但会分配额外内存 pub fn sort_slice_of_chunks_simple(chunks: &mut Vec<usize>, chunk_size: usize) { let n_slices = chunks.len() / chunk_size; assert_eq!(chunks.len(), n_slices * chunk_size, "chunks长度必须是chunk_size的整数倍"); let mut indices: Vec<_> = (0..n_slices).collect(); indices.sort_unstable_by(|&i, &j| { let chunk_i = &chunks[i * chunk_size..(i+1)*chunk_size]; let chunk_j = &chunks[j * chunk_size..(j+1)*chunk_size]; chunk_i.cmp(chunk_j) }); let mut sorted = Vec::with_capacity(chunks.len()); for &idx in &indices { sorted.extend_from_slice(&chunks[idx * chunk_size..(idx+1)*chunk_size]); } *chunks = sorted; } }
关键说明
- 不能直接通过排序
Chunk引用修改原数据:因为Chunk只是原切片的视图,排序视图不会改变原数据的物理位置。 - 两种方法的取舍:原地置换法内存效率更高,适合处理大体积数据;临时存储法代码更简洁,开发成本低,适合中小规模数据。
内容的提问来源于stack exchange,提问作者Erik P.
相关产品推荐
相关产品推荐

