LeetCode算法刷题记录(随时更新)


之前一直是用LeetCode刷题,不能称之为刷题,往往做了几十道题就不做了,这次发现LeetCode出了中文版,又想巩固和加深自己的算法知识,因此改造之前废弃的文章。
本篇文章题目来源是 力扣,其实对应就是英文的LeetCode,刷题的目的是为了巩固和复习自己的数据结构与算法知识,平常不用,基本上都忘记了,时常回顾与总结,有助于自己加深意识。这篇文章会讲解解题思路和常见及认为比较好的解题发放放上,有助于拓宽思路,本篇文章依然是以Java为基础语言编写,若是认为有任何疑问和建议欢迎评论。

1.两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:

	给定 nums = [2, 7, 11, 15], target = 9
	因为 nums[0] + nums[1] = 2 + 7 = 9
	所以返回 [0, 1]

解法1(暴力穷举法,适用于数量较少时):
解题思路:略

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] indexes = new int[2];
        if(nums == null)
            return null;
        for(int i = 0;i<nums.length;i++) {
            for(int j=i+1;j<nums.length;j++) {
                if(nums[i] + nums[j] == target) {
                    indexes[0] = i;
                    indexes[1] = j;
                    break;
                }
            }
        }
        return indexes;
    }
}

解法2(借助HashMap):
解题思路:
1.初始化一个map用于存储已经遍历过的值,便于后期查询,key为遍历过的值,value为遍历过的值在数组中的位置
2.将target-当前值,到第一步的map中进行查找值是否存在,存在直接将当前值的index和map中查找到的key对应的value取出返回
3.若不存在,则将当前值与它的位置存储到map

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

2.两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:

	输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
	输出:7 -> 0 -> 8
	原因:342 + 465 = 807

解题思路:
1.前提:掌握链表结构(这个一定要掌握,基础中的基础!)
2.返回的链表newNode一定是在起始位置,因此通过浅拷贝赋值给另一个链表current,current对象的属性变化的同时,newNode属性值也会发生变化,但是newNode始终维持在第一个current对象,current对象却会随着遍历会主键变为next
3.正常的将对10取余放于当前ListNode的val,判断>=10进行加1操作(由于题目中说明链表中的每个val是<10的,因此完全可以使用boolean判断是下一位否加1即可,减少使用空间)
4.对于边界值,比如两个链表长度不同、又比如最后一个值相加大于等于10,则需要新生成一个ListNode赋值为1。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode newNode = new ListNode(0);
        ListNode current = newNode;
        boolean nextAdd = false;
        while (l1 != null || l2 != null) {
            int l1Val = l1 == null ? 0 : l1.val;
            int l2Val = l2 == null ? 0 : l2.val;
            current.next = new ListNode((l1Val + l2Val + (nextAdd ? 1 : 0)) % 10);
            nextAdd = (l1Val + l2Val + (nextAdd ? 1 : 0)) >= 10;
            l1 = l1 == null ? null : l1.next;
            l2 = l2 == null ? null : l2.next;
            current = current.next;
        }
        if (nextAdd) {
            current.next = new ListNode(1);
        }
        return newNode.next;
    }
}

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

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

示例 1:

	输入: "abcabcbb"
	输出: 3 
	解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

	输入: "bbbbb"
	输出: 1
	解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
	示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
解法1(暴力穷举法,提交运行超时):
解题思路:
1.穷举字符串的所有子串,根据所有的子串长度得出最大的非重复子串长度
2.增加边界判断
3.因为是3层for循环,因此时间复杂度为O(n3),由于每次循环初始化一个Set进行存储值,因此空间复杂度为O(n)。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0)
            return 0;
        int sum = 0;
        for (int i = 0; i < s.length(); i++) {
            for (int j = i; j < s.length(); j++) {
                Set<Character> set = new HashSet<>();
                boolean contains = false;
                for (int k = i; k < j + 1; k++) {
                    if (set.contains(s.charAt(k))) {
                        sum = sum < k - i ? k - i : sum;
                        contains = true;
                        break;
                    }
                    set.add(s.charAt(k));
                }
                if (!contains) {
                    sum = sum < j - i + 1 ? j - i + 1 : sum;
                }
            }
        }
        return sum;
    }
}

