leetCode刷题记录

文章目录

    • 每日一题
      • 1630. 等差子数组
    • hot100题
      • 1. 两数之和
      • 2. 两数相加
      • 3. 无重复字符的最长子串
      • 4. 寻找两个正序数组的中位数
      • 5. 最长回文子串

每日一题

1630. 等差子数组

1630. 等差子数组

  • 先直接暴力,过了再说
public List<Boolean> checkArithmeticSubarrays(int[] nums, int[] l, int[] r) {
        ArrayList<Boolean> ans = new ArrayList<>();
        for(int i=0;i<l.length;i++){
            int[] arrs = Arrays.copyOfRange(nums, l[i], r[i]+1);
            Arrays.sort(arrs);
            int j=1;
            while (arrs.length>1&&j<arrs.length){
                if (arrs[j] - arrs[j -1] != arrs[1] - arrs[0]) {
                    ans.add(false);
                    break;
                }
                j++;
            }
            if(j==arrs.length) ans.add(true);
        }
        return ans;
    }
  • 再追求效率

高效率的做法
一次遍历找到最大最小值 那么公差 d = (max-min)/(len-1) 不能整除直接return false
然后再遍历,第i项 a[i] = a[0]+id 不如就算出来i=(a[i]-a[0])/d 若不能整除false 或者出现重复i false Hash[d]记录是否1~d都出现过 真是等差一定会0~len-1各出现一次的
哇:两次遍历2
O(n)+一个O(n)空间的数组就OK了,完全不需要排序

public List<Boolean> checkArithmeticSubarrays(int[] nums, int[] l, int[] r) {
        ArrayList<Boolean> ans = new ArrayList<>();
        int[] Hash = new int[nums.length];
        for (int i = 0; i < l.length; i++) {
            Arrays.fill(Hash, 0);
            int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
            for (int j = l[i]; j <= r[i]; j++) {
                min = Math.min(min, nums[j]);
                max = Math.max(max, nums[j]);
            }
            if ((max - min) % (r[i] - l[i]) != 0) {
                ans.add(false);
            } else if (max == min) {//所有数字都一样
                ans.add(true);
            } else {
                int d = (max - min) / (r[i] - l[i]);
                boolean flag = true;
                for (int j = l[i]; j <= r[i]; j++) {
                    if((nums[j]-min)%d!=0) {//注意这里也得判断
                        flag = false;
                        break;
                    }
                    int t = (nums[j] - min) / d;
                    if (Hash[t] == 1) {//等差数列一定每个都一份的 否则一定会有重复
                        flag = false;
                        break;
                    } else Hash[t] = 1;
                }
                ans.add(flag);
            }
        }

        return ans;
    }

hot100题

1. 两数之和

1. 两数之和

public int[] twoSum(int[] nums, int target) {
    HashMap<Integer, Integer> map = new HashMap<>();//存放元素下表
    for (int i = 0; i < nums.length; i++) {
        if(map.containsKey(target-nums[i])){//map.containsKey()时间复杂度是O(1) !!放心大胆用
            return new int[]{i,map.get(target-nums[i])};
        }
        map.put(nums[i],i);//记录下标
    }
    return null;
}

知识点:java map.containsKey()时间复杂度是O(1), 访问大胆用即可
用map做Hash,简直不要太爽

2. 两数相加

2. 两数相加

节点个数100,一眼大数,自己再写一次大数吧。确实很简单的大数

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode zero = new ListNode(0);
    ListNode head = new ListNode(-1);//头结点使得操作统一
    ListNode tail = head;
    int carry = 0;
    while (l1 != null || l2 != null) {
        if (l1 == null) l1 = zero;
        if (l2 == null) l2 = zero;
        //System.out.println(l1.val+" "+l2.val);
        tail.next = new ListNode((l1.val + l2.val + carry) % 10);
        tail = tail.next;
        carry = (l1.val + l2.val + carry) / 10;

        l1 = l1.next;
        l2 = l2.next;
    }
    if (carry!=0){
        tail.next = new ListNode(carry);
    }
    return head.next;
}

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

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

剑指里好像刷过这题,好像就是dp+Hash, 直接上

 public int lengthOfLongestSubstring(String s) {
     int max = 0;
     int l = 0,r = 0;//首尾双指针 多好 (子串一定是连续的呀)
     int[] Hash = new int[128];//ASCII只有128个
     char[] chars = s.toCharArray();//习惯用数组
     for (r = 0; r < chars.length; r++) {
         while (Hash[chars[r]]>0){
             Hash[chars[l++]]--;
         }
         Hash[chars[r]]++;
         max = Math.max(max,r-l+1);
     }
     return max;
 }

果然是做过,一点难度没有觉察到
但是看题解发现了java的HashSet专门用来判重
(HashSet底层就是HashMap,也就是专门的Hash表去了下重复,所以contains方法也是O(1) 直接看源码就map.containsKey(o))

