专栏声明:只求用最简单的,容易理解的方法通过,不求优化,不喜勿喷
今天更新五个中等难度题目:
两数相加
-
题面
给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。 -
知识点:
链表,模拟 -
思路
模拟加法的操作,因为是逆序的数字链表,所以我们从两个链表头开始遍历链表即可,每次将两个数字相加并且加上进位标记(上一次结果有没有进位),放到新链表中,然后相加的结果超过10,我们对10取余,然后将进位存储在一个进位标记中,如果出现一个链表到底的情况,那么本次相加时,它是 0
遍历两个链表,直到两个链表都遍历到底部,并且进位为0即可 -
代码
var addTwoNumbers = function(l1, l2) {
let head = null, tail = null;
let carry = 0;
while (l1 || l2) {
const n1 = l1 ? l1.val : 0;
const n2 = l2 ? l2.val : 0;
const sum = n1 + n2 + carry;
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
carry = Math.floor(sum / 10);
if (l1) {
l1 = l1.next;
}
if (l2) {
l2 = l2.next;
}
}
if (carry > 0) {
tail.next = new ListNode(carry);
}
return head;
};
无重复字符的最长子串
- 题面
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 - 知识点
字符串、哈希表、滑动窗口 - 思路
初始化一个哈希表来存储我们的字符,遍历我们是字符串,然后用另一个变量记录当前字符串的开头位置
每次遇到一个字符,如果是没出现过的,那么我们直接把它记录到哈希表里,然后更新长度
如果出现过,那么我们更新字符串的开头位置,开头位置一直向前,期间从哈希表里删除经过的字母,直到删除现在正在遍历的字母位置,然后把现在遍历的字母记录到哈希表里,最后更新长度 - 代码
var lengthOfLongestSubstring = function (s) {
let hash = new Array(200).fill(0);
let a = 0;
let max = 0;
for (var i = 0; i < s.length; i++) {
let t = s.charCodeAt(i);
if(hash[t] != 0 ){
while(s[a]!=s[i]){
hash[s.charCodeAt(a)] = 0;
a++;
}
hash[s.charCodeAt(a)] = 0;
a++;
}
max = Math.max(i - a + 1,max);
hash[t] = 1;
}
return max;
};
最长回文子串
- 题面
给你一个字符串 s,找到 s 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。 - 知识点
回文串,动态规划 - 思路
我们使用一个二位数组来记录 [ i , j ] 之间是不是回文串,我们遍历每一位,它有三种情况:
当遍历 i 和它本身时,肯定是一个回文串,记录为 true
当遍历 i 和 i + 1 时,只有这两个数相等,他们才是回文串,否则不是
当遍历 i 和 i + 2 以及以上的时候,需要他们相等,并且 i + 1 和 j - 1 也是回文串,它才是回文串
对于每一组 i 和 j ,如果它是回文串并且比现在记录的长,更新返回的回文串 - 代码
var longestPalindrome = function (s) {
var n = s.length;
var d = [];
for (var k = 0; k < n; k++) {
d.push([]);
}
var ans = '';
var j;
for (var l = 1; l <= n; l++) {
for (var i = 0; i < n; i++) {
j = i + l - 1;
if (j >= n) break;
if (i == j) d[i][j] = true;
else if (j == i + 1) d[i][j] = (s[i] == s[j])
else {
d[i][j] = d[i + 1][j - 1] && (s[i] == s[j])
}
if (d[i][j]) {
ans = s.slice(i, j + 1);
}
}
}
return ans;
};
盛最多水的容器
- 题面
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。 - 知识点
双指针,贪心 - 思路
我们用两个指针来更新我们的容器,一个指向下标 0 ,一个指向数组最后一位 ,每次我们更新两个指针中较为矮的一个,然后计算他们的盛水量,直到两个指针相遇 - 代码
var maxArea = function (height) {
let b = height.length;
let max = 0;
let i = 0;
let j = b;
while (i < j) {
let area = Math.min(height[i], height[j]) * (j - i);
if (area > max) {
max = area
}
if (height[j] > height[i]) {
i++;
}
else {
j--;
}
}
return max;
};
三数之和
- 题面
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。 - 知识点
双指针 - 思路
首先对我们的数组排序,只有有序的数组可以使用双指针,之后遍历每一位,对于每一位 i ,我们用两个指针寻找需要的剩下两个数,他们的和要等于 0 - nums [ i ] ,要注意的一点是不能重复,所以我们每次只计算相同的元素中,最靠近另一个指针的那一项即可 - 代码
var threeSum = function (nums) {
var ret = [];
nums.sort(function (a, b) {
return a - b;
})
for (var i = 0; i < nums.length - 1; i++) {
var a = i + 1;
var b = nums.length - 1;
if (i != 0 && nums[i] == nums[i - 1]) {
continue;
}
var re = 0 - nums[i];
while (a < b) {
if (nums[a] + nums[b] < re) {
a++;
while (nums[a - 1] == nums[a]) {
a++;
}
} else if (nums[a] + nums[b] > re) {
b--;
while (nums[b + 1] == nums[b]) {
b--;
}
} else {
ret.push([nums[a], nums[i], nums[b]]);
a++;
while (nums[a - 1] == nums[a]) {
a++;
}
b--;
while (nums[b + 1] == nums[b]) {
b--;
}
}
}
}
return ret;
};