LeetCode 31 Next Permutation
方法:找规律
时间复杂度:O(n)
空间复杂度:O(1)
想法:这是个经典题了,方法也是比较固定的。首先从后往前找,找到第一个值减小的地方,这时候i-1指向那个比后面的值小的那个值。当然也有可能直接i=0了,这时候就说明数组完全倒序,因此后序直接把数组翻转就完了。但是在i>0的情况下,我们需要从后往前,找到第一个严格大于nums[i-1]的值,然后把这俩元素交换。最后把数组从i到末尾的元素全部倒序。
这个该怎么理解呢?首先如果从前往后找,之后i=0,这个时候直接翻转数组,这是很好理解的。对于i>0的情况,考虑一串数字xxxxxxx2765431,next permutation是找下一个排列,前缀如果能不变的话,就尽可能不要动前缀,因为你改前缀的话得到的数肯定是严格比只改从2之后的数得到的数字更大的。然后,我们从后往前找,找到i-1指向的是2。这个时候因为它后面的都是严格按照单调不增的顺序写在后面的,因此我们就达到了以xxxxxxx2为前缀所能组成的最大的数字。你要是想找下一个排列的话,就肯定这一位不能再以2开头了。那么以谁开头呢?那就是在这后面的严格比2大的第一个数字。我们这里是3。把2和3交换顺序的时候,我们就确认了,下一个排列肯定是以xxxxxxx3开头的,需要找以此为前缀的最小的数。2和3交换顺序后不会影响3后面的数的顺序,后面肯定还是从左往右单调不增的,因此直接把后面再倒一下就完了。
代码:
class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
int i = n - 1;
while (i > 0 && nums[i] <= nums[i - 1]) i--;
if (i > 0) {
int j = n - 1;
while (nums[j] <= nums[i - 1]) j--;
swap(nums, i - 1, j);
}
swapList(nums, i, n - 1);
}
private void swap(int[] nums, int l, int r) {
int tmp = nums[l];
nums[l] = nums[r];
nums[r] = tmp;
}
private void swapList(int[] nums, int l, int r) {
while (l < r) {
swap(nums, l, r);
l++;
r--;
}
}
}
LeetCode 91 Decode Ways
方法:DP
时间复杂度:O(n)
空间复杂度:O(n)
想法:这道题会发现,字符串的前n-1个元素能够decode出来的方式,与整个字符串能decode出来的方式有关联,因此采用dp。然后就开一个数组表示字符串前n个元素的decode ways是多少。递推公式也比较好想,就是在字符串末尾加入一个元素后,这个元素自己能不能decode出来一个字母(关系到算dp[i]时要不要加上dp[i-1]),然后它能不能跟前面一个元素合起来decode出来一个字母,如果能的话就又能加上dp[i-2]。基本上思路是这样,这个题比较神奇的地方在于dp[0]应该设置为多少。就是说dp[1]代表前1个字符能decode出来几个方式,其实很简单,它如果是0的话就不能解码,如果是1到9的话就有一种解码方式。考虑dp[2]。按照我们的递推公式,假设说前两个元素组成了一个在[10,26]范围的数字,那它是能以这种方式解码出来的。这个时候dp[i]需要加上dp[i-2],这时候两个元素的时候,如果按照两个元素解码,肯定只能有一种解码方式,因为一共就两个元素,因此这个地方dp[0]应该取1。dp[0]=1不是代表什么别的特殊含义,而只是为了让dp[2]的计算正确所采用的办法,你也可以单把dp[1]和dp[2]赋完值之后往下推别的值。
代码:
class Solution {
public int numDecodings(String s) {
int n = s.length();
if (n == 1) return s.charAt(0) == '0' ? 0 : 1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = s.charAt(0) == '0' ? 0 : 1;
for (int i = 2; i <= n; i++) {
char c = s.charAt(i - 1);
int num = Integer.parseInt(s.substring(i - 2, i));
if (c == '0') {
if (num == 10 || num == 20) {
dp[i] = dp[i - 2];
}
else {
return 0;
}
}
else {
dp[i] += dp[i - 1];
if (num >= 10 && num <= 26) {
dp[i] += dp[i - 2];
}
}
}
return dp[n];
}
}