天天算法之回溯
思想
- 利用
递归+栈
来刻画行为逻辑,遍历问题的所有可能的解法。 - 回溯依赖栈的方式进行诠释。
实践
-
-
Next Permutation
首先要理解题目,也就是求一组数的下一个全排列(字典顺序)。
然后进行步骤推演,首先找到
第一个正序对
,如果正序对是末尾的最后两个元素,则直接交换即可。如果正序对不是最后两个末尾的元素的情况,则需要 在
正序对终止元素
后面的倒序数列
中 找到比正序对起始元素
大 且在倒序数列
中最小的数 并与之交换位置。然后让正序对终止元素
后面的序列正序。比如:123654,第一个正序对
为23,正序对终止元素
为3,在倒序数列
中最小的数为4(比3大),然后交换位置得到124653,再让正序对终止元素
后面的序列正序,最终得到124356,即为下一个全排列序列。在让
正序对终止元素
后面的序列正序的过程中,由于正序对终止元素
后面是倒序的,所以直接翻转即可,对应reverse方法。
class Solution { public void nextPermutation(int[] nums) { if(nums.length == 1){ return; } int firstOrderIndex = findFirstOrderIndex(nums); if(firstOrderIndex == nums.length - 2){ swap(nums, firstOrderIndex, firstOrderIndex+1); return; } if(firstOrderIndex==-1){ reverse(nums, 0, nums.length-1); return; } int minNumIndex = findMinNumThanNIndex(nums, nums[firstOrderIndex]); swap(nums, firstOrderIndex, minNumIndex); reverse(nums, firstOrderIndex+1, nums.length-1); } /** * 找到比某个数大的最小的数,在倒序序列中 */ private int findMinNumThanNIndex(int[] nums, int n){ int i = nums.length-1; for(; i>=0; i--){ if(nums[i]>n){ break; } } return i; } /** * 找到第一次出现正序对的数据的下标,比如:3,1,2,则返回 1。 3,2,1,则返回-1. */ private int findFirstOrderIndex(int[] nums){ int i = nums.length-2; for(; i>=0; i--){ if(nums[i]<nums[i+1]){ break; } } return i; } /** * 数组值链反转 */ private void reverse(int[] nums, int i, int j){ while(i<j){ swap(nums,i,j); ++i; --j; } } /** * 数组值交换 * 亦或操作:相同为0,不同为1。 * 测试提交此种方法比正常 值值交换 快1ms左右。 */ private void swap(int[] nums, int i, int j){ // 自己与自己亦或会置空。如果涉及i和j相同的情况,需要附加判断逻辑。 // if(i == j) return; // 留下 nums[i] 与 nums[j] 的不同赋值给 nums[i] nums[i]^=nums[j]; // nums[j] 去掉与 nums[i] 的相同部分,剩下 nums[i] 赋值给 nums[j] nums[j]^=nums[i]; // nums[i] 加上 nums[i]与nums[j] 不同的部分,变为 nums[j] nums[i]^=nums[j]; } }
-
-
-
Permutations II
打印所有全排列序列。
class Solution { public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> result = new ArrayList<List<Integer>>(); permute(nums, 0, result); return result; } private void permute(int[] nums, int stackHeight, List<List<Integer>> result){ if (stackHeight == nums.length) { // 栈高 满 则输出 result.add(Arrays.stream(nums).boxed().collect(Collectors.toList())); return; } // 针对题眼:nums, that might contain duplicates Set<Integer> set = new HashSet<>(); for (int j = stackHeight; j < nums.length; ++j) { if (!set.contains(nums[j])) { // 横向遍历中如果已经入过栈,则后续相同的数无需再入栈,避免重复。 swap(nums, stackHeight, j); // 纵向递归 permute(nums, stackHeight + 1, result); // 出栈 swap(nums, stackHeight, j); set.add(nums[j]); } } } private void swap(int[] nums, int i, int j){ if(i == j)return; nums[i] ^= nums[j]; nums[j] ^= nums[i]; nums[i] ^= nums[j]; } }
-
-
1850:Minimum Adjacent Swaps to Reach the Kth Smallest Number
class Solution { public int getMinSwaps(String num, int k) { StringBuilder numNew = new StringBuilder(num); for(int i = 0; i < k; i++){ getNextPermutation(numNew); } return countSwap(num, numNew); } private int countSwap(String num, StringBuilder numNew){ int j = 0, count = 0; for(int i = 0; i < num.length() - 1; i++){ if(num.charAt(i) != numNew.charAt(j)){ while(num.charAt(i) != numNew.charAt(j)){ ++j; } while(j>i){ swap(numNew, j-1, j); --j; count++; } } j++; } return count; } private void getNextPermutation(StringBuilder numNew) { int firstPosPairIndex = findFirstPosPair(numNew); if(firstPosPairIndex == numNew.length() - 2){ swap(numNew, numNew.length() - 2, numNew.length() - 1); return; } // if(firstPosPairIndex == -1){ // The tests are generated in such a way that kth smallest wonderful integer exists. // } int smallBiggerIndexThanN = findSmallBiggerThanN(numNew, numNew.charAt(firstPosPairIndex)); swap(numNew, firstPosPairIndex, smallBiggerIndexThanN); reverse(numNew, firstPosPairIndex + 1, numNew.length() -1); } private int findSmallBiggerThanN(StringBuilder numNew, char n){ int i = numNew.length() - 1; for(; i >= 0; i--){ if(numNew.charAt(i) > n){ return i; } } return i; } private int findFirstPosPair(StringBuilder numNew){ int i = numNew.length() - 2; for(; i >= 0; i--){ if(numNew.charAt(i) < numNew.charAt(i+1)){ return i; } } return i; } private void reverse(StringBuilder numNew, int i, int j){ while(i < j){ swap(numNew, i, j); i++; j--; } } private void swap(StringBuilder numNew, int i, int j){ if(i == j) return; char tmp = numNew.charAt(j); numNew.setCharAt(j, numNew.charAt(i)); numNew.setCharAt(i, tmp); // num.charAt(i) = num.charAt(j); // num.charAt(j) = tmp; } }
leetCode国际版类型题
- \31. Next Permutation:https://leetcode.com/problems/next-permutation/
- \46. Permutations:https://leetcode.com/problems/permutations/
- \47. Permutations II:https://leetcode.com/problems/permutations-ii/
- \60. Permutation Sequence:https://leetcode.com/problems/permutation-sequence/
- \1850. Minimum Adjacent Swaps to Reach the Kth Smallest Number:https://leetcode.com/problems/minimum-adjacent-swaps-to-reach-the-kth-smallest-number/