1.两数之和(简单题)
题目描述:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
代码:
class Solution {
public static 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 j = target - nums[i];
if(map.containsKey(j) && map.get(j) != i) {
return new int[] {i, map.get(j)};
}
}
return new int[2];
}
}
方法解析:
为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。
通过以空间换取速度的方式,我们可以将查找时间从 O(n)O(n) 降低到 O(1)O(1)。哈希表正是为此目的而构建的,它支持以 近似 恒定的时间进行快速查找。我用“近似”来描述,是因为一旦出现冲突,查找用时可能会退化到 O(n)O(n)。但只要你仔细地挑选哈希函数,在哈希表中进行查找的用时应当被摊销为 O(1)O(1)。
一个简单的实现使用了两次迭代。在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i]target−nums[i])是否存在于表中。注意,该目标元素不能是 nums[i]nums[i] 本身!
优化:其实这个方法可以更加优化,我们可以一次完成。在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。也就是边加入map元素边查询。
- 两数相加(中等题)
题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
代码:
/**
* 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 d = new ListNode(0);
ListNode p = l1, q = l2, c = d;
int sum = 0;
while(p != null || q != null){
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
sum = sum + x + y;
c.next = new ListNode(sum%10);
sum = sum/10;
c = c.next;
if(p != null){
p = p.next;
}
if(q != null){
q = q.next;
}
}
if(sum > 0)
c.next = new ListNode(sum);
return d.next;
}
}
方法解析:
首先从最低有效位也就是列表 l1和 l2的表头开始相加。由于每位数字都应当处于 0~9 的范围内,我们计算两个数字的和时可能会出现 “溢出”。例如,5 + 7 =12。在这种情况下,我们会将当前位的数值设置为 2,并将进位 sum = 1 带入下一次迭代。进位 sum 必定是 0或1,这是因为两个数字相加(考虑到进位)可能出现的最大和为 9 + 9 + 1 = 19。
3.无重复字符的最长子串(中等题)
代码
public class Solution {
public int lengthOfLongestSubstring(String s) {
char[] a = s.toCharArray();
int sum = 0;
for(int i = 0; i < a.length ; i++){
ArrayList<Character> b = new ArrayList<>();
b.add(a[i]);
//当有元素时,将sum值设为1
sum = sum < b.size() ? b.size() :sum;
for(int j = i + 1; j < a.length; j++){
if(b.contains(a[j]) ){
sum = sum < b.size() ? b.size() :sum;
b.clear();
break;
}
else{
b.add(a[j]);
sum = sum < b.size() ? b.size() :sum;
}
}
}
return sum;
}
}
方法解析:
我的这种方法属于暴力法,检查每一个子字符串,将不含重复的字母的字符串经过比较长度大小,筛选出最长的记录在sum中
优化:在暴力法中,反复检查一个子字符串是否含有有重复的字符,但这是没有必要的。这道题主要用到思路是:滑动窗口
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移
动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
代码:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
}
代码二:
以前的我们都没有对字符串 s 所使用的字符集进行假设。=
当我们知道该字符集比较小的时侯,我们可以用一个整数数组作为直接访问表来替换 Map。
常用的表如下所示:
int [26] 用于字母 ‘a’ - ‘z’ 或 ‘A’ - ‘Z’
int [128] 用于ASCII码
int [256] 用于扩展ASCII码
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
int[] index = new int[128]; // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
i = Math.max(index[s.charAt(j)], i);
ans = Math.max(ans, j - i + 1);
index[s.charAt(j)] = j + 1;
}
return ans;
}
}
4.最长回文子串(中等题)
题目描述:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
方法代码:
class Solution {
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 = around(s,i,i);
int len2 = around(s,i,i+1);
int len = Math.max(len1, len2);
if(len > end - start +1){
start = i - (len-1)/2;
end = i + len/2;
}
}
return s.substring(start , end + 1);
}
public static int around(String s ,int left ,int right){
int i = left, j = right ;
while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
i--;
j++;
}
return j - i -1;
}
}
方法解析:
事实上,只需使用恒定的空间,我们就可以决这个问题。
我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有 2n - 1个这样的中心。
你可能会问,为什么会是 2n - 12n−1 个,而不是 nn 个中心?原因在于所含字母数为偶数的回文的中心可以处于两字母之间(例如 “abba” 的中心在两个‘b’ 之间)。