五、有效三角形的个数
给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。
示例

题目要求返回所有数组中可以组成有效三角形的元素组合。
最简单的解法就是暴力枚举,列出所有组合,记录下可以组成三角形的三元组个数。
示例一的解析给出两次相同组合,说明虽然两个元素值一样,但是下标不一样,也可以算作一组。
直接套三层循环暴力过掉。
代码:
java
class Solution {
public int triangleNumber(int[] nums) {
int count=0;
for(int a=0;a<nums.length;a++){
for(int b=1;b<nums.length;b++){
for(int c=2;c<nums.length;c++){
while(b<=a){
b++;
}
while(c<=b){
c++;
}
if(c>=nums.length){
break;
}
if((nums[a]+nums[b])>nums[c]){
if ((nums[a]+nums[c])>nums[b]){
if ((nums[b]+nums[c])>nums[a]){
count++;
}
}
}
}
}
}
return count;
}
}
但是肯定报错,时间复杂度起步 O(n³) ,必然超时。

需要想一个更优解法。
题目说明所有的用例都是非负整数,那就可以给数组先排序,再进行遍历。
乱序数组很难找出规律,而且最主要的原因在于不能移动,因为保证不了移动后的整数大小。
这里用一组暴力解法报错的例子找更优解。

刚才说了,乱序数组无法向不确定大小的位置移动寻找有效三角形,所以先让数组排序。

此时再基于三层循环的思路做优化。
让三个指针分别指向数组左右侧

如果写过盛水最多的容器题目,那应该能想到用指针优化这题。
我们固定 a ,让 b 移动,会发现两种情况。

若是最小的数加上这个区间里最大的数依旧小于等于 C ,那么 a 要向右移。

反之,这个区间里的数都不用算了,全是有效三角形。

完成一次循环后,C 的位置向左移一格,继续寻找有效三角形,直到 C 移到2的位置。
优化后的代码:
java
class Solution {
public int triangleNumber(int[] nums) {
int count=0;
Arrays.sort(nums);
for(int i=nums.length-1;i>=2;i--){
int left=0,right=i-1;
while(left<right){
if(nums[left]+nums[right]>nums[i]){
count+=right-left;
right--;
}else{
left++;
}
}
}
return count;
}
}

六、和为s的两个数字
购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。

这是一道简单题,暴力解法依旧两层循环枚举,同时肯定超时。
代码:
java
class Solution {
public int[] twoSum(int[] price, int target) {
int[] arr={-1,-1};
for(int i=0;i<price.length;i++){
for(int j=1;j<price.length;j++){
if((price[i]+price[j])==target && price[i]!=price[j]){
arr[0]=price[i];
arr[1]=price[j];
}
}
}
return arr;
}
}

优化解法依旧采用双指针。

由于数组升序排列降低了很多难度,所以不用考虑这么多,直接把指针放在左右两侧。


等到和为 target ,把这两个整数放到一个新数组里返回。
代码:
java
class Solution {
public int[] twoSum(int[] price, int target) {
int left=0,right=price.length-1;
while(left<right){
int sum=price[left]+price[right];
if(sum==target){
return new int[] {price[left],price[right]};
}else if(sum>target){
right--;
}else{
left++;
}
}
return new int[]{0};
}
}
注意一下,如果返回值写在循环体里,外边一定要再给一个返回值,否则编译器会认为可能没有返回值从而报错。