public int lengthOfLongestSubstring(String s) {
    int max = 0;
    int l = 0,r = 0;//首尾双指针 多好 (子串一定是连续的呀)
    HashSet<Character> set = new HashSet<>();
    char[] chars = s.toCharArray();//习惯用数组
    for (r = 0; r < chars.length; r++) {
        while (set.contains(chars[r])){
            set.remove(chars[l++]);//remove应该也是O(1) Hash表嘛 都是O(1)
        }
        set.add(chars[r]);
        max = Math.max(max,r-l+1);
    }
    return max;
}

确实好用~

4. 寻找两个正序数组的中位数

4. 寻找两个正序数组的中位数

先牺牲空间归并一下,时间完全满足O(m+n),这也能过,太水了~ 还hard

public class LC4 {

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int[] ans = new int[nums1.length + nums2.length];
        int k = 0;
        int i = 0, j = 0;
        while (i < nums1.length && j < nums2.length) {
            if(nums1[i]<nums2[j]){
                ans[k++]=nums1[i];
                i++;
            }else {
                ans[k++]=nums2[j];
                j++;
            }
        }
        while (i<nums1.length) ans[k++] = nums1[i++];
        while (j<nums2.length) ans[k++] = nums2[j++];

        /*if(k%2==1) return ans[k/2];
        return (ans[k/2]+ans[k/2-1])/2.0;*/
        //小trick
        return (ans[(k-1)/2]+ans[k/2])/2.0;
    }

    public static void main(String[] args) {
        test(new int[]{1,3},new int[]{2});
        test(new int[]{1,2},new int[]{3,4});
        test(new int[]{1,3},new int[]{2,3,4,5});//1 2 3 3 4 5
    }

    static void test(int[] A,int[] B){
        LC4 t = new LC4();
        double ans = t.findMedianSortedArrays(A, B);
        System.out.println(ans);
    }
}
  • 空间限制O(1) 才能算hard

先想到不存储,直接找到第(l1+l2)/2旁边就截止,不就将空间省下来了吗,没想到边界竟然这么麻烦
还是在用到了奇偶都可以用 (ans[(k-1)/2]+ans[k/2]) /2.0 得到中位数的trick

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length + nums2.length;
        int m1 = (n - 1) / 2;
        int m2 = n / 2;
        int k = 0, ans1 = 0, ans2 = 0;
        //不管奇偶 找这个两个取平均即可
        int i1 = 0, i2 = 0;
        while (i1 < nums1.length && i2 < nums2.length) {
            if(nums1[i1] < nums2[i2]) {
                if (k == m1) {
                    ans1 = nums1[i1];
                }
                if (k == m2) {
                    ans2 = nums1[i1];
                    return (ans1 + ans2) / 2.0;
                }
                i1++;
                k++;

            } else {

                if (k == m1) {
                    ans1 = nums2[i2];
                }
                if (k == m2) {
                    ans2 = nums2[i2];
                    return (ans1 + ans2) / 2.0;
                }
                i2++;
                k++;
            }
        }

        while (i1 < nums1.length) {
            if (k == m1) {
                ans1 = nums1[i1];
            }
            if (k == m2) {
                ans2 = nums1[i1];
                return (ans1 + ans2) / 2.0;
            }
            i1++;
            k++;
        }

        while (i2 < nums2.length) {
            if (k == m1) {
                ans1 = nums2[i2];
            }
            if (k == m2) {
                ans2 = nums2[i2];
                return (ans1 + ans2) / 2.0;
            }
            i2++;
            k++;
        }
        return -1;
    }
  • 看题解 简化代码

trick:
总长度 n = l1 + l2
奇数: k/2
偶数:k/2和(k-1)/2
都遍历到下标 k/2
或者 都 k/2和(k-1)/2取平均 (奇数时二者相等 就自然统一了)

或者直接记录pre和now即可
if (n1 < l1 && nums1[n1] < nums2[n2])改成if (n1 < l1 && (n2>=l2|| nums1[n1] < nums2[n2]))
另一个越界了也是我+,小技巧,省了大代码

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int l1 = nums1.length;
        int l2 = nums2.length;
        int len = l1 + l2;
        int n1 = 0, n2 = 0;//
        int pre = 0, now = 0;//记录上次的结果

        int T = len / 2 +1 ;//找到这就够了 下标len/2,但其实是(len/2)+1多个
        while (T-- > 0) {
            pre = now;
            if (n1 < l1 && (n2>=l2|| nums1[n1] < nums2[n2])) {//nums2越界了也是我+  k2>=l2写前面短路,否则nums2[k2]会越界
                now = nums1[n1++];
            }else {
                now=nums2[n2++];//不用想 肯定走这边 (只有2种逻辑可以走)
            }
        }

        if((len&1)==1) return now;//奇数
        return (now+pre)/2.0;
    }

5. 最长回文子串

5. 最长回文子串

  • 先用最简单的dp做一下,先通过再说
