【Algorithm】用JS刷剑指offer
1 二维数组的查找
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
题目分析
该二维数组中的一个数,它左边的数都比它小,下边的数都比它大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间,当前元素的查找区间为左下角的所有元素。
代码
function Find(target, array) {// 边界条件if (array == null || array.length === 0 || array[0].length === 0) {return false;}var rows = array.length;var cols = array[0].length;var r = 0;var c = cols - 1;while (r <= rows - 1 && c >= 0) {if (target == array[r][c]) {return true;} else if (target > array[r][c]) {r++;} else {c--;}}return false;
}
2 替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
题目分析
我们如果要替换空格,两步
- 先知道空格的位置
- 替换,但是字符串中有多个空格,所以我们就要循环,替换完之后再去查找字符串空格位置
当然你也可以选择用正则
代码
function replaceSpace(str)
{return str.replace(/\s/g, '%20')
}
3 从尾到头打印链表
题目描述
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
题目分析
代码
function printListFromTailToHead(head)
{// write code herelet arr = [];while(head) {arr.push(head.val);head = head.next}return arr.reverse();
}
4 重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
题目分析
前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。
代码
/* function TreeNode(x) {this.val = x;this.left = null;this.right = null;
} */
function reConstructBinaryTree(pre, vin)
{// write code here// 边界条件if(pre.lenght === 0 || vin.length === 0) {return null;}let index = vin.indexOf(pre[0]);let left = vin.slice(0, index);let right = vin.slice(index + 1);return {val: pre[0],left: reConstructBinaryTree(pre.slice(1, index + 1), left),right: reConstructBinaryTree(pre.slice(index + 1), right)}
}
5 用两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
题目分析
in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。
代码
var inStack = [];
var outStack = [];function push(node)
{// write code hereinStack.push(node);}
function pop()
{if(!outStack.length) {while(inStack.length) {outStack.push(inStack.pop());}}return outStack.pop();
}
6 旋转数组中的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
题目分析
在一个有序数组中查找一个元素可以用二分查找,二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度都为O(logN)
。
本题可以修改二分查找算法进行求解:
- 当 nums[mid] <= nums[high] 的情况下,说明解在 [low, mid] 之间,此时令 high = mid;
- 否则解在 [mid + 1, high] 之间,令 low = mid + 1。
代码
暴力使用sort
function minNumberInRotateArray(rotateArray)
{// 边界条件if(rotateArray.length === 0) return 0;return rotateArray.sort(function(a, b){return a - b})[0];
}
二分法
function minNumberInRotateArray(rotateArray) {if (rotateArray.length == 0) return 0;var low = 0;var high = rotateArray.length - 1;while (low < high) {var mid = low + high >> 1;if (rotateArray[mid] <= rotateArray[high])high = mid;elselow = mid + 1;}return rotateArray[low];
}
7 斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
题目分析
代码
递归
虽然可以实现,但是运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
function Fibonacci(n)
{// write code hereif(n >= 0 && n < 2) return n;return Fibonacci(n - 1) + Fibonacci(n - 2);
}
动态规划
function Fibonacci(n)
{// write code herelet arr = [];arr[0] = 0;arr[1] = 1;for (let i = 2; i <= n; i++) {arr[i] = arr[i - 1] + arr[i - 2];}return arr[n];
}
8 跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
题目分析
稍微分析就知道这是斐波那契数列,所以可以动态规划来做
- 如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1)
- 假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
- 由1、2假设可以得出总跳法为:f(n) = f(n-1) + f(n-2)
- 通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2
- 可以发现最终得出的是一个斐波那契数列
代码
递归
function jumpFloor(number)
{// write code hereif (number <= 2) return number;return jumpFloor(number - 1) + jumpFloor(number - 2);
}
动态规划
function jumpFloor(number)
{// write code herelet arr = new Array(number + 1).fill(null);arr[0] = 0;arr[1] = 1;arr[2] = 2;for(let i = 3; i <= number; i++) {arr[i] = arr[i - 1] + arr[i - 2];}return arr[number]
}
9 变态跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
题目分析
根据上一个题目可以知道,青蛙只跳1或2可以得出是一个斐波那契问题,即a[n] = a[n-1] + a[n-2]
,那么能跳1,2,3个台阶时a[n] = a[n-1] + a[n-2] + a[n-3] + ... + a[1]
那么有:
a[n] = a[n-1] + a[n-2] + ... + a[1] ①
a[n-1] = a[n-2] + a[n-3] + ... + a[1] ②
② - ①
可得:a[n] = 2*a[n-1]
代码
function jumpFloorII(number)
{// write code herelet acc = 1;while(--number) {acc = acc * 2;}return acc;
}
10 矩形覆盖
题目描述
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
题目分析
其本质就是一个斐波那契数列
代码
function rectCover(number)
{// write code herevar arr = [];arr[0] = 0;arr[1] = 1;arr[2] = 2;for (let i = 3; i <= number; i++) {arr[i] = arr[i - 2] + arr[i - 1];}return arr[number];
}
11 二进制中1的个数
题目描述
题目分析
如果一个整数与1做与运算的结果是1,则表示该整数最右边一位是1,否则是0
那么解法就出来了:一个一个向右移位,并且判断最右边的那个位是否为1,为1就count++
但是这样输入负数时会陷入死循环,因为负数右移时,最高位补得是1,那么这样会有无数个1
此时这时候有两个解决办法:
- 既然不能对要操作的数一个一个右移位,那么我们可以考虑对另一个数1进行左移位计算
- 把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制有多少个1,就可以进行多少次这样的操作
代码
方法1
function NumberOf1(n) {let count = 0;let flag = 1;while (flag) {// 循环的次数等于整数二进制的位数,32位的整数需要循环32位if (flag & n) count++;flag = flag << 1;}return count;
}
方法2
function NumberOf1(n) {let count = 0;while (n) {// 有几位就循环几次,效率高n = n & n - 1count++;}return count;
}
Java
public class Solution {public int NumberOf1(int n) {int count = 0;int flag = 1;while(flag != 0) {if((n & flag) != 0) {count++;}flag = flag << 1;}return count;}
}
12 数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
题目分析
- js中所有数字都是浮点数,所以
3 / 2 === 1.5
,所以在进行位运算和乘除运算时,最好都使用parseInt()
- 用右移运算(
>>
)代替除运算(/
),所以parseInt(3) / 2 === parseInt(3) >> 1
,直接3 >> 1
也可以,但是浮点数位运算效率十分低 - 用位与运算代替求余运算(
%
),所以parseInt(3) % 2 === parseInt(3) & 1
,直接3 & 1
也可以,但是浮点数位运算效率十分低
代码
function Power(base, exponent) {// write code hereif (exponent === 0) return 1;if (exponent === 1) return base;var isNegative = false;if (exponent < 0) {exponent = -exponent;isNegative = true;}var pow = Power(base * base, parseInt(exponent) >> 1);if (parseInt(exponent) & 1 !== 0) pow = pow * base;return isNegative ? 1 / pow : pow;
}
13 调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
题目分析
两个变量作为奇数和偶数的下标,我们可以维护两个指针
- 第一个指针初始化时指向数组的第一个数字,它只向后移动
- 第二个指针初始化时指向数组的最后一个数字,它只向前移动
在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数,并且第二个指针指向的数字是奇数,则交换这两个数字
代码
function reOrderArray(array)
{// write code herelet oddbegin = 0;let oddcount = 0;let arr = [];for (let i = 0; i < array.length; i++) {if(array[i] & 1) oddcount++}for (let i = 0; i < array.length; i++) {if (array[i] & 1){arr[oddbegin++] = array[i];} else{arr[oddcount++] = array[i];}}return arr;
}
14 链表中倒数第k个节点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
题目分析
代码
我的解法
/*function ListNode(x){this.val = x;this.next = null;
}*/
function FindKthToTail(head, k)
{// write code hereif (head == null || k <= 0) return null;let arr = [];while(head) {arr.push(head);head = head.next;}return arr[arr.length - k];
}
引用类型不是共享内存吗?应使用浅拷贝
/* function ListNode(x){this.val = x;this.next = null;}*/
function FindKthToTail(head, k) {if (head === null || k <= 0) return null;let pNode1 = head;let pNode2 = head;while (--k) {if (pNode2.next !== null) {pNode2 = pNode2.next;} else {return null;}}while (pNode2.next !== null) {pNode1 = pNode1.next;pNode2 = pNode2.next;}return pNode1;
}
15 反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
题目分析
所以第一步要把当前节点的next记住
定义3个指针
- 当前遍历到的节点
- 它的前一个节点
- 它的后一个节点
代码
/*function ListNode(x){this.val = x;this.next = null;
}*/
function ReverseList(pHead)
{// write code here// 链表题都要判断边界条件,下句比较通用,都可以写if (!pHead || !pHead.next) return pHead;// 记录当前节点let current = pHead;// 记录当前节点的前一个节点let pre = null;// 记录当前节点的后一个节点let next;while(current) {// 先记录当前节点的下一个节点,到时候断掉就找不到了next = current.next;// 将当前节点的下一节点指向前一个节点current.next = pre;// 前一个节点后移1位pre = current;// 当前节点后移1位current = next;}return pre;
}
16 合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调递增规则。
题目分析
运用递归
代码
/*function ListNode(x){this.val = x;this.next = null;
}*/
function Merge(pHead1, pHead2)
{// write code hereif(!pHead1) return pHead2;if(!pHead2) return pHead1;let pMergeHead = null;if (pHead1.val < pHead2.val) {pMergeHead = pHead1;pMergeHead.next = Merge(pHead1.next, pHead2);} else {pMergeHead = pHead2;pMergeHead.next = Merge(pHead1, pHead2.next);}return pMergeHead;
}
17 树的子结构❓
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
题目分析
分析如何判断树B是不是树A的子结构,只需要两步。很容易看出来这是一个递归的过程。一般在树的求解方面都和递归有关。
- 在树A中找到和B的根结点的值一样的结点R
- 判断树A中以R为根结点的子树是不是包含和树B一样的结点
代码
/* function TreeNode(x) {this.val = x;this.left = null;this.right = null;} */
function HasSubtree(pRoot1, pRoot2) {let res = false;if (pRoot1 === null || pRoot2 === null) return false;if (pRoot1.val === pRoot2.val) res = doesTree1HasTree2(pRoot1, pRoot2);if (!res) res = HasSubtree(pRoot1.left, pRoot2);if (!res) res = HasSubtree(pRoot1.right, pRoot2);return res;
}function doesTree1HasTree2(pRoot1, pRoot2) {if (pRoot2 === null) return true;if (pRoot1 === null) return false;if (pRoot1.val !== pRoot2.val) return false;return doesTree1HasTree2(pRoot1.left, pRoot2.left) && doesTree1HasTree2(pRoot1.right, pRoot2.right);
}
18 二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像
二叉树的镜像定义:源二叉树
题目分析
递归交换左右节点
代码
/* function TreeNode(x) {this.val = x;this.left = null;this.right = null;
} */
function Mirror(root)
{// write code hereif(root === null) return;if(root.left === null && root.right === null) return;let temp = root.left;root.left = root.right;root.right = temp;if(root.left) Mirror(root.left);if(root.right) Mirror(root.right);
}
19 顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如
题目分析
用左上和右下的坐标定位出一次要旋转打印的数据,一次旋转打印结束后,往对角分别前进和后退一个单位。
代码
function printMatrix(matrix)
{// write code hereif(matrix == null) return;const rows = matrix.length;const cols = matrix[0].length;let start = 0;let result = [];while (rows > start * 2 && cols > start * 2) {result = result.concat(printMatrixInCircle(matrix, rows, cols, start));start++;}return result;
}function printMatrixInCircle(matrix, rows, cols, start) {const endX = cols - 1 - start;const endY = rows - 1 - start;let result = [];// 从左往右for (let i = start; i <= endX; i++) {result.push(matrix[start][i]);}// 从上往下for(let i = start + 1; i <= endY; i++) {result.push(matrix[i][endX]);}// 从右往左for(let i = endX - 1; i >= start && endY > start; i--) {result.push(matrix[endY][i]);}// 从下往上for(let i = endY -1; i >= start + 1 && endX > start; i--) {result.push(matrix[i][start]);}return result;
}
20 包含min函数的栈 中等 辅助栈
21 栈的压入、弹出序列 中等 辅助栈
22 从上往下打印二叉树 简单 广度遍历、队列
23 二叉树的后续遍历序列 中等 画图
24 二叉树和为某一值的遍历序列 中等 深度遍历、递归
25 复杂链表的复制 难 map保存<N,N’> || N->N’得S->S‘
26 二叉搜索树与双向链表 中等偏难 递归、中序遍历
27 字符串的排列 难 回溯法 || 递归全排列法
28 数组中出现次数超过一半的数 中等 partion法 || times变量变化法
29 最小的k个数 中等 partion法
30 连续子数组的最大值 中等 找规律、动态规划、注意判断条件
31 (~n整数中1出现的次数 中等 位运算 || 数学分析
把数组排成最小的数 简单偏难 改变排序规则
丑数 难 动态规划、注意判断条件
第一个只出现一次的字符 哈希表记录
数组中的逆序对 难+ 基于归并排序、临时数组
两个链表中的第一个公共节点 简单 双指针法
数字在排序数组中出现的次数 简单偏难 二分法改造
二叉树的深度 简单 递归
平衡二叉树 简单 递归
数组中只出现一次的数字 简单 indexOf || map记录 || 异或
和为S的连续正数序列 中等 数学分析
和为S的字符串 简单 双指针
左旋转字符串 简单 裁剪拼接
单次翻转序列 简单 转数组,对每项反序
扑克牌顺子 中等 注意题目条件、位运算判断数字重复
孩子们的游戏 难 数学分析得出公式 || 画图按题目做、注意下标
求1+2+3+…+n 中等 位运算、递归
不用加减乘除做加法 中等 位运算
把字符串转成整数 中等 位运算
数组中重复的数字 中等 将值放到对应位置上
构建乘积数组 中等偏上 借助中间变量存储后面的乘积
正则表达式的匹配 难 注意判断条件、递归
表示数值的字符串 中等 正则
字符流中第一个不重复的数字 中等 map记录 || indexOf法
链表中环的入口节点 中等 双指针法、数学分析
删除链表中重复的节点 中等 加头节点、注意多个重复
二叉树的下一个节点 中等 画图、分析各种情况
对称的二叉树 中等 递归、对称遍历
按之字形顺序打印二叉树 难 广度遍历、两个栈
把二叉树打印成多行 中等偏难 队列+两个记录变量
序列化二叉树 中等 数组代表流、递归
二叉搜索树的第k个节点 中等 中序遍历+计数变量
数据流的中位数 中等 partion法 || 维持排序 || 排序链表法 || AVL树 || 最大堆和最小堆
滑动窗口中的最大值 难 改变参考对象、双端队列、存下标
矩阵中的路径 中等 回溯法
机器人的运动范围 中等 回溯法
参考资料
[1] https://www.cnblogs.com/wuguanglin/p/code-interview.html
[2] https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/剑指 Offer 题解 - 目录.md
[3] https://www.cnblogs.com/wuguanglin/p/SummaryOfJSDoAlgorithmProblem.html
【Algorithm】用JS刷剑指offer相关推荐
- 用JS刷剑指offer
1.二叉树的深度 输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根.叶结点)形成树的一条路径,最长路径的长度为树的深度. //DFS,递归 function TreeDepth(pR ...
- JS版剑指offer
JS版剑指offer JS刷题总结 牛客网 递归算法的时间复杂度:递归的总次数*每次递归的数量. 递归算法的空间复杂度:递归的深度*每次递归创建变量的个数. 二叉树(12道): 剑指Offer(4): ...
- C#刷剑指Offer | 【常考题】最小的k个数
[C#刷题]| 作者 / Edison Zhou 这是EdisonTalk的第299篇学习分享 我们来用之前学到的数据结构知识来刷<剑指Offer>的一些核心题目(精选了其中30+道题目) ...
- C#刷剑指Offer | 二叉树中和为某一值的路径
[C#刷题]| 作者 / Edison Zhou 这是EdisonTalk的第292篇原创内容 我们来用之前学到的数据结构知识来刷<剑指Offer>的一些核心题目(精选了其中30+道题目) ...
- C#刷剑指Offer | 二叉搜索树的后序遍历序列
[C#刷题]| 作者 / Edison Zhou 这是EdisonTalk的第289篇原创内容 我们来用之前学到的数据结构知识来刷<剑指Offer>的一些核心题目(精选了其中30+道题目) ...
- C#刷剑指Offer | 从上到下打印二叉树
[C#刷题]| 作者 / Edison Zhou 这是EdisonTalk的第288篇原创内容 我们来用之前学到的数据结构知识来刷<剑指Offer>的一些核心题目(精选了其中30+道题目) ...
- C#刷剑指Offer | 在O(1)时间删除链表节点
[C#刷题]| 作者 / Edison Zhou 我们来用之前学到的数据结构知识来刷<剑指Offer>的一些核心题目(精选了其中30+道题目),希望对你有帮助!本文题目为:在O(1)时间删 ...
- 二刷剑指Offer:剑指Offer+LeetCode(全53题)
文章目录 剑指Offer刷题 67. 剪绳子(贪心算法) 66. 机器人的运动范围(dfs) 65. 矩阵中的路径(回溯算法) 64. 滑动窗口的最大值(双端队列) 63. 数据流中的中位数 62. ...
- 《剑指offer》刷题总结
从三月初开始刷剑指offer上面的题,到现在花了近二十天的时间终于刷完了.应该说,掌握上面的技巧应付一些公司面试题和小公司的笔试题是完全没有问题的.之前参加一个公司笔试,算法题就有一题是剑指offer ...
最新文章
- 获取DataRow某列的值的封装
- JSPDF运用实例(解决图片跨域问题)
- 设计模式08: Composite 组合模式(结构型模式)
- 《剑指offer》链表中倒数第k个结点
- [BZOJ3626] [LNOI2014] LCA 离线 树链剖分
- 前端学习(2211):网络请求模块的选择--axios的配置相关
- fusion按照多个centos,设置静态ip
- 计算本周是几号到几号
- Python基础----字典
- 无法使用带括号的初始值设定项初始化数组_leetcode32. 最长有效括号
- apache tomcat 6集群负载和session复制(转)
- UIView延迟效果做出动画/UIView动画块
- 集成阿里云视频播放器
- 压铸件孔隙率的检测与等级测定
- java冒泡排序代码详解
- ALEXA解释(日IP500,可以使你进10万内)
- php 验证码不正确,php验证码提示错误
- python绘制指数函数图像及性质_python实现画出e指数函数的图像
- Unity2018.3.11下载安装详细图文教程
- 前后端分离时ajax发送请求时后端能接送,但是前端的response为空时
热门文章
- hive转16进制unhex_Hive,
- Ubuntu20.04 编译Buildroot(支持Qt编译)
- CMutex、CCriticalSection、CSemaphore、CEvent、WaitForSingleObject 的小例子
- 高可用性系统架构:负载均衡与容错策略
- 汽车IVI中控开发入门及进阶(九):显示屏
- 江苏扬州通报考古人员被打事件:两名城管队员已被拘
- D-Map: Visual Analysis of Ego-centric Information Diffusion Patterns in Social Media
- Viso画图之 -- 平行四边形
- 好未来23届校园招聘开始了~靠谱内推
- Flash存储W25Q16芯片