刷题(删除倒数第N个节点、搜索插入位置、二进制求和、求x平方根、爬楼梯)
1. 删除链表的倒数第N个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解题思路: 快慢指针
- 定义一个新链表,链表在原链表的基础上新增一个头节点 0
- 慢指针指向新链表的头节点,后面在改变链表时改变的也是新链表数据
- 快指针指向原链表的头节点
- 快指针首先移动n格,保证与慢指针之间有n个节点作为间隔
- 慢指针在快指针移动n格之后和快指针同时移动
- 当快指针移动到最后一个时,慢指针的下一个就是倒数第N个节点
- 将下一个节点删除并返回新节点的下一个节点,即为删除倒数第N个节点之后的新链表
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let dummy=new ListNode(0,head);
let slow=dummy;
let fast=head;
for(let i=0;i<n;i++){
fast=fast.next; // 循环结束fast与slow指针相差n个结点,之后快慢指针一起移动,当快指针到最后一个 慢指针的下一个就是要删除的结点
}
while(fast){
fast=fast.next;
slow=slow.next;
}
slow.next=slow.next.next;
return dummy.next;
};
2. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
解题思路:
- 如果nums是[1,3,5,7],target是4,结果应该返回2,也就是应该替代大于它的5的位置(把5往后挤)。
- 假设target是5,结果同样应该返回2,就是数组中已经存在的5的位置,代替值相等的位置。
- 假设target是8,则结果应该返回4,数组中没有一个数字比它大,它总是排在最后面,它的index也就是这个数组的length。
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var searchInsert=function(nums,target){
for(let i=0;i<nums.length;i++){
if(nums[i]>=target){
return i;
}
}
return nums.length;
}
只要看到题里给出的数组是有序数组,都可以想一想是否可以使用二分法。
3. 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
解题思路:
- 使用数组的
join()
方法,将数组转换为字符串,将字符串转为数值类型,并且加一
,由于精度问题,使用BigInt()
转换 - 将加一后的数值用
toString()
转换为字符串 - 使用字符串的
split()
方法,将字符串转换为数组
/**
* @param {number[]} digits
* @return {number[]}
*/
var plusOne = function(digits) {
return (BigInt(digits.join('')) + 1n).toString().split('')
};
4. 二进制求和
给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。
**解题思路:**将输入的二进制字符前面加上'0b'
,并通过BigInt()
方法将二进制字符串转换为对应的数字然后相加,将相加后的数字使用toString(2)
转换为二进制数字并返回。
a = ‘0b’ + a; 和 b = ‘0b’ + b;:这两行将输入的二进制字符串 a 和 b 前面添加 ‘0b’,以符合 JavaScript 中表示二进制数的惯例。在 JavaScript 中,以 ‘0b’ 开头的字符串被解释为二进制数。
let sum = BigInt(a) + BigInt(b);:使用 BigInt 类型进行二进制数的相加。将带有 ‘0b’ 前缀的二进制字符串转换为对应的 BigInt 数字,然后相加。
return sum.toString(2);:将相加后的 BigInt 数字转换为二进制字符串,使用 toString(2) 方法。这样就得到了两个二进制数相加的结果。
- 示例 1:
- 输入:a = “11”, b = “1”
- 输出:“100”
- 示例 2:
- 输入:a = “1010”, b = “1011”
- 输出:“10101”
/**
* @param {string} a
* @param {string} b
* @return {string}
*/
var addBinary = function(a, b) {
a='0b'+a;
b='0b'+b;
let sum=BigInt(a)+BigInt(b);
return sum.toString(2)
};
5. 求x的平方根(二分法求解)
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
解题思路:
- 初始化左指针,右指针,和中间指针。利用二分法时不用非得整一个数组,初始右指针时可以用x/2,算是一个优化,且要向上取整
- 循环条件为left<=right,计算mid中间值,计算时,用左右两个指针相加除以2即可
- 最后返回结果时,left会比right大1,所以返回left-1或者返回right都可以
- 注意当x=0或x=1,可以直接返回x
- 示例 1:
输入:x = 4
输出:2 - 示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function(x) {
if(x===0||x===1){
return x;
}
let left=0;
let right=x-1;
while(left<=right){
let mid=Math.floor((left+right)/2);
let val=mid*mid;
if(val===x) {
return mid;
}
if(val>x){
right=mid-1;
} else {
// val < x
left=left=mid+1;
}
}
return right;
};
6. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
解题思路: 通过简单的计算,发现这是一个斐波那契数列,可以定义一个数组,并通过f(n)=f(n-1)+f(n-2)
给数组元素赋值,返回相应的值。
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
/*
n=1 1 1
n=2 11,2 2
n=3 111,12,21 3
n=4,22,1111,121,211,112 5
即f(n)=f(n-1)+f(n-1)
*/
let res=new Array(n+1).fill(0);
res[0]=1;
res[1]=1;
for(let i=2;i<res.length;i++){
res[i]=res[i-1]+res[i-2];
}
return res[n]
};
优化:由于最终只需要n-1
和n-2
的方法数,因此没有必要将计算的每一个数值都保存,可以从这方面入手,将代码进行优化。
/**
* @param {number} n
* @return {number}
*/
var climbStairs = function(n) {
let pre=1;
let cur=1;
for(let i=2;i<n+1;i++){
let t=cur; // 暂存上一次的cur
cur=pre+cur; // 当前的cur=上上次cur[pre]+上次cur
pre=t; // pre更新为上一次的cur
}
return cur;
};