合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
难度:容易
解题思路:
- 暴力解法
递归两个链表,取出所有val,对这些val进行排序,将排序之后的val在生成一个新链表输出。
class ListNode {
val: number = 0
next: ListNode = null
constructor(val: number, next: ListNode = null) {
this.val = val
this.next = next
}
}
function getNodeVal(l: ListNode, values: number[]) {
if (!l) return
values.push(l.val)
getNodeVal(l.next, values)
}
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function (l1: ListNode, l2: ListNode): ListNode {
if (l1 && l2) {
let values: number[] = []
getNodeVal(l1, values)
getNodeVal(l2, values)
values.sort((a, b) => a - b)
let res: ListNode[] = values.map(v => new ListNode(v))
res.reduce((prev, cur) => prev.next = cur)
return res[0]
} else {
return l1 || l2
}
};
- 不生成新的链表
既然两个链表都是升序排列,那么只需要把两个列表的节点相互比较,然后插入到愿列表中即可
1. 如果l1的当前节点比l2的当前节点大,那么就应该是l1的当前节点插入l2的当前节点之后
2. 反之亦然
3. 递归比较没一个节点
var mergeTwoLists2 = function (l1: ListNode, l2: ListNode): ListNode {
if (!l1 || !l2) return l1 || l2
if (l1.val > l2.val) {
l2.next = mergeTwoLists2(l1, l2.next)
return l2
} else {
l1.next = mergeTwoLists2(l1.next, l2)
return l1
}
}
合法的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足
1. 左括号必须用相同类型的右括号闭合。
2. 括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串!
示例:
输入: "()[]{}"
输出: true
难易度:中等
解题思路:
- 括号必须成对出现,所以如果字符串长度为奇数,则不成立
- 反括号出现的次序 必须与对应成对正括号 出现的次序相同
- 考虑用栈的形式存储正括号出现的顺序
var isValid = function (s: string): boolean {
const len = s.length;
if (len == 0) return true;
if (len % 2) return false; // 括号必须成对出现,所以如果字符串长度为奇数,则不成立
const leftDic = '([{', rightDic = ')]}';
const stack: string[] = [];
for (let i = 0; i < len; i++) {
if (leftDic.indexOf(s[i]) != -1) {
stack.push(s[i]);
}
if (rightDic.indexOf(s[i]) != -1) {
const temp = stack.pop(); // 栈里面始终存储的是有序的,未成对的括号
// 括号必须成对
if (leftDic.indexOf(temp) != rightDic.indexOf(s[i])) return false;
}
}
return !stack.length; // 如果for循环完了栈里面还存在元素,说明正反括号的个数不同
};
字符串转整数
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
1. 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
2. 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
3. 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。
提示:
本题中的空白字符只包括空格字符 ' ' 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231)
示例:
输入: " -42" 输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
难易度:中等
解题思路:
- parseInt
看了这个题这么多描述,有把我吓到。但是总结起来其实就是parseInt这个API的扩展版而已。
它比parseInt多个数据范围限制INT_MAX (2^31 − 1) 或 INT_MIN (−2^31)
转换出NaN需要返回0
var myAtoi = function (str: string): number {
str = str.trim()
if (!str) return 0
const res = parseInt(str, 10) // 以十进制计算,避免0开头和 数字和字母连在一起的情况触发 八进制 十六进制转换
if (isNaN(res)) {
return 0
} else if (res < Math.pow(-2, 31) || res > Math.pow(2, 31) - 1) {
return res < Math.pow(-2, 31) ? Math.pow(-2, 31) : Math.pow(2, 31) - 1;
} else {
return res;
}
};
寻找两个正序数组的中位数
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
难度:困难
解题思路
- 暴力解法
中位数是一个有序数组里面处于中间的数,所以只需要将一个数组排序,按照偶数个数,和奇数个数,取出中位数即可
var findMedianSortedArrays = function (nums1: number[], nums2: number[]): number {
let newarr = nums1.concat(nums2)
let resArr = newarr.sort((a, b) => a - b)
if (resArr.length % 2) {
// 奇数个数
return resArr[Math.floor(resArr.length / 2)]
} else {
const prevIdx = resArr.length / 2 - 1
const nextIdx = resArr.length / 2
return (resArr[prevIdx] + resArr[nextIdx]) / 2
}
};
- 二分查找
由于题中给出的数组都是排好序的,在排好序的数组中查找很容易想到可以用二分查找(Binary Search), 这里对数组长度小的做二分, 保证数组A 和 数组B 二分之后
len(Aleft)+len(Bleft)=(m+n+1)/2 - m是数组A的长度, n是数组B的长度
const findMedianSortedArrays2 = function (nums1: number[], nums2: number[]): number {
// 用nums1做二分,保证nums1的长度是最小的
if (nums1.length > nums2.length) {
[nums1, nums2] = [nums2, nums1]
}
const m = nums1.length
const n = nums2.length
let low = 0
let high = m
while (low <= high) {
const i = low + Math.floor((high - low) / 2)
const j = Math.floor((m + n + 1) / 2) - i
const maxLeftA = i === 0 ? -Infinity : nums1[i - 1]
const minRightA = i === m ? Infinity : nums1[i]
const maxLeftB = j === 0 ? -Infinity : nums2[j - 1]
const minRightB = j === n ? Infinity : nums2[j]
if (maxLeftA <= minRightB && minRightA >= maxLeftB) {
return (m + n) % 2 === 1
? Math.max(maxLeftA, maxLeftB)
: (Math.max(maxLeftA, maxLeftB) + Math.min(minRightA, minRightB)) / 2
} else if (maxLeftA > minRightB) {
high = i - 1
} else {
low = low + 1
}
}
}