Leetcode 1-5

1:两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

 

思路:通过哈希表存储出现过的数据及其索引。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]), i};
            }
            map.put(nums[i], i);
        }
        return null;
    }
}

 

 

2:两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

  • 每个链表中的节点数在范围 [1, 100] 内
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

 

思路:使用循环依次对每一位进行相加,把进制数携带到下一个循环,同时使用两个对象记录开始的链表和执行中的链表对象。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(-1), pre = head;
        int t = 0;
        while(l1 != null || l2!=null || t!=0){
            int sum = t;
            if(l1 != null){
                sum += l1.val;
                l1 = l1.next;
            }
            if(l2 != null){
                sum += l2.val;
                l2 = l2.next;
            }
            t = sum/10;
            pre.next = new ListNode(sum%10);
            pre = pre.next;
        }
        return head.next;
    }
}

 

 

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

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

  • 0 <= s.length <= 5 * 104
  • s 由英文字母、数字、符号和空格组成

 

思路:循环读取每一个字符,1、使用哈希表记录其所在的下标索引,2、使用变量记录当前记录的 "滑动窗口" 的起始下标,在数据重复时判断其是否在起始下标之后,如果是再重置 "滑动窗口"

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap<Character, Integer> map = new HashMap<>();
        int maxLength = 0;
        for (int start = 0,end = 0; end < s.length(); end++) {
            if(map.containsKey(s.charAt(end))   &&   map.get(s.charAt(end))>=start){
                start = map.get(s.charAt(end))+1;
                map.remove(s.charAt(end));
            }else{
                maxLength = Math.max(maxLength, end  - start + 1);
            }
            map.put(s.charAt(end), end);
        }
        return maxLength;
    }
}

 

 

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

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

 

 

思路:

(伪)一开始没注意是正序,导致想当然思考先进行排序,再去寻找中位数,因为中位数在总数为奇数时是其中的一个数,偶数时是两个数平均值,所以这里统一计算下标为  (m+n+1)/2  和 (m+n+2)/2 的平均值,使用TreeMap来实现排序以及记录出现的次数,代码如下:

class Solution {
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        Map<Double, Integer> hashMap = new TreeMap<>();
        int i1 = nums1.length + nums2.length;
        for (int i = 1; i <= i1; i++) {
            if(i > nums1.length){
                int i2 = i - nums1.length - 1;
                hashMap.put((double) nums2[i2], hashMap.get((double)nums2[i2])== null?1:hashMap.get((double)nums2[i2])+1);
            }else{
                hashMap.put((double) nums1[i-1], hashMap.get((double)nums1[i-1])==null?1:hashMap.get((double)nums1[i-1])+1);
            }
        }
        if(i1 == 1) return hashMap.keySet().stream().findFirst().get();
        return (getNum(hashMap, (i1+1)/2) + getNum(hashMap, (i1+2)/2))/2;
    }

    static Double getNum(Map<Double, Integer> hashMap, int i){
        int count = 0;
        for (Double m : hashMap.keySet()) {
            count += hashMap.get(m);
            if(count>=i){
                return m;
            }
        }
        return (double) 0;
    }
}

结果自然是:

