反转一个单链表
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/*var head = {
val: 1,
next: {
val: 2,
next: {
val: 3,
next: null
}
}
}*/
- 方法一:将val存入数组,反转数组
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var newList = null;
var reverseList = function (head) {
var temp = head, result = [];
while (temp) {
result.push(temp.val);
temp = temp.next;
}
temp = head;
var i = 0;
result.reverse();
while (temp) {
temp.val = result[i++];
temp = temp.next;
}
return head;
};
- 方法二:局部反转最终实现整体反转
var reverseList = function (head) {
var pre = null;
while (head) {
var next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
};
斐波那契数列
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N,计算 F(N)。
示例:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.
输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2.
- 解答方法一:递归
//用时68ms 内存消耗34.2m
var fib = function(N) {
var obj = {};
return fibCal(N, obj)
};
function fibCal(n, obj) {
if (0 <= n && n <= 30) {
var result;
if (obj.hasOwnProperty(n)) { //记忆化(memoization)解决重复计算问题
return obj[n];
}
if (n < 2) {
result = n
} else {
result = fib(n - 1) + fib(n - 2)
}
obj[n] = result;
return result;
}
}
- 解答方法二:循环
//推荐:击败 100%,用时 48ms
var fib = function(N) {
const a=[0,1]
for(let i=2;i<=N;i++){
a[i]=a[i-1]+a[i-2]
}
return a[N]
};
const fib = function(N) {
if(N<=1) return N;
let [a, b] = [0, 1];
while(N-- > 1) {
let sum = a + b;
a = b;
b = sum;
}
return b;
};
爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
- 方法一:带索引的递归(斐波那契)
var map = {
0: 1,
1: 1
}
var climbStairs = function(n) {
if(map[n] !== undefined) return map[n]
map[n] = climbStairs(n-1) + climbStairs(n-2)
return map[n]
};
二叉树的最大深度
给定一个二叉树,找出其最大深度。即二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
- 方法一:递归
//执行用时 :76 ms, 在所有 javascript 提交中击败了62.58%的用户
//内存消耗 :37.1 MB, 在所有 javascript 提交中击败了51.55%的用户
var maxDepth = function(root) {
if(root === null)
return 0;
var left = maxDepth(root.left) + 1;
var right = maxDepth(root.right) + 1;
return left > right ? left : right;
};
//简化写法
//执行用时 :80 ms, 在所有 javascript 提交中击败了44.31%的用户
//内存消耗 :37.2 MB, 在所有 javascript 提交中击败了38.35%的用户
var maxDepth = function(root) {
return root === null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1
}
- 方法二:迭代(栈)
//执行用时 :68 ms, 在所有 javascript 提交中击败了90.24%的用户
//内存消耗 :37.7 MB, 在所有 javascript 提交中击败了5.15%的用户
var maxDepth = function(root) {
if(root === null) return 0;
let depth = 1;
let stack = [root];
let currentNode = root;
while(stack.length) {
while(getNotArrivedChild(currentNode)) {
stack.push(getNotArrivedChild(currentNode));
currentNode = getNotArrivedChild(currentNode);
//这里给访问过的节点标记一下
currentNode.ok = true;
//如果加入这个节点后,stack长度大于当前depth,就更新depth为stack长度。
if(stack.length > depth) depth = stack.length;
}
stack.pop();
currentNode = stack[stack.length-1];
}
return depth;
};
//这个函数用来返回当前节点还未被访问过的子节点
function getNotArrivedChild(root) {
if(root.left && !root.left.ok) return root.left;
if(root.right && !root.right.ok) return root.right;
return null;
}
Pow(x,n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例:
输入: 2.00000, 10
输出: 1024.00000
输入: 2.10000, 3
输出: 9.26100
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
/**
* @param {number} x
* @param {number} n
* @return {number}
*/
说明:
-100.0 < x < 100.0
n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1]
- 方法一:递归快速幂算法
如果 n 是偶数,我们可以使用公式 (x ^ n) ^ 2 = x ^ {2 * n}(x n) 2=x 2∗n得到 x ^ n = A * Ax n=A∗A。 如果 n 是奇数,那么 A * A = x ^ {n - 1}A∗A=x n−1。直观地说,我们需要将另一个 xx 和结果相乘,所以 x ^ n = A * A * xx n=A∗A∗x
//执行用时 :72 ms, 在所有 javascript 提交中击败了54.10%的用户
//内存消耗 :33.8 MB, 在所有 javascript 提交中击败了26.02%的用户
var myPow = function (x, n) {
const fastPow = (x, n) => {
if (n == 1) { return x };
let val = n % 2 === 0 ? fastPow(x, n / 2) : fastPow(x, (n - 1) / 2);
return n % 2 === 0 ? val * val : val * val * x;
}
if (x === 1 || n === 0) {
return 1;
}
return n < 0 ? 1 / fastPow(x, Math.abs(n)) : fastPow(x, n);
}
- 方法二:分治、递归
//执行用时 :56 ms, 在所有 javascript 提交中击败了98.48%的用户
//内存消耗 :34.2 MB, 在所有 javascript 提交中击败了5.69%的用户
var myPow = function (x, n) {
//临界条件
if (n === 0) return 1;
if (x === 0) return 0;
if (n < 0) return 1 / myPow(x, -n);
//n为奇数
if (n % 2) return x * myPow(x, n - 1);
//n为偶数
return myPow(x * x, n / 2);
};
合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
- 方法一:递归
取两个列表头部中较小的那个,然后再加上合并其余元素所得到的结果
//执行用时 :80 ms, 在所有 javascript 提交中击败了53.05%的用户
//内存消耗 :35.7 MB, 在所有 javascript 提交中击败了22.92%的用户
var mergeTwoLists = function (l1, l2) {
if (l1 === null) {
return l2;
} else if (l2 === null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
};
- 方法二:迭代
假设 l1 完全小于 l2,并逐个处理元素,在 l1 的必要位置插入 l2 元素。
//执行用时 :76 ms, 在所有 javascript 提交中击败了71.00%的用户
//内存消耗 :35.5 MB, 在所有 javascript 提交中击败了39.88%的用户
var mergeTwoLists = function (l1, l2) {
var prehead = new ListNode();
var prev = prehead;
while (l1 !== null && l2 !== null) {
if (l1.val < l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
prev.next = l1 === null ? l2 : l1;
return prehead.next;
};
第K个语法符号
在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。
给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始)
示例:
输入: N = 1, K = 1
输出: 0
输入: N = 2, K = 2
输出: 1
输入: N = 4, K = 5
输出: 1
解释:
第一行: 0
第二行: 01
第三行: 0110
第四行: 01101001
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/k-th-symbol-in-grammar
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
注意:
N 的范围 [1, 30].
K 的范围 [1, 2^(N-1)].
- 递归
第 K 位的父位应该是第 (K+1) / 2 位。如果父位是 0,那么这一位就是 1 - (K%2)。如果父位是 1,那么这一位就是 K%2
//执行用时 :72 ms, 在所有 javascript 提交中击败了29.88%的用户
//内存消耗 :33.6 MB, 在所有 javascript 提交中击败了40.00%的用户
var kthGrammar = function (N, K) {
if (N == 1) { return 0; }
return (~K & 1) ^ kthGrammar(N - 1, (K + 1) / 2);
};
//执行用时 :76 ms, 在所有 javascript 提交中击败了22.99%的用户
//内存消耗 :34.1 MB, 在所有 javascript 提交中击败了20.00%的用户
var kthGrammar = function (N, K) {
if (K === 1) {
return 0;
}
if (K === 2) {
return 1;
}
var parent = K % 2 == 0 ? kthGrammar(N - 1, K / 2) : kthGrammar(N - 1, (K + 1) / 2);
if (parent === 0) {
return K % 2 === 0 ? 1 : 0;
}
return K % 2 === 0 ? 0 : 1;
}
不同的二叉搜索树
给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树
示例:
输入: 3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {number} n
* @return {TreeNode[]}
*/
- 递归
从序列 1 …n 取出数字 i 并以它作为当前树的根节点。 那么就有 i - 1 个元素可以用来构造左子树,而另外的 n - i 个元素可以用于构造右子树。
//执行用时 :88 ms, 在所有 javascript 提交中击败了76.71%的用户
//内存消耗 :38.4 MB, 在所有 javascript 提交中击败了12.50%的用户
var generateTrees = function (n) {
if (n === 0) { return []; }
return getTree(1, n);
function getTree(start, end) {
var res = [];
if (start > end) {
res.push(null);
return res;
}
for (var i = start; i <= end; i++) {
var left = getTree(start, i - 1);
var right = getTree(i + 1, end);
for (var l = 0; l < left.length; l++) {
for (var r = 0; r < right.length; r++) {
var newNode = new TreeNode(i);
newNode.left = left[l];
newNode.right = right[r];
res.push(newNode);
}
}
}
return res;
}
}
题目来源于leetcode https://leetcode-cn.com/explore