题目1
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解法1:暴力双重循环
这个解法很容易想到,而且实现起来十分简单,最多只需要进行(N,2)次数的比较即可,时间复杂度O(n2),具体实现如下所示。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result=new int[2];
for(int i=0;i<nums.length-1;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
result[0]=i;
result[1]=j;
return result;
}
}
}
return result;
}
}
解法2:利用哈希表来判断
哈希表这种解法有使用条件,要求nums数组无重复项,否则就会出现多个重复的键被当作一个,方法是首先一次遍历,将所有数据输入到哈希表,然后第二次循环,来判断target-nums[i]是否在哈希表中存在。具体实现如下。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
但是这种解法增加了空间,但是hashmap本身的containsKey方法就类似于一层循环,时间上并没有很大提升,只是编码效率会比暴力法稍有提升。
题目2
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-two-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解法:所见即所得
这就是一个小学数学的加法,按照数学思维来写代码就可以了,这道题目我个人觉得主要是考察数据结构,链表的使用和尾插,我看这道题的评论上说,有人想到了将这两个链表先转化为数字,然后通过直接求和再转化为链表,看似简单可行,实际上如果链表足够长,Long Long也是存不下的,反而增加时间以及空间的复杂度。具体解法如下。
public ListNode addTwoNumbers(ListNode l1,ListNode l2){
ListNode root=new ListNode(0);
ListNode cursor=root;
int add=0;
while (l1!=null||l2!=null||add!=0){
int a=l1.val;
int b=l2.val;
int sum=a+b+add;
add=sum/10;
ListNode node=new ListNode(sum%10);
cursor.next=node;
cursor=node;
if (l1!=null) l1=l1.next;
if (l2!=null) l2=l2.next;
}
return root.next;
}
题目3
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解法:
这个是在评论里面找到的解法,果然都是大神级别,自愧不如,先上代码和我自己理解加的一部分解释。
public static int lengthOfLongestSubstring(String s){
//a b c a b c d a d e
//0 1 2 3 4 5 6 7 8 9
int maxSize =0;
//记录ASCII 码字符出现的位置,以字符作为下标
int[]dict =new int[128];
//为了方便理解,这里把数组内容全部设为 -1,之后在记录的时候就可以从 0 开始,方便理解
Arrays.fill(dict, -1);
//用于记录重复 ASCII 码字符出现的位置的值
int repeatValue = -1;
// 当前下标
int i =0;
int ASCII;
while (i < s.length()) {
ASCII = s.charAt(i);//获取当前字符的ASCII码
//如果当前位置的值 > repeatValue,证明当前位置已经赋过一次值了,证明字符重复
//dict[ASCII]是当前字符的位置
if (dict[ASCII] > repeatValue)
//更新 repeatValue 为之前赋值的下标
repeatValue =dict[ASCII];
//将当前下标赋值到数组相应位置
dict[ASCII] = i;
//i - repeatValue(去除重复部分)
// 比如 abcabcdade 中的三个 a 的计算abca - a(3 - 0)=bca abcabcda - abca(7 - 3)=bcda
maxSize =Math.max(maxSize, i - repeatValue);
//s.length() - repeatValue - 1 判断剩下的数有没有必要继续循环
//比如 abcabcdade 最后的 a(当 i = 7 repeatValue = 3) ,abcabcdade - abca(10-3-1) = bcdade 剩下最多有六位
//比如 abcabcdade 最后的 d(当 i = 8 repeatValue = 6) ,abcabcdade - abcabcd(10-6-1) = ade 剩下最多也是三位
if (maxSize >= s.length() - repeatValue -1) {
return maxSize;
}
i++;
}
return maxSize;
}
其实这个解法与滑动窗口类似,这里面的repeatValue就是相当于尾指针,都是在遇到重复字符后更新位置,每次循环都更新下最大长度,算法简洁,理解起来稍微有些困难,让我自己想怕是要几年。