算法题解(Leetcode 1-5:两数之和、两数相加、无重复字符的最长子串、寻找两个正序数组的中位数、最长回文子串)

LeetCode官网:https://leetcode-cn.com/problemset/all/

1. 两数之和 8/27

https://leetcode-cn.com/problems/two-sum/

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

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

你可以按任意顺序返回答案。
在这里插入图片描述
在这里插入图片描述


解法一:

暴力解决:遍历所有情况看相加是否等于目标和,如果符合直接输出。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //暴力解决,两层循环
        for(int i=0; i<nums.length; i++){
            for(int j=i+1; j<nums.length;j++){
                if(nums[i] + nums[j] == target){
                    return new int[] {i,j};
                }
            }
        }
        return null;
    }
}

时间复杂度:两层 for 循环,O(n²)

空间复杂度:O(1)

在这里插入图片描述


解法二

遍历一次记下所有的nums元素,每次看数组当中是否存在taget-nums[i]这个值。若存在即返回下标为i与taget-nums[i]这个值的下标。那么我们就使用hash表去记录数组值为key下标为value。如下图所示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //创建一个HashMap
        Map<Integer,Integer> map = new HashMap<>();

        //遍历一次记下所有的nums元素,每次看数组当中是否存在taget-nums[i]这个值。
        //若存在 即返回下标为i与taget-nums[i]这个值的下标。
        //那么我们就使用hash表去记录数组值为key下标为value
        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); //不存在taget-nums[i]的值 就将该数存入map中
        }
        throw new IllegalArgumentException("No nums");
    }
}

在这里插入图片描述
时间复杂度:O(N)O(N),其中 NN 是数组中的元素数量。对于每一个元素 x,我们可以 O(1)O(1) 地寻找 target - x。

空间复杂度:O(N)O(N),其中 NN 是数组中的元素数量。主要为哈希表的开销。

2. 两数相加 8/29

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

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

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

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


解析:

在这里插入图片描述
链表最左边表示个位数,代表 342 + 465 =807 。


思路:
在这里插入图片描述

dummy指针用于占位,carr指针用于遍历
在这里插入图片描述


Java实现:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(0); //占位指针
        ListNode curr = dummyHead; //当前遍历指针
        int carry = 0; //进位值
        while(l1 != null || l2 != null){
            int x = (l1 == null) ? 0 : l1.val; // l1的值
            int y = (l2 == null) ? 0 : l2.val; // l2的值
            int sum = x + y + carry; //相加的值
            carry = sum / 10; //计算进位值,得:1
            //存放当前指针的下一个值 如:12%10=2,则存放 2在curr.next
            curr.next = new ListNode(sum % 10); 
            //指针移动
            curr = curr.next;
           
            if(l1 != null) l1 = l1.next; //移动l1指针
            if(l2 != null) l2 = l2.next; //移动l2指针
        }
        //如果进位值大于0,则把进位值存放在 当前指针的下一个节点
        if(carry > 0) curr.next = new ListNode(carry);
        return dummyHead.next;

    }
}

在这里插入图片描述

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

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

在这里插入图片描述
在这里插入图片描述
我们不妨以示例一中的字符串 \texttt{abcabcbb}abcabcbb 为例,找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。对于示例一中的字符串,我们列举出这些结果,其中括号中表示选中的字符以及最长的字符串:

在这里插入图片描述
我们可以使用「滑动窗口」来解决这个问题了:

  • 我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」;

  • 在每一步的操作中,我们会将左指针向右移动一格,表示 我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着 以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;

在枚举结束后,我们找到的最长的子串的长度即为答案。

判断重复字符

在上面的流程中,我们还需要使用一种数据结构来判断 是否有重复的字符,常用的数据结构为哈希集合(即 Java 中的 HashSet)。在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。


Java实现:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s == "") return 0;
        //哈希集合,判断字符串是否出现过
        Set<Character> occ = new HashSet<Character>();
        //字符串长度
        int n = s.length();
        //右指针 -1,相当于在字符串的左边界的左侧,还没开始移动
        int rightKey = -1;
        //最长字符串的长度
        int maxLength = 0;
        //遍历字符串
        for(int i=0; i<n; i++){
            if(i != 0){
                //左指针向右移动一格,就移除一个字符
                occ.remove(s.charAt(i-1));
            }
            //当右指针不超出字符串长度并且移动过程中没发现有重复字符,右指针就移动
            while(rightKey + 1 < n && !occ.contains(s.charAt(rightKey+1))){
                //添加字符到哈希集合
                occ.add(s.charAt(rightKey + 1));
                //不断移动右指针
                ++rightKey;
            }
            //当前的字符串长度  第i到rightKey个字符是一个无重复的字符字串,长度为 rightKey - i + 1
            maxLength = Math.max(maxLength, rightKey - i + 1);
        }
        //把最长的字符串长度返回
        return maxLength;


    }
}

在这里插入图片描述

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

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

在这里插入图片描述
在这里插入图片描述


方式一: 将两个数组的值合并到一个集合去,然后排序;排好序后判断奇偶数情况 计算中位数的值。

Java实现:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length_n1 = nums1.length; //数组1的长度
        int length_n2 = nums2.length; //数组2的长度
        List<Integer> list = new ArrayList<>(); //新建一个集合来存放合并后的数据
        double m = 0.00; //中位数

        //添加数组1 到集合
        if(nums1 != null){
            for(int k=0; k<length_n1; k++){
                list.add(nums1[k]);
            }
        }
        //添加数组2 到集合
        if(nums2 != null){
            for(int i=0; i<length_n2; i++){
                list.add(nums2[i]);
            }
        }
        //集合内排序
        Collections.sort(list);
        //奇数
        if(list.size() % 2 != 0){ 
            m = (double) list.get(list.size() / 2);
        }
        //偶数
        else{
            m = (double) (list.get(list.size() / 2 - 1) + list.get(list.size() / 2)) / 2;
        }
        
        return m;
    }
}

在这里插入图片描述

5. 最长回文子串 9/1

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

在这里插入图片描述

在这里插入图片描述


解析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

动态规划:

class Solution {
    public String longestPalindrome(String s) {
        //字符串长度
        int s_length = s.length(); 
        //如果长度为1,则一定是回文字符串,直接返回
        if(s_length < 2){ 
            return s;
        }

        //定义子字符串起始 到 是回文字符串末尾 的最大长度
        int maxLen = 1;
        //定义子字符串起下标
        int start = 0;

        //dp[i][j] 表示 s[i..j] 是否是回文串
        boolean[][] dp = new boolean[s_length][s_length];

        //对角线赋值,对角线上为字符本身,是回文,赋值true
        for(int i=0; i<s_length; i++){
            dp[i][i] = true;
        }

        //从左下角先赋值 从1开始是因为对角线已经赋值了 先填写 列的值
        for(int j=1; j<s_length; j++){
            for(int i=0; i<j; i++){
                if(s.charAt(j) != s.charAt(i)){
                    dp[i][j] = false;
                } else { //外层字符相等
                    if(j - i < 3){  //字符串长度为2且相等 返回true
                        dp[i][j] = true;
                    } else { //状态转移 到内层字符串
                        dp[i][j] = dp[i+1][j-1];
                    }
                }
                // 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
                if(dp[i][j] && j-i+1 > maxLen){
                    maxLen = j-i+1;
                    start = i;
                }
            }
        }
        //字符串截取返回
        return s.substring(start, start + maxLen);
    }
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值