public String longestPalindrome(String s) {
    char[] arr = s.toCharArray();
    int l = arr.length;
    int[][] dp = new int[l][l];

    int start=0,maxl=1;//维护一个起始下标和长度作为最终结果返回
    for (int i = 0; i < dp.length; i++) {
        dp[i][i]=1;//长度为1
        if(i>0&&arr[i]==arr[i-1]) {
            dp[i-1][i]=1;//长度为2 千万注意i
            start=i-1;//i-1不是i 小细节害死人
            maxl=2;
        }
    }
    for(int len =2;len<l;len++ ){//长度为3开始遍历  [i,i+len]长度为len+1
        for (int i = 0; i+len < l; i++) {
            int j = i+len;
            if(arr[i]==arr[j]) dp[i][j] = dp[i+1][j-1];
            else dp[i][j]=0;//直接就是0 不是回文子串
            if(dp[i][j]==1&&len+1>maxl){//len+1才是实际长度
                maxl=len+1;
                start=i;
            }
        }
    }
    return new String(arr,start,maxl);//直接取数组部分初始化为String
}

代码有点长,想优化一下,总感觉优化了个寂寞:

//感觉代码有点长 休整一下
public String longestPalindrome(String s) {
    char[] arr = s.toCharArray();
    int l = arr.length;
    boolean[][] dp = new boolean[l][l];
    for (int i = 0; i < dp.length; i++) dp[i][i] = true;//长度为1

    int start = 0, maxl = 1;//维护一个起始下标和长度作为最终结果返回
    for (int len = 2; len <= l; len++) {//长度真的从2开始遍历
        for (int i = 0; i + len - 1 < l; i++) {
            int j = i + len - 1;//[i,i+len-1] 长度正好是len

            if (arr[i] != arr[j]) dp[i][j] = false;//非回文子串
            else if (len == 2) dp[i][j] = true;//长度为2的边界
            else dp[i][j] = dp[i + 1][j - 1];

            if (dp[i][j] && len > maxl) {//len+1才是实际长度
                maxl = len;
                start = i;
            }
        }
    }
    //return new String(arr, start, maxl);//直接取数组部分初始化为String
    return s.substring(start, start + maxl);//参数竟然是首尾下标
}

时间O(n^2) , 空间O(n^2)

接下来就是老老实实看题解,一步一步发掘最优解了

  • 方法二:中心扩展算法

leetCode刷题记录_第1张图片

此法,时间还是O(n^2),但是空间变成O(1)了
注意回文中心有两种,长度为1或者长度为2
长度为1如: a => bab 也就是奇数子串
长度为2如: aa=> baab 也就是偶数子串
所以得以回文中心为下标专门写一个扩散函数

我的写法:

public String longestPalindrome(String s) {
        char[] arr = s.toCharArray();
        int l = arr.length;
        int begin = 0, maxl = 1;
        for (int i = 0; i < l - 1; i++) {
            Integer[] ok1 = expandAroundCenter(i,i,arr);
            Integer[] ok2 = expandAroundCenter(i,i+1,arr);
            if(ok1!=null&&ok1[1]-ok1[0]+1>maxl){
                maxl = ok1[1] - ok1[0]+1;
                begin = ok1[0];
            }
            if(ok2!=null&&ok2[1]-ok2[0]+1>maxl){
                maxl = ok2[1] - ok2[0]+1;
                begin = ok2[0];
            }
        }
        return s.substring(begin, begin + maxl);//参数竟然是首尾下标 当然还是左闭右开
    }

    public Integer[] expandAroundCenter(int i, int j, char[] arr) {
        int k = 0;
        while (i - k >= 0 && j + k < arr.length && arr[i-k]==arr[j+k]) k++;
        k--;
        Integer[] ans = null;
        if(k>-1) ans = new Integer[]{i-k,j+k};//返回首尾下标
        return ans;
    }

题解写法:

public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                //★ 奇偶奇迹般地统一了   i=1 aaaa和aaab 举例验证 是正确的
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    public int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return right - left - 1;//(right-1)-(left+1)+1
    }

巧妙之处在于,不管结果是奇数还是长偶数长,都给你一个式子统一了:

start = i - (len - 1) / 2;
end = i + len / 2;

eg: i=1 “aaaa”
expandAroundCenter(1,1) => 3
expandAroundCenter(1,2) => 4
len取4 此时 :
start = i - (len - 1) / 2 = 1 - 3/2 = 0
end = i + len / 2 = 1+4/2 = 3
正确
eg: i=1 “aaab”
expandAroundCenter(1,1) => 3
expandAroundCenter(1,2) => 2
len取3 此时 :
start = i - (len - 1) / 2 = 1 - 2/2 = 0
end = i + len / 2 = 1+3/2 = 2
正确

理解:

start = i - (len - 1) / 2; //i左偏了 所以 (len-1)
end = i + len / 2;  //右边正常
//然后举例论证 2个不同的例子(1奇1偶)能正确 基本全局都正确了

在这里插入图片描述
时间紧迫,不看了

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