数组
1.剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
function Find(target, array) {
for(let i=0;i<array.length;i++){
if(array[i][0]>target || array[i][array[i].length-1]<target) continue;
let low = 0, high = array[i].length - 1, mid;
while (low <= high){
mid = Math.floor((low+high)/2);
if(array[i][mid] > target) high = mid-1;
if(array[i][mid] < target) low = mid+1;
if(array[i][mid] === target) return true;
}
}
return false;
}
2.剑指 Offer 03. 数组中重复的数字
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
/**
* @param {number[]} nums
* @return {number}
*/
var findRepeatNumber = function(nums) {
let map =new Map();
for(let i=0;i<nums.length;i++){
if(map[nums[i]]){
return nums[i];
}else{
map[nums[i]]=1;
}
}
return null;
};
3.剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
var spiralOrder = function(matrix) {
let direction="right";
let data=[];
if(matrix.length==0){
return data;
}
let l=0;
let r = matrix[0].length-1;
let b=matrix.length-1
let h=0;
while(l<=r&&h<=b){
if(direction=="right"){
for(let i=l;i<=r;i++){
data.push(matrix[h][i]);
}
h++;
direction="down";
}
else if(direction=="down"){
for(let i=h;i<=b;i++){
data.push(matrix[i][r]);
}
r--;
direction="left";
}
else if(direction=="left"){
for(let i=r;i>=l;i--){
data.push(matrix[b][i]);
}
b--;
direction="up";
}
else if(direction=="up"){
for(let i=b;i>=h;i--){
data.push(matrix[i][l]);
}
l++;
direction="right";
}
}
return data;
};
4.剑指 Offer 53 - I. 在排序数组中查找数字I
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
var search = function(nums, target) {
let n=0;
for(let i=0;i<nums.length;i++){
if(nums[i]==target){
n++;
}
else if(nums[i]>target){
break;
}
}
return n;
};
5.剑指 Offer 53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
示例 2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8
var missingNumber = function(nums) {
for(let i=0;i<nums.length;i++){
if(nums[i]!=i){
return i;
}
}
return nums[nums.length-1]+1;
};
字符串
1.剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = “abcdefg”, k = 2
输出: “cdefgab”
示例 2:
输入: s = “lrloseumgh”, k = 6
输出: “umghlrlose”
var reverseLeftWords = function(s, n) {
let temp=s.slice(0,n);
let temp2=s.slice(n);
return temp2.concat(temp);
};
2.剑指 Offer 58 - I. 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。
示例 1:
输入: “the sky is blue”
输出: “blue is sky the”
示例 2:
输入: " hello world! "
输出: “world! hello”
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入: “a good example”
输出: “example good a”
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
var reverseWords = function(s) {
let temp=s.trim().split(' ').reverse();
let res=temp.join(' ');
res=res.replace(/\s+/g,' ');//去掉多余空格
return res;
};
***3.剑指 Offer 67.***把字符串转换成整数(!正则表达式)
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
var strToInt = function(str) {
let res =str.match(/^\s*[+-]?\d+/);
if(!res){
return 0;
}
res=str.trim().match(/[+-]?\d+/)[0];
if(res>=2**31){
return 2**31-1;
}else if(res<=Math.pow(-2,31)){
return Math.pow(-2,31)
} else {
return res;
}
};
栈
1.剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
var CQueue = function() {
this.stack1 = [];
this.stack2 = [];
};
/**
* @param {number} value
* @return {void}
*/
CQueue.prototype.appendTail = function(value) {
this.stack1.push(value);
};
/**
* @return {number}
*/
CQueue.prototype.deleteHead = function() {
if(this.stack2.length){
return this.stack2.pop();
}else{
if(this.stack1.length){
while(this.stack1.length){
this.stack2.push(this.stack1.pop());
}
return this.stack2.pop();
}else{
return -1;
}
}
};
/**
* Your CQueue object will be instantiated and called as such:
* var obj = new CQueue()
* obj.appendTail(value)
* var param_2 = obj.deleteHead()
*/
链表
1**.剑指 Offe**r 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
var getKthFromEnd = function(head, k) {
//双指针
var p = head, q = head
while(p){
if(k > 0){
p = p.next//p先走k步,保证p、q之间相差k步
k--
}else{
p = p.next;//当p走到结尾时,此时的q就是倒数的第k个
q = q.next
}
}
return q
};```
2.剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
//双指针法
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
let pre=null,cur=head,temp;
while(cur){
temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
};
3.剑指 Offer 52. 两个链表的第一个公共节点
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
if (headA==null || headB==null) {
return null;
}
let p1=headA,p2=headB;
while(p1!==p2){
p1=(p1==null?headB:p1.next);
p2=(p2==null?headA:p2.next);
}
return p1;
};
4.剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var deleteNode = function(head, val) {
if(head.val==val){
return head.next;
}
let p=head.next,cur=head,s=head;
while(p.val!=val){
p=p.next;
s=s.next;
}
s.next=p.next;
return cur;
};
树
1.剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var mirrorTree = function(root) {
if(!root){
return null;
}
[root.left,root.right]=[root.right,root.left];
mirrorTree(root.left);
mirrorTree(root.right);
return root;
};
2.剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} k
* @return {number}
*/
var kthLargest = function(root, k) {
let cur =inOrder(root);
return cur.reverse()[k-1];
};
let res=[];
function inOrder(root){//中序遍历
if(!root){
return null;
}
inOrder(root.left);
res.push(root.val);
inOrder(root.right);
return res;
}
3.剑指 Offer 68 - II. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
、、题解
查找当前节点的左右子树。
如果左右子树都查到了结果,说明当前节点就是最近祖先。
如果p、q都在当前节点的左侧,如果左侧节点不是p或q,就继续递归查找。
如果是任意一个,则返回这个节点。 因为此节点就是另一个节点的祖先。
右侧同样。
var lowestCommonAncestor = function(root, p, q) {
if(!root)return null
if(root.val === p || root.val === q) return root
let left = lowestCommonAncestor(root.left,p,q)
let right = lowestCommonAncestor(root.right,p,q)
if(left && right) return root
return left || right
};
4.剑指 Offer 32 - II. II从上到下打印二叉树
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var levelOrder = function(root) {
if (!root) return [];
const queue = [root];
const res = []; // 存放遍历结果
let level = 0; // 代表当前层数
while (queue.length) {
res[level] = []; // 第level层的遍历结果
let levelNum = queue.length; // 第level层的节点数量
while (levelNum--) {
const front = queue.shift();
res[level].push(front.val);
if (front.left) queue.push(front.left);
if (front.right) queue.push(front.right);
}
level++;
}
return res;
};
5.剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
因为前序遍历的第一个元素就是当前二叉树的根节点。那么,这个值就可以将中序遍历分成 2 个部分。
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function (preorder, inorder) {
if (!preorder.length) return null
var node = new TreeNode(preorder[0])
var index = inorder.indexOf(preorder[0])
node.left = buildTree(preorder.slice(1, index + 1), inorder.slice(0, index))
node.right = buildTree(preorder.slice(index + 1), inorder.slice(index + 1))
return node
};
6.515. 在每个树行中找最大值
您需要在二叉树的每一行中找到最大的值。
示例:
输入:
1
/ \
3 2
/ \ \
5 3 9
输出: [1, 3, 9]
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var largestValues = function(root) {
let max=[];
if(root==null){
return max;
}
max.push(root.val);
let queue=[root];
let level=0;
let temp=[];
while(queue.length){
let len=queue.length;
temp[level]=[];
while(len--){
let cur=queue.shift();
if(cur.left!=null){
queue.push(cur.left);
temp[level].push(cur.left.val);
}
if(cur.right!=null){
queue.push(cur.right);
temp[level].push(cur.right.val);
}
}
let p=Math.max(...temp[level]);
max.push(p);
level++;
}
return max.slice(0,max.length-1);
};
深度优先搜索
1.剑指 Offer 55 - II. 平衡二叉树
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/
9 20
/
15 7
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isBalanced = function(root) {
if(!root){
return true;
}
let l=depth(root.left);
let r=depth(root.right);
if(Math.abs(l-r)>1){
return false;
}else{
return isBalanced(root.left)&&isBalanced(root.right);
}
};
function depth(root){
if(!root){
return 0;
}
let left=depth(root.left);
let right=depth(root.right);
return Math.max(left,right)+1;
}
动态规划
1.剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
for(let i=1;i<nums.length;i++){
if(nums[i-1]>0){
nums[i]+=nums[i-1];
}
}
return Math.max(...nums);
};
字符串
1.剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
if(s.length==0){
return 0;
}
let temp=[];
let max=0;
let res=[];
for(let i=0;i<s.length;i++){
if(temp.includes(s[i])){
temp.push(s[i]);
temp=temp.slice(temp.indexOf(s[i])+1);
res.push(temp.length);
}else{
temp.push(s[i]);
res.push(temp.length);
}
}
return Math.max(...res);
};