解法2(滑动窗口):
解题思路:
1.首先了解什么是滑动窗口,遍历一个字符串,若是窗口中没有该字符,则加入窗口,若窗口内有该字符,则找到上一个该字符,将该字符及其之前的字符全部删掉,然后将新的字符加入到窗口中。如图所示,遍历前三个a1,b1,c1(为了方便与后续同样字符区分开,加入下标,实际下标不存在)的时候,窗口中没有这三个字符,因此依次加入,遍历第4个字符a2的时候,发现窗口内有字符a1,因此将窗口内的字符a1及其之前的字符删除,窗口从b1开始,并将a2加入,继续遍历。
在这里插入图片描述
2.Java中通过Map进行构建窗口,key为字符,value为当前字符的在字符创中的位置,增加概念窗口最左边位置(上一个相同字符位置index+1,初始化为0)和当前遍历字符的位置,用于计算窗口长度(窗口长度=当前字符位置-窗口最左侧位置),通过put直接改变相同字符的value,最终得出字符串中最大的子串的长度。
3.判断边界情况,如字符串为空或空字符串。
4.由于只遍历一遍,因此时间复杂度为O(n),因为只有一个Map进行存储,因此空间复杂度为O(n)。
5.若是求字符串子串相关问题,可优先考虑滑动窗口算法。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0)
            return 0;
        int sum = 0;
        Map<Character, Integer> childStrMap = new HashMap<>();
        int left = 0;
        for (int i = 0; i < s.length(); i++) {
            if (childStrMap.containsKey(s.charAt(i))) {
                left = Math.max(childStrMap.get(s.charAt(i)) + 1, left);
            }
            childStrMap.put(s.charAt(i), i);
            sum = Math.max((i - left + 1), sum);
        }
        return sum;
    }
}

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

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。
示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

解法1(概念法,时间复杂度O(m+n),不满足题目时间复杂度要求):
解题思路:
将两个有序数组合并为一个有序数组,然后根据中位数的概念取得中位数。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
		if (nums1.length == 0 && nums2.length == 0)
            return 0.0;
        // 将两个数组合并为一个有序数组
        int newLength = nums1.length + nums2.length;
        int[] nums = new int[newLength];
        int i = 0, j = 0;
        for (int k = 0; k < newLength; k++) {
            if (i >= nums1.length) {
                nums[k] = nums2[j];
                j++;
                continue;
            }
            if (j >= nums2.length) {
                nums[k] = nums1[i];
                i++;
                continue;
            }
            if (nums1[i] < nums2[j]) {
                nums[k] = nums1[i];
                i++;
            } else {
                nums[k] = nums2[j];
                j++;
            }
        }
        if (newLength % 2 == 0) {
            return ((double) (nums[newLength / 2 - 1] + nums[newLength / 2])) / 2;
        } else {
            return (double) nums[newLength / 2];
        }
    }
}

解法2(从小到大取两个数组中第K大的值,K为中位数的index):
解题思路:
1.首先获取K的值,根据两个数组大小(m和n)获取中位数index,若m+n为基数,则K=(m+n)/2+1,中位数为K位置值;若m+n为偶数,则K=(m+n)/2,中位数为K和K+1的值相加除以2。
2.根据第一步按照从小到大查找中位数。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int newLength = nums1.length + nums2.length;
        if (newLength == 0)
            return 0.0;
        int i = 0, j = 0;
        int left = 0, right = 0;
        for (int k = 0; k <= newLength / 2; k++) {
            left = right;
            if (i < nums1.length && (j >= nums2.length || nums1[i] < nums2[j])) {
                right = nums1[i];
                i++;
            } else {
                right = nums2[j];
                j++;
            }
        }
        if (newLength % 2 == 0) {
            return (left + right) / 2.0;
        } else {
            return right;
        }
    }
}

解法3(滑动窗口+递归):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值