1 两数之和
题目地址:https://leetcode-cn.com/problems/two-sum/
思路1.:两层循序找,判断是否相等,时间复杂度O(n2),空间复杂度O(1);
class Solution {
public int[] twoSum(int[] nums, int target) {
int len=nums.length;
for(int i=0;i<len;i++)
for(int j=i+1;j<len;j++)
if(target==nums[i]+nums[j]){
return new int[]{i,j};
}
return null;
}
}
思路2:先排序数据,然后再找,时间复杂度O(nlogn),空间复杂度O(n)
class Solution {
public int[] twoSum(int[] nums, int target) {
int len=nums.length;
int[] arr=new int[len];
//copy 数组
for(int i=0;i<len;i++){
arr[i]=nums[i];
}
Arrays.sort(arr);
int i=0,j=len-1;
while(i<j){
if(arr[i]+arr[j]>target){
j--;
}else if(arr[i]+arr[j]<target) {
i++;
}else {
int i1=0,i2=0;
for(int index=0;index<len;index++){
if(nums[index]==arr[i]){
i1=index;
break;
}
}
for(int index=0;index<len;index++){
if(nums[index]==arr[j]){
i2=index;
}
}
return new int[]{i1,i2};
}
}
return null;
}
}
思路3:通过hash表映射,可以将两层循环变成一层,使用hashMap时候重复元素问题,题目中数了只有唯一解。时间复杂度O(n),空间复杂度O(n)
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
int len = nums.length;
for (int i = 0; i < len; i++) {
//有重复元素,只记录最后一个,题目中约束了只有唯一解
map.put(nums[i], i);
}
for (int i = 0; i < len; i++) {
Integer j = map.get(target - nums[i]);
//不能和自身相加
if (j != null && j != i) {
return new int[]{i, map.get(target - nums[i])};
}
}
return null;
}
}
class Solution {
public int[] twoSum(int[] nums, int target) {
int len=nums.length;
//指定长度
Map<Integer,Integer> map=new HashMap<>(len);
for(int i=0;i<len;i++){
if(map.containsKey(target-nums[i])){
return new int[]{i,map.get(target-nums[i])};
}
map.put(nums[i],i);
}
return null;
}
}
2 三数之和
题目地址:https://leetcode-cn.com/problems/3sum/
思路1:三层循环暴力解决,先排序(为了方便去重),三层循环找,最后去重
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums==null || nums.length<3){
return new ArrayList<>(1);
}
int len=nums.length;
int target=0;
quickSort(nums,0,len-1);
List<List<Integer>> ret=new ArrayList<>();
// 三层嵌套循环
for(int i=0;i<len;i++)
if(num[i]>target) break;
for(int j=i+1;j<len;j++)
for(int k=j+1;k<len;k++){
if(target==nums[i]+nums[j]+nums[k]){
List<Integer> res=new ArrayList<>(3);
res.add(nums[i]);
res.add(nums[j]);
res.add(nums[k]);
ret.add(res);
}
}
//去重
removeDup(ret);
return ret;
}
void removeDup(List<List<Integer>> ret){
List<String> tmpList=new ArrayList<>(ret.size());
Iterator<List<Integer>> it = ret.iterator();
while(it.hasNext()){
String tmpStr= it.next().toString();
if(tmpList.contains(tmpStr)){
it.remove();
}else{
tmpList.add(tmpStr);
}
}
}
void quickSort(int[] nums,int low,int high){
if(low>=high){
return;
}
int p=partition(nums,low,high);
quickSort(nums,low,p-1);
quickSort(nums,p+1,high);
}
int partition(int[] nums,int low,int high) {
//哨兵
int pivot=nums[low];
int left=low,right=high;
while(left<right){
//要先从右边找,然后再从左边找顺序不要错了,原因哨兵值在左边开始
while(nums[right]>=pivot && left<right) right--;
while(nums[left]<=pivot && left<right) left++;
if(left<right){
int tmp=nums[left];
nums[left]=nums[right];
nums[right]=tmp;
}
}
nums[low]=nums[left];
nums[left]=pivot;
return left;
}
}
思路2:借鉴2数之和思路,使用hash 将3层循环降成两层循环,使用Set 去重List(要保证list 顺序一致的)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums==null || nums.length<3){
return new ArrayList<>(1);
}
int len=nums.length;
int target=0;
quickSort(nums,0,len-1);
// 使用set 去重,要保证List是有序的
Set<List<Integer>> ret=new LinkedHashSet<>();
// 两层层嵌套循环
for(int i=0;i<len;i++){
if(nums[i]>target) break;
if(i>0 && nums[i]==nums[i-1]) continue;
Set<Integer> set=new HashSet<>(nums.length);
for(int j=i+1;j<len;j++){
if(set.contains(target-nums[i]-nums[j])){
List<Integer> res=new ArrayList<>(3);
res.add(target-nums[i]-nums[j]);
res.add(nums[i]);
res.add(nums[j]);
ret.add(res);
}else{
set.add(nums[j]);
}
}
}
return new ArrayList<>(ret);
}
void quickSort(int[] nums,int low,int high){
if(low>=high){
return;
}
int p=partition(nums,low,high);
quickSort(nums,low,p-1);
quickSort(nums,p+1,high);
}
int partition(int[] nums,int low,int high) {
//哨兵
int pivot=nums[low];
int left=low,right=high;
while(left<right){
//要先从右边找,然后再从左边找顺序不要错了,原因哨兵值在左边开始
while(nums[right]>=pivot && left<right) right--;
while(nums[left]<=pivot && left<right) left++;
if(left<right){
int tmp=nums[left];
nums[left]=nums[right];
nums[right]=tmp;
}
}
nums[low]=nums[left];
nums[left]=pivot;
return left;
}
}
思路3:数组已经有序,可以固定一个,然后通过双指针。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums==null || nums.length<3){
return new ArrayList<>(1);
}
int len=nums.length;
int target=0;
quickSort(nums,0,len-1);
// 使用set 去重,要保证List是有序的
Set<List<Integer>> ret=new LinkedHashSet<>();
// 两层层嵌套循环
for(int i=0;i<len;i++){
if(nums[i]>target){
break;
}
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int left=i+1,right=len-1;
int needVal=target-nums[i];
while(left<right){
if(needVal==nums[left]+nums[right]){
List<Integer> ans=new ArrayList<>(3);
ans.add(nums[i]);
ans.add(nums[left]);
ans.add(nums[right]);
ret.add(ans);
left++;
right--;
}else if(nums[left]+nums[right]<needVal) {
left++;
}else{
right--;
}
}
}
return new ArrayList<>(ret);
}
void quickSort(int[] nums,int low,int high){
if(low>=high){
return;
}
int p=partition(nums,low,high);
quickSort(nums,low,p-1);
quickSort(nums,p+1,high);
}
int partition(int[] nums,int low,int high) {
//哨兵
int pivot=nums[low];
int left=low,right=high;
while(left<right){
//要先从右边找,然后再从左边找顺序不要错了,原因哨兵值在左边开始
while(nums[right]>=pivot && left<right) right--;
while(nums[left]<=pivot && left<right) left++;
if(left<right){
int tmp=nums[left];
nums[left]=nums[right];
nums[right]=tmp;
}
}
nums[low]=nums[left];
nums[left]=pivot;
return left;
}
}
双指针去重
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums==null || nums.length<3){
return new ArrayList<>(1);
}
int len=nums.length;
int target=0;
quickSort(nums,0,len-1);
// 使用set 去重,要保证List是有序的
List<List<Integer>> ret=new LinkedList<>();
// 两层层嵌套循环
for(int i=0;i<len;i++){
if(nums[i]>target){
break;
}
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int left=i+1,right=len-1;
int needVal=target-nums[i];
while(left<right){
if(needVal==nums[left]+nums[right]){
List<Integer> ans=new ArrayList<>(3);
ans.add(nums[i]);
ans.add(nums[left]);
ans.add(nums[right]);
ret.add(ans);
left++;
right--;
//避免重复
while(left<right&& nums[left]==nums[left-1]){
left++;
}
while(left<right&& nums[right]==nums[right+1]){
right--;
}
}else if(nums[left]+nums[right]<needVal) {
left++;
}else{
right--;
}
}
}
return ret;
}
3 四数之和
https://leetcode-cn.com/problems/4sum/
思路1:使用hash 将4层循环降成三层循环,使用Set 去重List(要保证list 顺序一致的)
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
if(nums==null || nums.length<4) {
return new ArrayList<>(1);
}
//先排序
Arrays.sort(nums);
int len=nums.length;
Set<List<Integer>> ret=new LinkedHashSet<>();
for(int i=0;i<len;i++){
//防止输入负数
if(nums[i]>0 && nums[i]>target){
break;
}
if(i>0 && nums[i]==nums[i-1]){
continue;
}
for(int j=i+1;j<len;j++){
Set<Integer> set=new HashSet<>(len);
for(int k=j+1;k<len;k++){
int needNum=target-nums[i]-nums[j]-nums[k];
if(set.contains(needNum)){
List<Integer> ans=new ArrayList<>(4);
ans.add(nums[i]);
ans.add(nums[j]);
ans.add(needNum);
ans.add(nums[k]);
ret.add(ans);
}else{
set.add(nums[k]);
}
}
}
}
return new ArrayList<>(ret);
}
}
思路2:双指针
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
if (nums == null || nums.length < 4) {
return new ArrayList<>(1);
}
//先排序
Arrays.sort(nums);
int len = nums.length;
List<List<Integer>> ret = new LinkedList<>();
for (int i = 0; i < len - 3; i++) {
//防止输入负数
if (nums[i] > 0 && nums[i] > target) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < len - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1, right = len - 1;
while (left < right) {
int val = nums[i] + nums[j] + nums[left] + nums[right];
if (val == target) {
List<Integer> ans = new ArrayList<>(4);
ans.add(nums[i]);
ans.add(nums[j]);
ans.add(nums[left]);
ans.add(nums[right]);
ret.add(ans);
left++;
right--;
//去重
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
} else if (val < target) {
left++;
} else {
right--;
}
}
}
}
return ret;
}
4 总结
对于类似两数之和、三数之和、四数之和都可以通过hash来降维,可以通过双指针来提升效率。