执行用时:27 ms, 在所有 Java 提交中击败了5.13%的用户
内存消耗:40 MB, 在所有 Java 提交中击败了5.07%的用户
 
 
真正思路:获取中位数方式还是取正序第  (m+n+1)/2  和 (m+n+2)/2 的平均值,而如何得到第 K 个值就比较困难,这里是通过二分法取 K/2 的值,然后将两个数组的指针从最左侧开始,每次比较他们的索引值大小,将小的索引向右侧移动 K/2 ,直到 K ==1 时(步数走完,两个数组中的较小的数就是中位数)或其中一个数组的指针移到了最右侧(直接从另外一个数组下标移动剩下的 K 值),那么得到中位数就很简单了。
class Solution {
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int i1 = nums1.length + nums2.length;
        return (getNum(nums1, 0 , nums2, 0, (i1+1)/2) + getNum(nums1, 0 , nums2, 0, (i1+2)/2))/2.0;
    }

    static int getNum(int[] nums1,int i1, int[] nums2, int i2, int k){
        if(i1 >= nums1.length){     // 为空
            return nums2[i2+k-1];
        }
        if(i2 >= nums2.length){     // 为空
            return nums1[i1+k-1];
        }
        if(k == 1){
            return Math.min(nums1[i1], nums2[i2]);
        }

        int avg1 = (i1 + k/2 -1 < nums1.length) ? nums1[i1 + k/2 -1] : Integer.MAX_VALUE;
        int avg2 = (i2 + k/2 -1 < nums2.length) ?  nums2[i2 + k/2 -1] : Integer.MAX_VALUE;
        if(avg1 < avg2){
            return getNum(nums1, i1 + k/2, nums2, i2, k-k/2);
        }else{
            return getNum(nums1, i1, nums2, i2 + k/2, k-k/2);

        }

    }
}

对于i1、i2 ,表示下标索引, K 表示移动的位数,所以每次涉及 K 的数组取值时都会 -1来保证取到的是正确的下标数据。

 
 

5:最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。
 
 

1、中心扩散:

遍历,去取每次的字符向两边扩展判断,直到两边的字符不同,如果长度大于则记录起始索引和长度。
public static String longestPalindrome(String s) {
        char[] chars = s.toCharArray();
        if(chars.length <= 1){
            return s.substring(0,1);
        }
        int start = 0, maxLength = 0;
        for (int i = 0; i < chars.length; i++) {
            int length = getLength(chars, i, i);
            int length2 = getLength(chars, i,i+1);
            int realLength = Math.max(length2, length);
            if(realLength > maxLength){
                start = i - (realLength+1)/2 + 1;
                maxLength = realLength;
            }
        }
        return s.substring(start, start+maxLength);
    }

    static int getLength(char[] chars, int left, int right){
        while(left >= 0 && right < chars.length && chars[left] == chars[right]){
            left--;
            right++;
        }
        return (right-1)-(left+1)+1;
    }

这题因为有121和1122两种回文格式,所以在调用 getLength 时两种取值都使用,防止漏掉其中一种

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

2、动态规划。

将左右两个比较的索引划分为一个二维数组,如果该索引中间的字符串是回文,那么存 true, 否则存 false。
其次,1、当中间的字符串长度小于2时(也就是1),且当前左右下标对应的字符相同时,中间的字符串一定是回文,假设左索引是 i ,右下标是 j ,那么 (j-1) - (i+1) + 1< 2,得出 j - i <3
2、如果左右两边的字符相同,那么其中间是否为回文决定于其向中间靠拢一格的字符串是否是回文。也就是 array[i] = array[j] ,如果 二维数组 dp[i+1][j-1] = true,那么 dp[i][j] = true,如果 dp[i+1][j-1] = false,那么 dp[i][j] = false。
3、二维数组对角线上的值都是 true,因为其都是单独的字符。
class Solution {
    public String longestPalindrome(String s) {
        char[] chars = s.toCharArray();
        if(chars.length <= 1){
            return s.substring(0,1);
        }
        int start = 0, maxLength = 1;
        boolean[][] bp = new boolean[chars.length][chars.length];
        for (int i = 0; i < chars.length; i++) {
            bp[i][i] = true;
        }
        for (int j = 0; j < chars.length; j++) {
            for (int i = 0; i < j; i++) {
                if(chars[i] != chars[j]){
                    bp[i][j] = false;
                }else{
                    if(j-i < 3){
                        bp[i][j] = true;
                    }else{
                        bp[i][j] = bp[i+1][j-1];
                    }
                }

                if(bp[i][j] && (j-i+1)>maxLength){
                    maxLength = j-i+1;
                    start = i;
                }
            }
        }
        return s.substring(start, start+maxLength);
    }
}

 同样使用两个嵌套循环,同时嵌套添加数组数据,所以时间、空间复杂度都是O(n^2),效率较低。

 
posted on 2022-01-09 00:01  萌新J  阅读(39)  评论(0编辑  收藏  举报