1.两数之和
解法1:暴力解法【线性查找】
//空间复杂度O(1) 空间复杂度O(n^2)class Solution {public int[] twoSum(int[] nums, int target) {if(nums==null||nums.length==0){return new int[0];}for(int i=0;i<nums.length;i++){int x=nums[i];for(int j=i+1;j<nums.length;j++){if(nums[j]==target-x){return new int[]{i,j};}}}return new int[0];}}
解法2:二分查找
由于该题目要求给出值的索引,二分查找的前提是数组有序的,这样就打乱了原来数组的索引,如果硬要这么做,需要开辟额外的空间来记住原有的值的索引,所以在这里不适合用二分查找!
解法3:哈希查找优化
// 时间复杂度O(n) 空间复杂度O(n) -->开辟了一个map,最大是数组长度class Solution {public int[] twoSum(int[] nums, int target) {if(nums==null||nums.length==0){return new int[0];}HashMap<Integer,Integer> map=new HashMap<>();for(int i=0;i<nums.length;i++){ //O(n)map.put(nums[i],i);}for(int i=0;i<nums.length;i++){ //O(n)int x=nums[i];if(map.containsKey(target-x)){int index=map.get(target-x);//防止使用同一元素两次if(i!=index){return new int[]{i,index};}}}return new int[0];}}
再次优化
// 时间复杂度:O(n)// 空间复杂度:O(n)public int[] twoSum(int[] nums, int target) {if (nums == null || nums.length == 0) return new int[0];int n = nums.length;Map<Integer, Integer> map = new HashMap<>();// O(n)for (int i = 0; i < n; i++) {int x = nums[i];// 哈希查找if (map.containsKey(target - x)) {int index = map.get(target - x);return new int[]{i, index};}map.put(x, i); //核心思想:有可能在数组的前几个元素就有答案了,后面的元素就没必要加 进map}return new int[0];}
2.两数之和 II - 输入有序数组
这道题可以用暴力解法,也可以用hash解法,但是hash解法有一个缺点,需要开辟额外一个map配合使用。
这道题给的数组是有序的,那么我们可以使用二分查找,快且不需要开辟空间。
解法1:二分查找
//时间复杂度O(nlogn)class Solution {public int[] twoSum(int[] nums, int target) {if (nums == null || nums.length == 0) return new int[0];for(int i=0;i<nums.length;i++){int x=nums[i];//在i+1~nums.length-1区间二分查找int index=binarySerach(nums,i+1,nums.length-1,target-x);if(index!=-1){return new int[]{i+1,index+1};}}return new int[0];}private int binarySerach(int[] nums,int left,int right,int target){while(left<=right){//防止数值溢出int_Maxint mid=left+(right-left)/2;if(nums[mid]==target)return mid;else if(nums[mid]>target)right=mid-1;elseleft=mid+1;}return -1;}}
解法2:双指针
因为该数组有序,所以可以在两边设置指针,让最小的指针和最大的指针相加,如果还是小于target,那么最小指针往右移动,大于同理。就可以实现时间复杂度低于二分查找,空间复杂度不变的优化。
// 时间复杂度:O(n)// 空间复杂度:O(1)class Solution {public int[] twoSum(int[] nums, int target) {if (nums == null || nums.length == 0) return new int[0];int left=0;int right=nums.length-1;while(left<=right){if(nums[left]+nums[right]==target)return new int[]{left+1,right+1};else if(nums[left]+nums[right]<target)left++;elseright--;}return new int[0];}}
两数之和 IV - 输入 BST

