1.2的幂 *简单
题目:给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
思路:这题算简单的,递归、迭代、位运算都行
迭代:
/**
* @param {number} n
* @return {boolean}
*/
var isPowerOfTwo = function (n) {
if (n <= 0) return false;
if (n == 1) return true;
while (n >= 2) {
n = n / 2;
if (!Number.isInteger(n)) return false;
}
return true;
};
递归:
/**
* @param {number} n
* @return {boolean}
*/
var aa = (n) => {
if (!Number.isInteger(n)) return false;
if (n == 1) return true;
return aa(n / 2);
};
var isPowerOfTwo = function (n) {
if (n <= 0) return false;
if (n == 1) return true;
return aa(n);
};
位运算:
/**
* @param {number} n
* @return {boolean}
*/
var isPowerOfTwo = function (n) {
if(n<1){
return false;
}
return !(n & (n-1))
};
2.回文链表 *简单
题目:请判断一个链表是否为回文链表。
思路:这和之前的回文字符串比较像
最容易想到的,是把出现的值放进数组里,然后判断是否回文
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
const v = [];
while (head) {
v.push(head.val);
head = head.next;
}
while (v.length > 1) {
if (v.pop() !== v.shift()) return false;
}
return true;
};
下面优化一下。
我们可以从中间一分为二,分成两个链表,然后把前一半翻转(把后一半翻转是一样的),这样得到两个链表,比较两个链表的每个值即可。如果是奇数长度的链表,那么后半部分要再往后取一次
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
if(head === null || head.next === null) return true;
let mid = head;
let pre = null;
let reversed = null;
while(head !== null && head.next !== null) {
pre = mid
mid = mid.next
head = head.next.next
pre.next = reversed
reversed = pre
}
if(head) mid = mid.next
while(mid) {
if(reversed.val !== mid.val) return false
reversed = reversed.next
mid = mid.next
}
return true
};
3.二叉搜索树的最近公共父节点 *简单
题目:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
首先要明确以定义,。什么是二叉搜索树。二叉搜索树的意思是,每个节点,它的左节点比当前节点的值小,右节点比当前节点的值更大。企鹅同一级,从左到右节点的值也是一次增大。
所以思路就很简单,如果一个节点,它的值比p大比q小,那么它肯定就是结果。如果比两个都要大,那么递归它的左树,比两个都要小,递归它的右树。
/**
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
const lowestCommonAncestor = (root, p, q) => {
while (
(root.val > p.val && root.val > q.val) ||
(root.val < p.val && root.val < q.val)
) {
if (root.val > p.val && root.val > q.val) {
root = root.left;
} else {
root = root.right;
}
}
return root;
};
4.Z形变换 *中等
题目:
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。
请你实现这个将字符串进行指定行数变换的函数:
思路:首先,用一个二维数组记录每一行的结果,用一个变量记录行数,用另一变量记录方向。行数是在0-rumrows-1之间取值,每记录一个字符串,就+1或者-1,方向则是到边界之后需要掉头。所以代码就出来了
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function(s, numRows) {
if (numRows < 2) return s;
const res = new Array(numRows).fill(null).map(() => []);
let i = 0;
let flag = true;
let v = "";
while (s.length) {
v = s[0];
s = s.substring(1);
res[i].push(v);
if (i == numRows - 1) {
flag = false;
} else if (i == 0) {
flag = true;
}
i += flag?1:-1;
}
return res.reduce((r, v) => {
r += v.join("");
return r;
}, "");
};
接下来换种思路,字符串其实是以每2numrows-2为一个单位,所以用2numrows-2划分字符串,然后每个单位的字符串都是以行数最大的值为镜像的(除去第一行)
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function(s, numRows) {
if (numRows < 2) return s;
const res = new Array(numRows).fill("");
const temp = [];
while (s.length) {
temp.push(s.substring(0, 2 * numRows - 2));
s = s.substring(2 * numRows - 2);
}
temp.forEach((v, i) => {
for (let i = 0; i < numRows; i++) {
if (i == numRows - 1) {
res[i] += v[i] || "";
} else {
res[i] += (v[i] || "") + (v[2 * numRows - 2 - i] || "");
}
}
});
return res.join("");
};
下面是第一种方法的改良版,不用数组,用字符串拼接即可
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
var convert = function(s, numRows) {
if (numRows < 2) return s;
const res = new Array(numRows).fill("");
let i = 0;
let flag = true;
while (s.length) {
res[i] += s[0];
s = s.substring(1);
if (i == numRows - 1) {
flag = false;
} else if (i == 0) {
flag = true;
}
i += flag ? 1 : -1;
}
return res.join("");
};
5.字符串转换整数 (atoi) *中等
题目:
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。
提示:
本题中的空白字符只包括空格字符 ' ' 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
思路:首先,我们知道,parseInt()就是结果,不过显然是要我们用原生写
然后,我们先依次遍历字符串,如果第一次遇到空格,跳过,遇到有效的字符,如果是+、-号,记录,如果是字符,则拼接。此时,如果遇到了有效数字字符之后,后面只能遇到0-9的字符,遇到其他字符要直接返回,结束计算
/**
* @param {string} str
* @return {number}
*/
var myAtoi = function(str) {
const a = "-+0123456789 ";
let res = "";
let flag = 0;
while (str.length) {
let s = str[0];
str = str.substring(1);
if (a.includes(s)) {
if (flag && s === " ") break;
if (!flag && s === " ") continue;
if (!flag) {
if (s === "-") {
flag = -1;
} else if (s === "+") {
flag = 1;
} else {
res += s;
flag = 1;
}
} else {
if (s == "-" || s == "+") break;
res += s;
}
} else {
break;
}
}
if (res && !(res == "-" || res == "+")) {
const a = flag * Number(res);
if (a >= 2147483647) return 2147483647;
if (a <= -2147483648) return -2147483648;
return a;
}
return 0;
};
6.盛最多水的容器
题目:
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
思路:水的体积,就是Math.min(ai,aj)*(j-i),最容易想到的,遍历所有结果,取最大值
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let res = 0;
for (let i = 1, l = height.length; i < l; i++) {
for (let j = 0; j < i; j++) {
res = Math.max(res, Math.min(height[i], height[j]) * (i - j));
}
}
return res;
};
下面来介绍一下双指针法。
注意一下容器的体积和它们端点高度的关系。现在我们取两边的点,左指针向右移动,右指针向左移动,怎么移动才能使体积有可能变大?或者说,怎么移动才能使体积肯定变小?
注意,我们的体积,是由高度较小的那个端点决定的。所以,如果对于边界情况,左指针的高度小于右指针的高度,那么,如果我们移动右指针,在(j-i)确定减小的情况下,左指针的高度之前是更小的,那么移动了右指针,两个指针上的高度的最小值,不可能比高度还小(因为左指针没动,右指针的高度更大也没意义,比左指针还小,那么I最小值更小),,所以这时我们只能移动左指针,才有可能让体积更大。
如果左右指针一样呢?我们可以同时移动。为什么呢?因为如果内部如果存在更优解,那么肯定不会以此时的指针作为边界(因为x轴距离变小了,然后想要得到更优解,指针上的高度的最小值肯定要更大,所以不可能某一个指针不动还能得到最优解)。
代码:
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let a1 =0;
let a2 = height.length - 1;
let res = 0;
while (a1 < a2) {
res = Math.max(res, Math.min(height[a1], height[a2])*(a2-a1));
if (height[a1] < height[a2]) {
a1++;
} else if(height[a1] > height[a2]) {
a2--;
}else{
a1++;
a2--;
}
}
return res;
};
这样我们最多遍历一轮就能得到最优解