力扣刷题总结

文章目录

    • 1.位运算
      • [260. 只出现一次的数字 III ](https://leetcode-cn.com/problems/single-number-iii/)
      • [面试题 05.06. 整数转换 ](https://leetcode-cn.com/problems/convert-integer-lcci/)
    • 2.二分查找
      • [704. 二分查找 ](https://leetcode-cn.com/problems/binary-search/)
    • 3.双指针
      • [977. 有序数组的平方](https://leetcode-cn.com/problems/squares-of-a-sorted-array/submissions/)
      • [209. 长度最小的子数组 ](https://leetcode-cn.com/problems/minimum-size-subarray-sum/submissions/)
      • [3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/submissions/)
    • 4.模拟
      • [59. 螺旋矩阵 II ](https://leetcode-cn.com/problems/spiral-matrix-ii/)
    • 5.数学
      • [1716. 计算力扣银行的钱 ](https://leetcode-cn.com/problems/calculate-money-in-leetcode-bank/)
      • [142. 环形链表 II ](https://leetcode-cn.com/problems/linked-list-cycle-ii/submissions/)
    • 6.哈希
      • [242. 有效的字母异位词 ](https://leetcode-cn.com/problems/valid-anagram/)
      • [349. 两个数组的交集 ](https://leetcode-cn.com/problems/intersection-of-two-arrays/submissions/)
      • [1. 两数之和 ](https://leetcode-cn.com/problems/two-sum/submissions/)
      • [454. 四数相加 II ](https://leetcode-cn.com/problems/4sum-ii/submissions/)
      • [219. 存在重复元素 II](https://leetcode-cn.com/problems/contains-duplicate-ii/submissions/)

自己做过的力扣题总结

1.位运算

260. 只出现一次的数字 III

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案

思路: x&(-x)

取到的结果就是把自身x最低位1和后面的0保存,其余位都是0
e.g:
lowbit(6):6的二进制是(110)所以结果就是(10)
lowbit(7):7的二进制是(111)所以结果就是(1)
然后把结果转化10进制就可以了
快速办法直接看 最低位1取到的是第几位,然后2的几次方就可以了
比如lowbit(6) 最低位1 在第1位上 所以2^1=2;
lowbit (7) 最低位1 在第0位上 所以2^0=1

用途: 一般可以用来获取某个二进制数的LowBit

然后分别与各个数做&运算,&结果为0的为一组,结果不为0的为1组 分别做^运算得到两个数x1 x2

题解:

class Solution {
    public int[] singleNumber(int[] nums) {
        //总体思路:把不同的两个数放在两个小组
        //如何分组?
        //由异或的性质 a^a = 0, a^0 = a
        //先把所有数值异或 得到结果x = x1^x2
        //再由因为x1!= x2 ,一定有一位为1, x&(-x)得到最低位比特为1的值 将这一位作为分组依据
        int x = 0;
        for(int e:nums){
           x=e^x;
        }//得到所有元素的异或和x
        int ground = x&(-x); //6的二进制是(0110)所以结果就是(0010)
        int x1 = 0;
        int x2 = 0;
        for(int e : nums){
           if((e & ground) == 0){ //位为0
              x1 =x1^ e;
           }else{
              x2 =x2^ e;
           }
        }
        return new int[]{x1,x2};
    }
}

面试题 05.06. 整数转换

整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B

思路:对两个整数异或 判断有几个1。

or&(or -1) //消去or二进制表示的最低位的1

class Solution {
    public int convertInteger(int A, int B) {
        int or = A^B;
        //将问题转换为or中1的个数 因为不同位为1
        int cnt = 0;        
        while(or != 0){
           or = or&(or -1);
           cnt++;
        }
        return cnt;
    }
}

2.二分查找

704. 二分查找

思路:二分查找细节详解

class Solution {
    public int search(int[] nums, int target) {
        int low = 0;
        int high = nums.length - 1;
         int mid = 0;
        //假设target在[left right]中 显然结束循环的条件如果是
        while(low <= high){
             mid = (low + high)/2;
            if(nums[mid] > target){
                high = mid - 1;
            
            }else if(nums[mid] < target){
                  low = mid + 1;
            }else{
                return mid;
            }

        }
        return -1;
    }
}

3.双指针

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

思路:因为数组是一个递增的顺序 平方的最大值一定是在数组的两边。所以考虑从两个向中间遍历不断找平方的最大值。注意有负数。

class Solution {
    public int[] sortedSquares(int[] nums) {
        //最大值一定在两侧取到
        int n = nums.length;
        int []res = new int[n];
        int left = 0,right = n - 1,k = n - 1;
        while(left <= right && k >= 0){
            if(Math.abs(nums[left]) < Math.abs(nums[right])){
                res[k--] = nums[right] * nums[right];
                right--;
            }else{
                res[k--] = nums[left] * nums[left];
                left++;
            }
        }
        return res;
    }
}

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0

思路:滑动窗口

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //left right表示一个窗口
        int left = 0;
        int right = 0;
        int minLen =Integer.MAX_VALUE;
        int sum = 0;
        while(right < nums.length){
                sum += nums[right];
                //寻找满足条件的最小左边界
                while(sum >= target){
                    //此时满足条件 记录此时的长度 并与最小长度比较
                     minLen = Math.min(minLen,right - left +1);
                     //缩减窗口 找最短的长度
                    sum -= nums[left];
                    left++;
                }
            	//此时的sum一定
                right++;
        }
        //若没有连续大于target的子数组 minLen不发生变化 则返回0
        return minLen ==  Integer.MAX_VALUE ? 0: minLen;
        
    }
}

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

思路:

​ 找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。

​ 用左指针指向每个开始的子字符 右指针不断向后遍历 若出现了重复元素记录之前的长度,直到出现重复元素为止。结束循环

​ 因为右指针停止循环的位置之前都没有出现重复元素,故下次直接从right开始遍历即可

​ 左指针向前移动一个单位,(注意:此时若不是第一个元素 需要把上一个的元素从set中移除)

改进思路(待实现):因为循环做了许多无效的循环 发现有重复字符时,可以直接把左指针移动到第一个重复字符的下一个位置即可。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == null||s==""){
            return 0;
        }
        if(s.length() == 1){
            return 1;
        }
        int left = 0;
        int right = 0;
        int maxLen = 0;
        Set<Character> set = new HashSet<>();
        char []chars = s.toCharArray();
        int len = chars.length;
        
        for(;left < len ; left++){
            //将上一个left的字符移除
            if(left != 0){
                set.remove(chars[left - 1]);
            }
            //到达right之前的元素一定是没有重复元素的 所以right无需移动
            while(right < len && !set.contains(chars[right])){
                set.add(chars[right]);
                right++;
            }
            maxLen = Math.max(maxLen,right - left);
        }
        return maxLen;
    }
}

4.模拟

59. 螺旋矩阵 II

Given a positive integer n, generate an n x n matrix filled with elements from 1 to n2 in spiral order.

思路:循环模拟 参考–Spiral Matrix II (模拟法,设定边界,代码简短清晰) - 螺旋矩阵 II

class Solution {
    public int[][] generateMatrix(int n) {
        //定义上下左右边界值
        int [][] matrix = new int[n][n];
        int top = 0;
        int bottom = n - 1;
        int left = 0;
        int right = n -1;
        int num = 1;
        int square = n * n;
        while(num <= square){
            //从左到右填充 [left,right]
            for(int i = left;i <= right;i++){
                matrix[top][i] = num;
                num++;
            }
            //对拐角处进行处理 也可以理解为缩小边界
            top++;
            //右边界从上到下填充
            for(int i = top;i <= bottom;i++){
                matrix[i][right] = num;
                num++;
            }
            right--;
            //下边界从右到左填充
            for(int i = right;i >= left;i--){
                matrix[bottom][i] = num;
                num++;
            }
            bottom--;
            //左边界从下到上填充
            for(int i = bottom;i >= top;i--){
                matrix[i][left] =num;
                num++;
            }
            left++;
        }
        return matrix;
    }
}

5.数学

1716. 计算力扣银行的钱

Hercy wants to save money for his first car. He puts money in the Leetcode bank every day.

He starts by putting in $1 on Monday, the first day. Every day from Tuesday to Sunday, he will put in $1 more than the day before. On every subsequent Monday, he will put in $1 more than the previous Monday.(比前一个周一存的钱数多1)
Given n, return the total amount of money he will have in the Leetcode bank at the end of the nth day.

思路:理解题意,等差数列求和问题,重温等差数列求和公式。。

class Solution {
    //等差数列公式: an = a1 +  (n-1) * d
    //等差数列求和公式: Sn = (a1 + an) * n / 2
    //显然每一个完整的周会比上一个完整的周多存7 设第一个完整的周为a1 则有an= a1 + (n-1) * 7 
    public int totalMoney(int n) {
        int weekNum = n / 7;         //有几个完整的一周
        int dayNum = n % 7;              //不完整的周有几天
        int a1 = (1+7)*7/2;         //第一个完整的周存的钱
        int an = a1 +(weekNum-1)*7; //第weekNum个完整的周存的钱
        int s1 = (a1+an)*weekNum /2;
        //还有不完整的周所存钱数
        //显然 不完整周的周存的钱为 weekNum+1
        //而接下来的每一天存的钱比前一天多1 公差为1 的等差数列求和问题
        int b1 = weekNum +1;
        int bn = b1 + (dayNum-1)*1;
        int s2 = (b1+bn)*dayNum / 2;
        return s1 + s2;
    }
}

142. 环形链表 II

力扣刷题总结_第1张图片

public class Solution {
    public ListNode detectCycle(ListNode head) {
        //先判断有无环
        if(head == null || head.next ==null){
            return null;
        }
        ListNode slow , fast;
        slow = fast = head;
        while(fast.next!=null && fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(fast == slow){
                //有环
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        return null;
        
        
    }
}

6.哈希

242. 有效的字母异位词

思路:简单哈希

class Solution {
    public boolean isAnagram(String s, String t) {
        int [] lettersNum = new int[26];
        char [] c1 = s.toCharArray();
        char [] c2 = t.toCharArray();
        for(int i = 0;i < s.length();i++){
            char c = c1[i];
            lettersNum[ c -'a']++;
        }
        for(int i = 0;i < t.length();i++){
            char c =c2[i];
            lettersNum[ c -'a']--;
        }
        for(int k = 0;k < 26;k++){
            if(lettersNum[k] != 0){
                return false;
            }
        }
        return true;
    }
}

349. 两个数组的交集

思路:利用set集合的不存在重复元素的特性找交集

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> num1Set = new HashSet<Integer>();
        Set<Integer> num2Set = new HashSet<Integer>();
        for(int i:nums1){
            num1Set.add(i);
        } 
        for(int i:nums2){
             num2Set.add(i);
        }
        return  subSection(num1Set,num2Set);

    }
    public int[] subSection(Set<Integer> set1,Set<Integer> set2){
        Set<Integer> minSet;
        Set<Integer> bigSet;
        Set<Integer> intersectionSet = new HashSet<Integer>();
        int len = 0;
        if(set1.size() > set2.size()){
            minSet = set2;
            bigSet = set1;
            
        }else{
            minSet = set1;
            bigSet = set2;
           
        }
        for(int i:minSet){
            if(bigSet.contains(i)){
                intersectionSet.add(i);
            }
        }
        int[] intersection = new int[intersectionSet.size()];
        for(int i:intersectionSet){
            intersection[len++] = i;
        }
        return intersection;

    }
}

1. 两数之和

思路:HashMap 保存键值对<元素值,元素下标>

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int res [] = new int [2];
        if(nums ==null || nums.length == 0){
            return res;
        }
        //一次性维护遍历过的元素值和它的下标 考虑到可以用HashMap
        Map<Integer,Integer> hashMap = new HashMap<>();
        for(int i = 0;i < nums.length;i++){
            //遍历每一个值 计算target - 当前值的结果,若结果已经存在在哈希表中,那么返回下标,并记录当前元素的下标。
            int temp = target - nums[i];
            if(hashMap.containsKey(temp)){
               res[0] = i;
               res[1] = hashMap.get(temp);
            }
            hashMap.put(nums[i],i);
        }    
        return res;

    }
}

454. 四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

思路:先计算 前两个数组nums1 和nums2的各个元素之和并将和记录为数组的键,出现的次数记为值。然后遍历num3和nums4,计算每一个元素的和,并查看 前两个数组的和中有无和为 - (num3[] + nums4[])的键,若有则记录次数

次数是将map中的次数进行累加,因为map中的键的每一个值和num3 nums4的一个和共同构成一个元组,而一个键对应出现次数代表有几种不同的元组。

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i < nums1.length;i++){
            for(int j = 0;j < nums2.length;j++ ){
                int sum = nums1[i] + nums2[j];
                if(map.containsKey(sum)){
                    map.put(sum , map.get(sum) + 1);
                }else{
                    map.put(sum,1);
                }
            }
        }
        int count = 0;
        for(int i = 0;i < nums3.length;i++){
            for(int j = 0;j < nums4.length;j++ ){
                int sum = nums3[i] + nums4[j];
                if(map.containsKey(0-sum)){
                  //仔细思考
                   count += map.get(0-sum);
                }
            }
        }
        return count;

    }
}

219. 存在重复元素 II

Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k.

思路
1.从前向后遍历 若存在相等的元素 2.看下标满足i - j <=k 满足返回true
3.若不存在相等的元素,则将元素和下标存入哈希表中,若存在相等的元素 但不满足下标关系 保存下标最大的那个

如果在下标 i 之前存在多个元素都和 nums[i] 相等,为了判断是否存在满足 nums[i]=nums[j] 且 i−j≤k 的下标 j,应该在这些元素中寻找下标最大的元素,将最大下标记为 j,判断i−j≤k 是否成立。

如果i−j≤k,则找到了两个符合要求的下标 j 和 i;如果i−j>k,则在下标 i 之前不存在任何元素满足与 nums[i] 相等且下标差的绝对值不超过 k,理由如下:

假设存在下标 j’ 满足 j’ < j < i。
且nums[j’ ]=nums[j]=nums[i],则 i - j’ > i - j,由于 i - j > k,因此必有 i - j’ > k

class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        //1.从前向后遍历 若存在相等的元素 
        //看下标满足i - j <=k 满足返回true
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i < nums.length;i++){
            int temp = nums[i];
            if(map.containsKey(temp) && (i - map.get(temp) <= k)){
                return true;
            }
            //put在此映射中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换。
            //始终保存的都是重复元素的最大下标, 
            map.put(temp,i);
        }
         return false;
    }
   
}

你可能感兴趣的:(leetcode,算法)