BST二叉查找树中根结点大于左子节点,小于右子节点。刚好中序遍历二叉查找树生成一条有序的数组,可以根据这条数组进行双指针解决。
方案1:双指针
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*///时间复杂度O(n)class Solution {public boolean findTarget(TreeNode root, int k) {if(root==null) return false;List<Integer> nums=new ArrayList<>();inOrder(root,nums);int left=0;int right=nums.size()-1;while(left<right){int sum=nums.get(left)+nums.get(right);if(sum==k)return true;else if(sum>k)right--;elseleft++;}return false;}//中序遍历 左中右private void inOrder(TreeNode node,List<Integer> nums){if(node==null) return;inOrder(node.left,nums);nums.add(node.val);inOrder(node.right,nums);}}
解法2:哈希查找
将每一次遍历的结点,与set集合中的元素进行比较,如果该结点+集合中元素==target,那么直接返回true,不用再进行遍历。
class Solution {public boolean findTarget(TreeNode root, int k) {if(root==null) return false;return find(root,k,new HashSet<>());}private boolean find(TreeNode node, int target, Set<Integer> set) {//前序遍历 中左右if(node==null) return false;if(set.contains(target-node.val)) return true;set.add(node.val);//一直遍历左结点 直到左节点为null才开始遍历右节点return find(node.left,target,set)||find(node.right,target,set);}}
补充:
DLR—前序遍历(根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面 )
LDR—中序遍历(根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面)
LRD—后序遍历(根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根前面)
三数之和
解法1:暴力解法
class Solution {public List<List<Integer>> threeSum(int[] nums) {if(nums==null||nums.length<3)return new ArrayList<>();//使用hashSet避免集合重复Set<List<Integer>> set=new HashSet<>();//因为需要集合中的元素不重复,所以进行排序Arrays.sort(nums); //O(nlogn)//O(n^3)for(int i=0;i<nums.length;i++){for(int j=i+1;j<nums.length;j++){for(int k=j+1;k<nums.length;k++){if(nums[i] + nums[j] + nums[k] == 0){set.add(Arrays.asList(nums[i],nums[j],nums[k]));}}}}return new ArrayList<>(set);}}
解法2:双指针
固定一个元素,然后设置两个指针指向剩余的元素,将该两个指针指向的元素相加,如果等于固定的元素的符数,那就是其中一个答案。由于是有序的,所以这两个指针可以配合二分查找。
class Solution {public List<List<Integer>> threeSum(int[] nums) {if(nums==null||nums.length<3)return new ArrayList<>();//使用hashSet避免集合重复Set<List<Integer>> set=new HashSet<>();//因为需要集合中的元素不重复,所以进行排序Arrays.sort(nums); //O(nlogn)//O(n^2)for(int i=0;i<nums.length;i++){int target=-nums[i];int left=i+1;int right=nums.length-1;while(left<right){int sum=nums[left]+nums[right];if(sum==target){set.add(Arrays.asList(nums[i],nums[left],nums[right]));left++;right--;}else if(sum <target){left++;}else{right--;}}}return new ArrayList<>(set); //O(n)}}
对Set转List进行优化
使用HashSet的目的是为了去重,那么如果不利用HashSet的去重特性,该怎么去重?
- left和right指针的下一个/前一个元素跟当前指向的元素值相同,则跳过下一个/前一个元素,指向下下个元素或前前个元素
固定元素i的下一个元素如果同当前元素值相同,那么下一次循环的结果肯定是相同的,所以应该跳过下一个元素。
class Solution {public List<List<Integer>> threeSum(int[] nums) {if(nums==null||nums.length<3)return new ArrayList<>();List<List<Integer>> list=new ArrayList<>();Arrays.sort(nums);for(int i=0;i<nums.length;i++){//当前元素跟上一个元素相同则跳过,i+1和i比较的话,就会导致还没拿到答案就跳过了if(i>0&&nums[i]==nums[i-1]) continue;int target=-nums[i];int left=i+1;int right=nums.length-1;while(left<right){int sum=nums[left]+nums[right];if(sum==target){list.add(Arrays.asList(nums[i],nums[left],nums[right]));//++left:先让left往后移 然后才拿到left+1后的值再进行判断 而不是先拿left进行判断再往后移while(left<right&&nums[left]==nums[++left]);while(left<right&&nums[right]==nums[--right]);}else if(sum <target){left++;}else{right--;}}}return list;}}
四数之和

class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {if (nums == null || nums.length < 4)return new ArrayList<>();Arrays.sort(nums);List<List<Integer>> res = new ArrayList<>();for(int i=0;i<nums.length;i++){if(i>0&&nums[i]==nums[i-1]) continue;for(int j=i+1;j<nums.length;j++){if(j>i+1&&nums[j]==nums[j-1]) continue;int partSum=nums[i]+nums[j];int left=j+1;int right=nums.length-1;while(left<right){int sum=partSum +nums[left]+nums[right];if(sum==target){res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));while(left<right&&nums[left]==nums[++left]);while(left<right&&nums[right]==nums[--right]);}else if(sum<target)left++;elseright--;}}}return res;}}

