LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)
Posted Zephyr丶J
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)相关的知识,希望对你有一定的参考价值。
581. 最短无序连续子数组
2021.8.3 每日一题
题目描述
给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
示例 1:
输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
示例 2:
输入:nums = [1,2,3,4]
输出:0
示例 3:
输入:nums = [1]
输出:0
提示:
1 <= nums.length <= 104
-105 <= nums[i] <= 105
进阶:你可以设计一个时间复杂度为 O(n) 的解决方案吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
第一种:排序
class Solution {
public int findUnsortedSubarray(int[] nums) {
//最长有序和最短无序
//第一种,很直观的想到,排序,比对,哪个不一样
int l = nums.length;
int[] temp = Arrays.copyOf(nums, l);
Arrays.sort(temp);
int left = 0;
int right = l - 1;
while(left <= right && nums[left] == temp[left]){
left++;
}
while(left <= right && nums[right] == temp[right]){
right--;
}
return right - left + 1;
}
}
第二种:
(加这些注释是为了记录我的思考过程)
我是这样想的,如果能知道左边开始的点的话,就能记录每个位置无序子串的长度,如果当前值大于目前最大值,那么这个点不需要处理,继续向后遍历;如果小于,那么说明当前位置的值肯定要移动。所以无序子串的右边界跳到这里
那么左边界怎么处理呢,刚开始想的是找到第一个要移动的点,下标为idx;然后在后面找idx右边的最小值,然后在左边idx前面(排好序的),找它插入的位置,就是左边的下标
但是写着写着,从右到左同理,不就也能找到左边位置了吗
然后就写出来了:
class Solution {
public int findUnsortedSubarray(int[] nums) {
//O(n)怎么做呢,隐隐感觉到是动规
//主要是要找到第一个要交换的点,后面感觉就动规就可以
//这样,先找到哪里开始是不是排序的
/*
int idx = 0;
for(int i = 0; i < l - 1; i++){
if(nums[i] > nums[i + 1]){
idx = i + 1;
break;
}
}
//从这里向后,找到之后的最小值
int min = Integer.MAX_VALUE;
for(int i = idx; i < l; i++){
min = Math.min()
}
*/
//写着写着,找到思路了,从左到右,查右边下标
//从右到左,查左边下标
int l = nums.length;
//从左到右,找到右边应该排序的最大下标
int right = 0;
int max = Integer.MIN_VALUE;
for(int i = 0; i < l; i++){
if(nums[i] < max)
right = i;
else{
max = nums[i];
}
}
if(right == 0)
return 0;
int left = 0;
int min = Integer.MAX_VALUE;
for(int i = l - 1; i >= 0; i--){
if(nums[i] > min){
left = i;
}else{
min = nums[i];
}
}
return right - left + 1;
}
}
611. 有效三角形的个数
2021.8.4 每日一题
题目描述
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。
示例 1:
输入: [2,2,3,4]
输出: 3
解释:
有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
注意:
数组长度不超过1000。
数组里整数的范围为 [0, 1000]。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-triangle-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
二分查找
class Solution {
public int triangleNumber(int[] nums) {
//a + b > c,两个较小的边之和小于第三边
//想到的第一个思路是二分
int l = nums.length;
Arrays.sort(nums);
int count = 0;
for(int i = 0; i < l - 2; i++){
for(int j = i + 1; j < l - 1; j++){
int a = nums[i];
int b = nums[j];
int c = nums[i] + nums[j];
int left = j + 1;
int right = l - 1;
//找第一个小于c的
while(left < right){
int mid = (right - left + 1) / 2 + left;
if(nums[mid] >= c){
right = mid - 1;
}else{
left = mid;
}
}
if(nums[left] < a + b)
count += left - j;
}
}
return count;
}
}
双指针,写了一年,一直错
class Solution {
public int triangleNumber(int[] nums) {
//a + b > c,两个较小的边之和小于第三边
//双指针
int l = nums.length;
Arrays.sort(nums);
int count = 0;
for(int i = 0; i < l - 2; i++){
int j = i + 1;
int k = i + 2;
while(j < l - 1 && k < l){
//走到头了,那么就统计数
while(k < l - 1 && nums[i] + nums[j] > nums[k]){
k++;
}
//如果k指向的数不满足条件,那么就执行这条
if(nums[i] + nums[j] <= nums[k]){
k--;
}
count += Math.max(k - j, 0);
//先让j加1
j++;
//如果a + b还小于c的话,继续增大j
while(j < l - 2 && nums[i] + nums[j] <= nums[k]){
j++;
}
}
}
return count;
}
}
class Solution {
public int triangleNumber(int[] nums) {
//a + b > c,两个较小的边之和小于第三边
//双指针
//看了人家的双指针,我不知道是在写什么
//枚举最大数简单
int l = nums.length;
Arrays.sort(nums);
int count = 0;
//枚举最大边
for(int i = l - 1; i >= 0; i--){
int left = 0;
int right = i - 1;
//左右两个双指针
while(left < right){
//如果满足条件,那么中间的left都满足条件
if(nums[left] + nums[right] > nums[i]){
count += right - left;
right--;
}else
left++;
}
}
return count;
}
}
15. 三数之和
题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
和上一道题一样,双指针做了一把
这里主要是要去重
看了力扣的官解,不禁感叹一句以前力扣的官解是真的良心啊
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//三数之和,慕名而来
//控制一个数,然后使剩下的两个数之和等于相反数
//也就是两数之和
//或者排序加双指针
int l = nums.length;
List<List<Integer>> res = new ArrayList<>();
if(l < 3)
return res;
Arrays.sort(nums);
for(int i = 0; i < l - 2; i++){
//如果这个数计算过了
if(i != 0 && nums[i] == nums[i - 1])
continue;
//加了这个判断,一下提升到很快
if(nums[i] > 0)
break;
int left = i + 1;
int right = l - 1;
while(left < right){
//如果大于了,那么应该减小
if(nums[left] + nums[right] > -nums[i]){
right--;
}else if(nums[left] + nums[right] < -nums[i]){
left++;
}else{
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
res.add(list);
left++;
//如果相同的值就剔除
while(left < right && nums[left] == nums[left - 1])
left++;
right--;
while(right > left && nums[right] == nums[right + 1])
right--;
}
}
}
return res;
}
}
18. 四数之和
题目描述
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [], target = 0
输出:[]
提示:
0 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
两层遍历加双指针
这次我没有用left和right,而是效仿三数之和官解的那种写法
剪枝没有加,因为第一时间没有想到
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
int l = nums.length;
List<List<Integer>> res = new ArrayList<>();
if(l < 4)
return res;
Arrays.sort(nums);
for(int i = 0; i < l - 3; i++){
if(i != 0 && nums[i] == nums[i - 1])
continue;
for(int j = i + 1; j < l - 2; j++){
int sum = nums[i] + nums[j];
if(j > i + 1 && nums[j] == nums[j - 1])
continue;
int t = target - sum;
int r = l - 1;
for(int k = j + 1; k < r; k++){
if(k > j + 1 && nums[k] == nums[k - 1])
continue;
//移动右指针
while(k < r && nums[k] + nums[r] > t){
r--;
}
//如果重合了,那么就跳出
if(k == r)
break;
if(nums[k] + nums[r] == t){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
list.add(nums[r]);
res.add(list);
}
}
}
}
return res;
}
}
以上是关于LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 581. 最短无序连续子数组(Shortest Unsorted Continuous Subarray)
LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)
LeetCode 581 最短无序连续子数组[排序] HERODING的LeetCode之路
LeetCode 581. Shortest Unsorted Continuous Subarray (最短无序连续子数组)