两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
- 难易程度:简单
解题思路:
- 暴力解法
- 两层for循环,数组的第一个值与第二,三,四...个值相加判断是否等于目标值,由于每个值只能使用一次,所以内存for起始值为
i + 1
var twoSum = function (nums: number[], target: number): number[] {
if (nums.length < 2) return []
if (nums.length === 2){
if (nums[0] + nums[1] === target) return [0, 1]
return []
}
for (var i = 0; i < nums.length; i++) {
for (var j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
return []
};
console.log(twoSum([2, 7], 9))
- 哈希表解法
- 利用对象存储已经循环过的值,比较目标值和当前正在循环的值的差值,如果差值正好在对象中,则找到了两个值
var towSumHash = function (nums: number[], target: number): number[] {
if (nums.length < 2) return []
if (nums.length === 2) {
if (nums[0] + nums[1] === target) return [0, 1]
return []
}
var map = {} // key: 数字 value:index
var loop = 0 // 循环次数
var dis = 0 // 差值
var len = nums.length
while (loop < len) {
dis = target - nums[loop]
if (map[dis] != undefined) {
return [map[dis], loop]
}
map[nums[loop]] = loop
loop++
}
return []
}
console.log(towSumHash([2, 7, 122, 123, 23], 129))
两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆 序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
- 难易程度: 中等
解题思路
- 平常思维,按序取出链表中的值,得出相加的结果,再利用结果倒序生成新的链表
class LinkedNode {
value: number
next: LinkedNode
constructor(value: number) {
this.next = null
this.value = value
}
}
const a = new LinkedNode(2)
const b = new LinkedNode(4)
const c = new LinkedNode(3)
const d = new LinkedNode(5)
const e = new LinkedNode(6)
const f = new LinkedNode(4)
a.next = b
b.next = c
d.next = e
e.next = f
function getNode(linkedList: LinkedNode, values: number[]) {
values.push(linkedList.value)
if (linkedList.next !== null) {
getNode(linkedList.next, values)
}
}
// 按序取出链表中的值的数组
function getNodeArr(linkedList: LinkedNode) {
let values: number[] = []
getNode(linkedList, values)
return values
}
const addTwoNumbers = function (l1: LinkedNode, l2: LinkedNode) {
let value1 = getNodeArr(l1).reverse().join('')
let value2 = getNodeArr(l2).reverse().join('')
let res = (+value1) + (+value2) + ''
// 存储结果的每个节点
const nodeArr: LinkedNode[] = []
res.split('').reverse().forEach(r => {
nodeArr.push(new LinkedNode(+r))
})
for (let i = 0, len = nodeArr.length; i < len - 1; i++) {
// 构建结果链表
nodeArr[i].next = nodeArr[i + 1]
}
return nodeArr[0]
};
console.log(addTwoNumbers(a, d)) // 7 -> 0 -> 8
- 两个链表相加,因为是倒序的,则两数相加大于10,向后进位,低位相加。
2 -> 4 -> 3
5 -> 6 -> 4 +
----------------------
7 -> 0 -> 8
因为6+4大于等于10, 所以需要向后进位, 3 + 4 + 1 = 8
function addTwoNumbers2(l1: LinkedNode, l2: LinkedNode): LinkedNode {
let node = new LinkedNode(0);
let temp = node;// temp节点
let add = 0;// 是否进一
let sum = 0;// 新链表当前未取余的值 = 链表1值 + 链表2值 + add;
// 遍历, 以最长链表为结束点
while (l1 || l2) {
sum = (l1 ? l1.value : 0) + (l2 ? l2.value : 0) + add;
temp.next = new LinkedNode(sum % 10);// 取余则为新链表的值
temp = temp.next;
add = sum >= 10 ? 1 : 0; // 相加大于10,则需向后进位(+1)
l1 && (l1 = l1.next); // 改变指向,继续循环
l2 && (l2 = l2.next);
}
add && (temp.next = new LinkedNode(add));
return node.next;
}
最长无重复子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
难易度: 中等
- 无重复子串 = 无重复字符开始 至 当前遍历字符串下标
当出现重复子串时,更新无重复子串开始位置
abcabca出现第一个重复子串a之前, 无重复子串为abc,长度为2 - 0 + 1 (遍历点 - 无重复子串开始位置 + 1)
var lengthOfLongestSubstring = function(s: string) {
if (!s) return 0
if (s.length === 1) return 1
let map: Map<string, number> = new Map() // 使用map存储已经遍历过的字符,k:字符 v:index
let max: number = 0
let len = s.length
let i = 0 // 无重复子串的开始位置
// 从无重复子串位置 到 遍历位置就为最长无重复子串 i->j 为无重复子串
for(let j = 0; j < len; j++) {
if(map.has(s[j])) {
// 如果这个字符被遍历过,更新无重复子串的开始位置为 这个字符出现的最大位置
i = Math.max(map.get(s[j]) + 1, i)
}
// 最长无重复子串就为 当前遍历下标 - 无重复子串开始位置 + 1(长度需要+1)
max = Math.max(max, j - i + 1)
// 记录当前字符的位置
map.set(s[j], j)
}
return max
}
console.log(lengthOfLongestSubstring('abcabcaa'))
- 无重复子串指针,当出现了重复子串,移动无重复子串指针,无重复子串为 指针到当前遍历位置的子串
var lengthOfLongestSubstring2 = function (s: string) {
if (!s) return 0
if (s.length === 1) return 1
let max = 0 // 最大长度
let temp = "" // 当前无重复字串
let start = 0 // 当前无重复字串指针
let loop = 0 // 循环次数
let index: number // 当前字符所在temp中的位置
let len = s.length
//假如当前max大于当前未遍历的剩余字符时,不需要往后遍历,不存在更长的不重复子串
//abccba 当运行到第二个c时,max = start = 3 不需要继续循环
while (loop < len && max < len - start) {
index = temp.indexOf(s[loop])
if (index >= 0) {
start += index + 1 // 出现了重复字符,移动指针,舍弃第一个重复字符位置之前的字符
}
temp = s.slice(start, loop + 1)
max = Math.max(temp.length, max)
loop++
}
return max
};
整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1: 输入: 123 输出: 321
示例 2: 输入: -123 输出: -321
示例 3: 输入: 120 输出: 21
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
难易度:简单
解题思路
-
暴力解法
转换为字符串,反转字符串即可
var reverse = function(x: number): number {
let maxNum = Math.pow(2, 31)
if (x === 0 || x < -maxNum || x > maxNum ) return 0
let num = +(Math.abs(x) + '').split('').reverse().join('')
if (x < 0) return -num
if (x > 0) return num
};
-
取余,取模
每次取原数字的最后一位,作为新数字的开始位,操作完成后截取掉原数字的最后一位,完成反转
214 取模 4 -> result: 4 origin: 2121 取模 1 -> result: 41 origin: 22 取模 2 -> result: 412 origin:
var reverse2 = function (x: number): number {
let maxNum = Math.pow(2, 31)
if (x === 0 || x < -maxNum || x > maxNum ) return 0
let num = Math.abs(x) // 将所有数变成正数,操作过程中不关心正负
let result = 0
while(num > 0) {
// 每次都操作最后一位数,所以每次操作数都是10的倍数
result = result * 10 + num % 10
num = Math.floor(num / 10)
}
if (x < 0) return -result
if (x > 0) return result
}
罗马数字转整数
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
| 字符 | 数值 |
|---|---|
| I | 1 |
| V | 5 |
| X | 10 |
| L | 50 |
| C | 100 |
| D | 500 |
| M | 1000 |
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
-
特殊情况
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内
-
示例:
输入: "III"输出: 3
难易度:中等
解题思路:
- 根据特殊规则,如果前一位罗马数 / 后一位罗马数 = 1/5 或者 1/10,则,这两位罗马数应组合成一位阿拉伯数字,值为后一位罗马数 - 前一位罗马数
var romanToInt = function (s: string): number {
if (!s) return 0
let obj = {
I: 1,
V: 5,
X: 10,
L: 50,
C: 100,
D: 500,
M: 1000
}
let len = s.length
let loop = 0
let res = 0
while (loop < len) {
let temp = obj[s[loop]]
let temp2 = obj[s[loop + 1]]
if (temp2 / temp === 5 || temp2 / temp === 10) {
res += (temp2 - temp)
loop +=2 // 消耗了两位罗马数,所以循环次数+2
} else {
res += temp
loop++ // 这里只需要消耗一位罗马数,所以+1
}
}
return res
};
- 根据特殊情况,前一位数如果比后一位数小,则这两位数应组合在一起
var romanToInt2 = function (s: string): number {
if (!s) return 0
let obj = {
I: 1,
V: 5,
X: 10,
L: 50,
C: 100,
D: 500,
M: 1000
}
let len = s.length, sum = 0, loop = 0, num = 0, now = 0;
while(loop < len) {
now = obj[s[loop]] // 用now代表当前罗马数对应的阿拉伯数
if(num < now) {
sum -= num // 如果上一位数小于当前数,那么肯定是这两位罗马数组合,值为两位数相减
} else {
sum += num
}
num = now // 用num缓存上一位罗马数对应的阿拉伯数
loop++
}
return sum += num
};