目录
两数之和
两数相加
无重复字符的最长子串
最长回文子串
盛最多水的容器
删除链表的倒数第n个节点
合并两个有序链表
有效的括号
两数之和
题目链接:1.两数之和
示例
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
解题思路
利用hashmap将数组的值和下标以键值对的形式存储起来,遍历数组,边遍历边判断,一旦遇到符合题意的条件时,直接返回结果。每次判断map中是否存在键为target-nums[i]的键值对,如果有,则返回该键对应的值和当前的i;如果没有就将nums[i]和i存入map中。
具体代码
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 new int[0];
}
}
两数相加
题目链接:2.两数相加
示例
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
说明:342 + 465 = 807.
解题思路
该问题的核心是处理进位问题,首先定义一个进位值,每个位相加后更新进位值。同时遍历两个链表,依次相加其每一位,当有一个链表为空时,则只处理非空的链表和进位值即可
具体代码
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode ret = new ListNode(0);
ListNode cur = ret;
int isAdd = 0;
while (l1 != null && l2 != null) {
int num = l1.val + l2.val + isAdd;
isAdd = num / 10;
num %= 10;
cur.next = new ListNode(num);
cur = cur.next;
l1 = l1.next;
l2 = l2.next;
}
ListNode l = l2 == null ? l1 : l2;
while (l != null) {
int num = l.val + isAdd;
isAdd = num / 10;
num %= 10;
cur.next = new ListNode(num);
cur = cur.next;
l = l.next;
}
if (isAdd == 1) {
cur.next = new ListNode(isAdd);
}
return ret.next;
}
}
无重复字符的最长子串
题目链接:3.无重复字符的最长子串
示例
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是
"abc",所以其
长度为 3。
解题思路
利用滑动窗口的思想可以解决该问题,遍历字符串,如果窗口内未出现重复字符,则扩大窗口的大小,右边界一直右移,如果出现了重复字符,则缩小窗口的大小,左边界右移。使用HashSet来存储每个字符,如果出现了重复的,就将窗口左边界右移,边移动边更新最大值,最终即可得到结果
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int ret = 0;
int r = -1;
int n = s.length();
for (int i = 0; i < n; i++) {
if (i != 0) {
set.remove(s.charAt(i - 1));
}
while (r + 1 < n && !set.contains(s.charAt(r + 1))) {
set.add(s.charAt(r + 1));
r++;
}
ret = Math.max(ret, r - i + 1);
}
return ret;
}
}
最长回文子串
题目链接:5.最长回文子串
示例
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
解题思路
动态规划
状态定义:f(i, j): 从i到j的字母是否为回文子串
状态方程:f(i, j) = f(i+1, j-1) 且 (s(i) == s(j))
初始化:f(i, i) = true f(i, i+1) = (s(i) == s(i+1))
返回值:p(i, j) == ture中j-i+1最长的那个串
具体代码
class Solution {
/**
状态定义:f(i, j): 从i到j的字母是否为回文子串
状态方程:f(i, j) = f(i+1, j-1) 且 (s(i) == s(j))
初始化:f(i, i) = true f(i, i+1) = (s(i) == s(i+1))
返回值:p(i, j) == ture中j-i+1最长的那个串
*/
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return s;
}
int len = s.length();
int maxLen = 1;//记录最长的回文串长度
int begin = 0;//记录最长的回文串的初始位置
int end = 0;//记录最长的回文串的结束位置
boolean[][] dp = new boolean[len][len];
for (int r = 1; r < len; r++) {
for (int l = 0; l < r; l++) {
if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
dp[l][r] = true;
if (r - l + 1 > maxLen) {
maxLen = r - l + 1;
begin = l;
end = r;
}
}
}
}
return s.substring(begin, end + 1);
}
}
盛最多水的容器
题目链接:11.盛最多水的容器
示例
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
解题思路
使用双指针法,初始时,l为该容器的左边界,r为容器的右边界,维护一个面积的最大值,取l的高和r的高小的那个值作为容器的高,乘以(r - l),得到的结果即为临时面积,再与维护的最大值对比,更新为大的那一个。如果此时r的高比l的高低,就r边界左移,反之l边界右移,当l >= r时循环结束,此时的ret即为最大面积。
class Solution {
public int maxArea(int[] height) {
int ret = 0;
int l = 0;
int r = height.length - 1;
while (l < r) {
int area = Math.min(height[l], height[r]) * (r - l);
ret = Math.max(area, ret);
if (height[l] > height[r]) {
r--;
} else {
l++;
}
}
return ret;
}
}
删除链表的倒数第n个节点
题目链接:19.删除链表的倒数第n个节点
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
解题思路
快慢指针思想,当n小于0时,直接返回原链表头结点;当n大于0时只移动快指针,当n等于0时,快慢指针一起移动,当快指针等于空或者快指针.next等于空就跳出循环;再对n进行判断,当n等于1时,说明要删除的是头结点,其他情况将慢指针的下一个指向其下一个的下一个
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if (n < 0) {
return head;
}
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
if (n > 0) {
fast = fast.next;
n--;
} else {
fast = fast.next;
slow = slow.next;
}
}
if (n == 1) {
head = head.next;
} else {
slow.next = slow.next.next;
}
return head;
}
}
合并两个有序链表
题目链接:21.合并两个有序链表
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
解题思路
首先两个链表一起遍历,哪个小就将哪个先加入到新链表中,循环结束后将非空的链表再加入到新链表中即可
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode ret = new ListNode(0);
ListNode cur = ret;
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
cur.next = list1;
cur = cur.next;
list1 = list1.next;
} else {
cur.next = list2;
cur = cur.next;
list2 = list2.next;
}
}
ListNode l = list1 == null ? list2 : list1;
cur.next = l;
return ret.next;
}
}
有效的括号
题目链接:20.有效的括号
解题思路
利用栈先进后出的特性可以完美解决该问题,需要注意的遍历结束后如果栈为空,说明是有效的,不为空时说明无效,另外循环时在出栈之前也要判断栈是否为空,如果为空则无效,不为空则继续判断。
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char tmp = s.charAt(i);
if (tmp == '(' || tmp == '[' || tmp == '{') {
stack.push(tmp);
} else {
if (stack.isEmpty()) {
return false;
}
if (tmp == ')' && stack.peek() == '(' ||
tmp == ']' && stack.peek() == '[' ||
tmp == '}' && stack.peek() == '{') {
stack.pop();
} else {
return false;
}
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
}