3.快乐数
本题说明只有两种情况,即最后会出现1重复,或者也可能是无限循环但始终变不到 1。我们实际上可以把它们两种看成一种来对待,即都是看成循环,分成1循环和不是1循环
看起来就像链表循环一样,我们可以利用快慢双指针来实现
即定义两个指针,fast指针一次走两步,slow指针一次走一步,由于一定会进入循环,那么两个指针由于速度差一定会相遇,我们只需要判断相遇点是否为1就好了
但是撇开这道题的提示,为什么一定会进入循环呢??
有一个原理叫做雀巢原理:
有题目我们知道:
n的最大值为2^31-1 = 2,147,483,647,我们把每一位换成9,即9999999999,即得到在0 ~ 2^31-1 可能出现的最大快乐数:9 * 9 * 10 = 810
即无论快乐数怎么变,最终的范围都在1 ~ 810,由雀巢原理,那么一定不会死循环
题解:
class Solution {
public int squaresSum(int n){
int sum = 0;
while(n != 0){
int t = n % 10;
sum += t * t;
n /= 10;
}
return sum;
}
public boolean isHappy(int n) {
int slow = squaresSum(n);//先让fast和slow各自走一遍
int fast = squaresSum(squaresSum(n));
while(slow != fast){
slow = squaresSum(slow);
fast = squaresSum(squaresSum(fast));
}
return slow == 1;
}
}
4.盛水最多的容器
此题目的暴力解法当然容易想到,就是一个一个去枚举,最后得出一个最大的即可,但是实际上并不是所有情况都需要枚举:
就举下面的例子来说:
我们定义两个指针分别在一左一右,当我们定住6,从4往前找的时候,会出现两种情况:
(1)数比4大,如5,那么高度还是4,高度不变,但是长度减小,所以体积一定比6 ~ 4的小
(2)数比4小,那么高度减小,长度也减小,所以体积一定比6 ~ 4的体积小
即定住6,从4往前找时,所有的情况都比6 ~ 4 的体积小,那么中间的我们就不必再一一枚举了,直接记录下最大值V1即可
这时候我们将right--,即来到5,
那情况不也是一样,6~5的体积是6~5范围内最大的,记录为V2,
由此我们发现,我们在左右寻找的时候,只需比较left和right的值,如果左边的数小,那就记录下体积,left++;右边小,记录下体积,right--即可
最后得出一个max
题解:
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length -1;
int max = 0;
while(left < right){
int v = (right - left) * Math.min(height[right],height[left]);
max = Math.max(v,max);
if(height[right] > height[left]){
left++;
}else{
right--;
}
}
return max;
}
}
优化:
就拿这个例子来说,我们在比较完4后,下一个比较2,但是2比4小,体积6~2的体积肯定没有6~4的大,那么2就不用比较,right--,直到有一个比4大,才开始比较.
优化代码:
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length -1;
int max = 0;
while(left < right){
int v = (right - left) * Math.min(height[right],height[left]);
max = Math.max(v,max);
if(height[right] > height[left] && left < right){
int cur = height[left];
while(cur >= height[left]){
left++;
}
}else{
int cur = height[right];
while(height[right] <= cur && left < right){
right--;
}
}
}
return max;
}
}
5.有效三角形的个数
此题目暴力解法容易想到,即一个一个枚举,但是我们要写3层循环,时间复杂度为O(n^3),再加上三角形的比较:任意两边之和大于第三边,那么还是比较3次,最后时间复杂度来到O(3 * n^3);
我们进行优化:
优化1:三角形的比较:
我们都会想到任意两边之和大于第三边,但是要比较3次
而但我们将3个数先排序后,如2 2 3,那么我们只需要比较2 + 2 > 3 即可,因为2 +2这三个数里面最小的两个数的和,都大于最大的第三边,那么其他两种情况一定成立,那么我们在3层循环中只需要比较一次即可,时间复杂度为O(n^3),加上排序的时间复杂度也小于先前的O(3 * n^3)
优化2:枚举的优化:
排序后:
我们先定住最大的数,利用双指针在最大数前面的范围内依次枚举,查找符合三角形规则的例子,重点就在枚举的过程:
但我们比较left + right 是否大于10时,会出现两种情况:
(1) 大于10:那么2 + 9都大于10了,从left往后找,如3 + 9 4 +9肯定都是大于10的,那么2~5这个范围的数加上9就都满足大于10的条件,因此我们不用一一枚举,就能得到(right - left) 种例子,之后right--即可
(2)小于等于10:
既然2 +5 都小于10了,那么2 加上5以前的数都肯定是小于10的,不必再一一枚举,直接left++即可
当left和right相遇时,说明最大值为10的全部比较完了,接下来让最大值指向9,依次类推……
这样我们发现,会少了很多没必要的比较,时间复杂度会降低很多
题解:
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int count = 0;
int n = nums.length;
for(int i = n - 1; i >= 2; i--){
int left = 0;
int right = i - 1;
while(left < right){
if(nums[left] + nums[right] <= nums[i]){
left++;
}else {
count += (right - left);
right--;
}
}
}
return count;
}