Я перевожу это следующая перестановка алгоритм в C ++:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
{
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true) {
BidirIt i1, i2;
i1 = i;
if (*--i < *i1) {
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
}
if (i == first) {
std::reverse(first, last);
return false;
}
}
}
В этот код Rust:
struct Solution;
impl Solution {
pub fn next_permutation(nums: &mut Vec<i32>) -> bool{
if nums.len() < 2 {
return false;
}
let first = 0;
let last = nums.len();
let mut i = last - 1;
loop {
let i1 = i;
i -= 1;
if nums[i] < nums[i1] {
let mut i2 = last;
loop {
i2 -= 1;
if nums[i] < nums[i2] {
break;
}
}
let tmp = nums[i];
nums[i] = nums[i2];
nums[i2] = tmp;
nums[i1..last].reverse();
return true;
}
if i == first {
nums[first..last].reverse();
return false;
}
}
}
}
Хотя это работает, я думаю, что это не очень красиво. Я новичок в ржавчине, любые улучшения приветствуются!
1 ответ
В произвольном порядке:
Создавайте тестовые примеры, чтобы быть уверенным при рефакторинге.
Не создавайте пустые структуры (
Solution
) просто чтобы дать им функции.next_permutation
отлично подходит как бесплатная функция. если ты имеют чтобы создать плохой API, удовлетворяющий LeetCode или что-то еще, напишите хорошо Сначала API, а затем оберните его. Если требуемый API настолько плох, что вы не можете обернуть его хорошим API, это, вероятно, пустая трата времени.Использовать
rustfmt
.Принимать
&mut [T]
вместо&mut Vec<T>
когда менять размер не нужно.let first = 0; let last = nums.len();
Не давайте ненужных символических имен вещам, которые не используются символически.
0
гораздо более очевидно индекс первого элемента в срезе, чемfirst
. Кроме того, вам даже не нужно указывать границы среза при индексировании с помощью[..]
; они подразумеваются. Так что они вам нужны даже меньше, чем вы, возможно, думали.i
,i1
,i2
бессмысленны. Как насчет описательных имен?Я предпочитаю ставить каждый
loop
в блоке с его переменными состояния, чтобы ограничить область изменчивости.let i1 = i;
Аналогично примечанию на
first
а такжеlast
выше, не создавайте переменные, которые можно тривиально вычислить из других переменных. Отношение междуi
а такжеi + 1
намного очевиднее, чемi
а такжеi1
.if nums[i] < nums[i + 1] { ... }
Почему это тело вложено во внешний цикл? Алгоритм довольно легко разбивается на четыре этапа:
- Получить индекс самой правой восходящей пары значений
- Получить индекс самого правого значения больше, чем первый элемент этой пары
- Поменяйте местами значения этих двух индексов
- Переверните срез, начиная сразу после первого индекса. У вас есть 2, 3 и 4, вложенные внутрь 1, что делает алгоритм более сложным, чем он есть на самом деле.
let tmp = nums[i]; nums[i] = nums[i2]; nums[i2] = tmp;
Использовать
nums.swap(i, i2)
вместо.
Применяемый
pub fn next_permutation(nums: &mut [i32]) -> bool {
if nums.len() < 2 {
return false;
}
let last_ascending_pair = {
let mut i = nums.len() - 1;
loop {
i -= 1;
if nums[i] < nums[i + 1] {
break i;
}
if i == 0 {
nums.reverse();
return false;
}
}
};
let last_greater_than_n = {
let mut i = nums.len();
loop {
i -= 1;
if nums[last_ascending_pair] < nums[i] {
break i;
}
}
};
nums.swap(last_ascending_pair, last_greater_than_n);
nums[last_ascending_pair + 1..].reverse();
true
}
Вы можете подумать, что есть более «итеративный» способ написать это, и вполне может быть. Я попробовал, но я не люблю результат. Я думаю, выбери свой яд.
Большое спасибо. Мне нравится и код здесь, и тот, который вы оставили на детской площадке. Я тоже задавал вопрос в Reddit, есть многообещающий ответ: reddit.com/r/rust/comments/mldrsx/…, Я думаю, мы можем взглянуть, может быть, мы сможем понять эту идею.
— доисторический пингвин
Все ваши комментарии блестящие, я так много узнал здесь.
— доисторический пингвин
Алгоритм с вашим исправлением чище, чем в моем сообщении, однако я проверил, что код стандартной библиотеки LLVM c ++: github.com/llvm-mirror/libcxx/blob/master/include/…, он использует непонятную версию, я думаю, что это можно как-то улучшить.
— доисторический пингвин
Хм, я забыл про
rposition
. Это плюсwindows
(хотяarray_windows
все равно было бы лучше) делает версию итератора намного чище, заменяяenumerate
+filter_map
+next_back
танцевать— trentcl
Вы также можете используйте бинарный поиск на монотонно убывающем подслое, чтобы найти заменяемую позицию
— trentcl
Показывать 1 больше комментариев