本文共 39197 字,大约阅读时间需要 130 分钟。
剑指offer-javascript版本
1.二维数组中的查找 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。代码
思路:因为矩阵的特殊性,所以先从第一列最后一行开始比较,小于则向上查找,大于则向后查找function Find(target, array)
{ var row = array.length-1;for(var i=row,j=0; i>=0 && j<array[i].length;){
if(target == array[i][j]){ return true; }else if(target > array[i][j]){ j++; continue; }else if(target < array[i][j]){ i--; continue; } } return false; } 2.替换空格 题目描述 请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。代码
思路:使用正则表达式,\s+代表多个空格,?则表示取尽可能少的空格,然后通过replace函数替换为%20function replaceSpace(str)
{ // write code here return str.replace(/\s+?/g,'%20') } 3.从尾到头打印链表 题目描述 输入一个链表,从尾到头打印链表每个节点的值。代码
思路:先将链表每个结点的值存入数组中,然后通过数组的reverse方法,即可从尾到头打印function printListFromTailToHead(head)
{ // write code here var arr=[]; while(head!=null){ 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 reConstructBinaryTree(pre, vin)
{ // write code here if(pre.length==0 || vin.length==0) return null; var index=vin.indexOf(pre[0]); var left=vin.slice(0,index);//中序左子树 var 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类型。代码
思路:队列是先进先出的结构,所以可以直接使用JavaScript的数组类型来实现。var result=[];
function push(node) { // write code here result.push(node)}
function pop() { // write code here return result.shift() } 6.旋转数组的最小数字 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。代码
解法1 思路:直接将数组按从小到大排序,返回第一个元素即可。function minNumberInRotateArray(rotateArray)
{ // write code here rotateArray.sort(function(a,b){ if(a<b) return -1; else return 1; }); return rotateArray[0]; } 解法2 思路:采用二分法解答这个问题,需要考虑3种情况数组为空
部分旋转,例如由(1,2,3,4,5)旋转为(3,4,5,1,2),此时只需要遍历数组,找到当前数比前面的数小的数即可。 完全旋转,例如由(1,2,3,4,5)旋转为(1,2,3,4,5),此时第一个数最小。 function minNumberInRotateArray(rotateArray) { if(rotateArray.length===0){ return 0; } if(rotateArray.length===1){ return rotateArray[0]; } var index=parseInt(Math.floor((rotateArray.length)/2)); var left=rotateArray.slice(0,index); var right=rotateArray.slice(index); var recuArray;//rotateArray[index-]>=rotateArray[0]?right:left; if(rotateArray[index-1]<rotateArray[0]){ recuArray=left; }else { //是否还是旋转数组 if(right[0]<=right[right.length-1]) return right[0]; else recuArray=right; return minNumberInRotateArray(recuArray); } 7.斐波那契数列 题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。 n<=39代码
思路:f(1)=1,f(2)=1,f(3)=f(1)+f(2)function Fibonacci(n)
{ // write code here var result=[]; if(n<=0) return 0; else if(n<=2) return 1; else{ result[1]=1; result[2]=2; for(var i=3;i<=n;i++){ result[i]=result[i-1]+result[i-2]; } return result[n-1] } } 8.跳台阶 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。代码
思路:跳1个台阶有一种方法,跳2个台阶可以有两种跳法,而跳3个台阶则就是跳一个台阶和跳2个台阶方法总和,function jumpFloor(number)
{ // write code here if(number==1) return 1; if(number==2) return 2; var n1=1; var n2=2; var result=0; for(var i=3;i<=number;i++){ result=n1+n2; n1=n2; n2=result; } return result; } 9.变态跳台阶 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。代码
思路:f(1)=1,f(2)=2,f(3)=1+f(2)+f(1)…f(3)可以这样考虑:分跳3(本身值直接+1),跳1,跳2三种情况,跳1之后还剩f(3-1)种跳法,跳2之后
还有f(3-2)种跳法,所以f(3)可以等于这三种分法相加。类推f(n)=1+f(1)+f(2)+…+f(n-1)。
function jumpFloorII(number)
{ // write code here if(number==0) return 0; if(number==1) return 1; if(number==2) return 2; var tmp = 0; while(number > 1){ tmp+=jumpFloorII(number-1); number--; } return tmp+1; } 10.矩形覆盖 题目描述 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?代码
思路:f(n) = f(n-1)+f(n-2), f(1) = 1; f(2) = 2;function rectCover(number)
{ // write code here if(number==1) return 1; if(number==2) return 2; var n1=1; var n2=2; var result=0; for(var i=3;i<=number;i++){ result=n1+n2; n1=n2; n2=result; } return result; } 11.二进制中1的个数 题目描述 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。代码
思路1:把一个整数减去1,再和原来的整数做与运算,会把该整数最右边的一个1变为0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。function NumberOf1(n)
{ // write code here var count=0; while(n!=0){ n=n&(n-1); count++ } return count }12345678910 思路2:如果是负数,对其进行位操作function NumberOf1(n)
{ if(n<0){ n = n>>>0; } var res = n.toString(2); var count = 0; flag = 1; for(var i = 0; i <res.length; i++){ if(res[i] == 1){ count++ } } return count; } 12.数值的整数次方 题目描述 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。代码
思路1:先判断浮点数的正负情况,然后通过累乘的方式function Power(base, exponent)
{ // write code here if(exponent==0) return 1; var result=1,flag=false; if(exponent<0) {exponent=Math.abs(exponent);flag=true;} while(exponent>0){ result*=base; exponent--; } if(flag) result=1/result; return result; } 思路2:JavaScript用**就通过啦function Power(base, exponent)
{ return base**exponent; } 13.调整数组顺序 题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。代码
思路:通过map函数,判断每个数组元素是否为偶数function reOrderArray(array)
{ // write code here var arr1=[],arr2=[]; array.map(function(a){ a%2==0?arr2.push(a):arr1.push(a); }) return arr1.concat(arr2); } 14.链表中倒数第k个结点 题目描述 输入一个链表,输出该链表中倒数第k个结点。代码
思路:通过prev和tail来获取距离为k的一段,然后就tail指向链表最后,function FindKthToTail(head, k)
{ // write code here if(head==null||k<=0) return null; var prev = head; var tail = head; for(var index=0;index<k-1;index++){ if(tail.next!=null){ tail=tail.next; }else{ return null; } } while(tail.next!=null){ prev=prev.next; tail=tail.next; } return prev; } 15.反转链表 题目描述 输入一个链表,反转链表后,输出链表的所有元素。代码
思路:通过prev固定反转后链表头,通过head来反转,通过next指向剩下的链表头部function ReverseList(phead)
{ // write code here if(phead==null||phead.next==null) return phead; var prev=null; var next=null; while(phead!=null){ next=phead.next; phead.next=prev; prev=phead; phead=next; } return prev; } 16.合并两个排序的链表 题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。代码
思路:两个指针分别指向链表元素,然后比较两个元素大小,小的则连到合成后链表,直到达到一个链表的末尾。然后如果哪一个链表还有元素,直接连到合成后链表后面即可。function ListNode(x){
this.val = x; this.next = null; } function Merge(pHead1, pHead2) { // write code here var head=new ListNode(0); var pHead=head; while(pHead1!=null && pHead2!=null){ if(pHead1.val>=pHead2.val){ head.next=pHead2; pHead2=pHead2.next; }else{ head.next=pHead1; pHead1=pHead1.next; } head=head.next; } if(pHead1!=null){ head.next=pHead1; } if(pHead2!=null){ head.next=pHead2; } return pHead.next; } 17.树的子结构 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)代码
思路:比较B是不是A的子树,B是不是A的右子树的子树,B是不是A的左子树的子树。如果根元素相同,则开始判断左子树和右子树function isSubtree(pRoot1,pRoot2){
if (pRoot2 == null) return true;//pRoot2为null,表示子树已经遍历完 if (pRoot1 == null) return false; if(pRoot1.val==pRoot2.val){ return isSubtree(pRoot1.left,pRoot2.left) && isSubtree(pRoot1.right,pRoot2.right); }else{ return false; } }function HasSubtree(pRoot1, pRoot2)
{ // write code here if(pRoot1==null||pRoot2==null) return false; return isSubtree(pRoot1,pRoot2)||HasSubtree(pRoot1.left,pRoot2)||HasSubtree(pRoot1.right,pRoot2); } 18.二叉树镜像 题目描述 操作给定的二叉树,将其变换为源二叉树的镜像二叉树的镜像定义:源二叉树
8
/ \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6 / \ / \ 11 9 7 5 代码 思路:先将根的左右节点互换,然后就是递归调用,对左右子树进行分别处理function Mirror(root)
{ // write code here if(root==null) return null; //首先先将左右节点互换 var tmp = root.left; root.left=root.right; root.right=tmp; //递归 Mirror(root.left); Mirror(root.right);}
19.顺时针打印矩阵 题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.代码
思路:选坐标为(0,0),(1,1)…的点记为(start,start),作为开始坐标,下一圈开始坐标为(start+1,start+1);
判断是否进入下一圈(即是否打印完成)的条件是rows>start2 && cols>start2; 打印一圈的左上角坐标为(start,start),右下角的坐标为(cols-start-1,rows-start-1) 根据一圈左上角和右下角坐标判断“从左到右”,“从上到下”,“从右到左”,“从下到上”需要打印的点。 function printMatrix(matrix) { // write code here if(matrix==null || matrix.length==0) return; var rows = matrix.length; var cols = matrix[0].length; var start=0; var result=[];while(cols>start*2 && rows>start*2){
//x,y方向边界值 var endX = cols-1-start; var endY = rows-1-start; //左到右 for(var i = start;i<=endX;i++){ result.push(matrix[start][i]) } //上到下 if(start<endY){ for(var i = start+1;i<=endY;i++){ result.push(matrix[i][endX]) } } //右到左 if(start<endX && start<endY){ for(var i = endX-1;i>=start;i--){ result.push(matrix[endY][i]) } }//下到上
if(start<endX && start<endY-1){ for(var i = endY-1;i>=start+1;i--){ result.push(matrix[i][start]) } }start++
} return result; } 20.包含min函数的栈 题目描述 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。代码
思路:通过数组来实现栈的功能var result=[]
function push(node) { // write code here return result.push(node) } function pop() { // write code here return result.pop() } function top() { // write code here return result.length>0?result[result.length-1]:null; } function min() { // write code here if(result.length==0||result==null) return; var min=result[0]; result.map(function(a){ if(a<min){ min=a; } }) return min; } 21.栈的压入、弹出 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)代码
思路:设置辅助栈,压入元素,判断栈顶元素是否等于出栈第一个,相同则出栈,不同则继续入栈function IsPopOrder(pushV, popV)
{ // write code here var tmp=[]; for(var i=0,j=0;i<pushV.length;i++){ tmp.push(pushV[i]); while(tmp.length&&tmp[tmp.length-1]==popV[j]){ tmp.pop(); j++; } } return tmp.length==0; } 22.从上往下打印二叉树 题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印。代码
思路1:递归方法,按顺序从上往下,从左往右打印function PrintFromTopToBottom(root)
{ // write code here if(root==null) return []; var result=[]; result.push(root.val); function travel(root){ if(root.left==null && root.right==null) return; if(root.left!=null) result.push(root.left.val) if(root.right!=null) result.push(root.right.val) if(root.left!=null) travel(root.left); if(root.right!=null) travel(root.right); } travel(root); return result; } 思路2:采用队列方法,先将根节点放入队列中,然后打印,接着将左右节点加入队列中。function PrintFromTopToBottom(root) {
var queue = []; queue.push(root); var result = [];if (root == null) {
return result; }while (queue.length) {
var temp = queue.shift();
result.push(temp.val);
if (temp.left) {
queue.push(temp.left); } if (temp.right) { queue.push(temp.right); } } return result; } 23.二叉搜索树的后序遍历 题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。代码
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。思路:后序遍历最后一个元素为根节点,所以先遍历,将数组分为小于根节点部分和大于根节点部分,然后比较这两个部分是否符合与根节点的大小关系。
function VerifySquenceOfBST(sequence)
{ // write code here if(sequence.length<=0) return; return test(sequence,0,sequence.length-1) } function test(sequence,start,end){ if(start>=end) return true; var i=end-1; while(i>=start && sequence[i]>sequence[end]){ i--; } for(var j=i;j>=start;j--){ if(sequence[j]>sequence[end]){ return false; } } return test(sequence,start,i)&&test(sequence,i+1,end-1) } 24.二叉树中和为某一值的路径 题目描述 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。代码
思路:判断当前结点是否是叶子结点,以及curtSum是否等于expectNumber。如果是,把当前路径保存在res结果中;function FindPath(root, expectNumber)
{ // write code herevar result=[];
if(root==null) return result; dfs(root,0,[]); function dfs(root,current,path){ current+=root.val; path.push(root.val) if(current==expectNumber && root.left==null && root.right ==null){ result.push(path.slice(0)) } if(root.left!=null){ dfs(root.left,current,path) } if(root.right!=null){ dfs(root.right,current,path) } path.pop() } return result; } 25.复杂链表的复制 题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)代码
思路:将复杂链表分为头结点和剩余结点两部分,剩余部分采用递归方法。function RandomListNode(x){
this.label = x; this.next = null; this.random = null; } function Clone(pHead) { // write code here if (!pHead) { return null; } // 复制头结点 var node = new RandomListNode(pHead.label); node.random = pHead.random; // 递归其他节点 node.next = Clone(pHead.next); return node; } 26.二叉搜索树与双向链表 题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。代码
思路:将左子树构成双向链表,返回的是左子树的尾结点,将其连接到root的左边; 将右子树构成双向链表,将其追加到root结点之后,并返回尾结点; 向左遍历返回的链表至头结点处,即为所求双向链表的首结点。function Convert(pRootOfTree)
{ // write code here if(pRootOfTree==null){ return null; } var lastNode=null; lastNode=convertNode(pRootOfTree,lastNode); var head=lastNode; while(head && head.left){//循环到头部 head=head.left; } return head; } function convertNode(root,lastNode){ if(root==null) return; if(root.left){//左子树 lastNode=convertNode(root.left,lastNode) } root.left=lastNode; if(lastNode){ lastNode.right=root; } lastNode=root; if(root.right){//右子树 lastNode=convertNode(root.right,lastNode) } return lastNode; } 27.字符串排列 题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。代码
思路:使用递归方法,n个元素的全排列 = (n-1) 个元素全排列 + 一个元素作为前缀。function Permutation(str)
{ // write code here var result=[]; if(str.length<=0){ return []; } var sortTemp=''; var arr = str.split(''); result=sortString(arr,sortTemp,[]); return result}
function sortString(arr,sortTemp,result){ if(arr.length==0){ result.push(sortTemp) }else{ var isRepeat={}; for(var i=0;i<arr.length;i++){ if(!isRepeat[arr[i]]){ var temp=arr.splice(i,1)[0];//取出第一个字符 sortTemp+=temp; sortString(arr,sortTemp,result); arr.splice(i,0,temp);//补全 sortTemp=sortTemp.slice(0,sortTemp.length-1)//清空 isRepeat[temp]=true; } } } return result; } 28.数组中出现次数超过一半的数字 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。代码
思路:可以使用map数据结构,但因为牛客系统不识别es6,所以就使用对象来存储每个元素出现次数。function MoreThanHalfNum_Solution(numbers)
{ // write code here var obj={}; var len = numbers.length; numbers.map(function(num){ if(obj[num]){ obj[num]++ }else{ obj[num]=1; } }) for (var i in obj){ if(obj[i]>Math.floor(len/2)) return i } return 0; } 29.最小的k个数 题目描述 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。代码
思路:先排序,然后slice()即可。function GetLeastNumbers_Solution(input, k)
{ // write code here if(k>input.length) return []; input.sort(function(a,b){ return a-b; })return input.slice(0,k);
} 30.连续子数组的最大和 题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)代码
思路:如果和为负数,则重新开始,如果和为正数,则继续加,然后比较大小,选出最大和即可。function FindGreatestSumOfSubArray(array)
{ // write code here if(array.length==0) return; var tempSum=0,sum=-Number.MAX_VALUE; array.map(function(a){ tempSum=(tempSum<0)?a:tempSum+a; sum=(sum<tempSum)?tempSum:sum; }) return sum; } 31.整数中1出现的次数 题目描述 求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数代码
思路1:如:30143:由于3>1,则个位上出现1的次数为(3014+1)*1
由于4>1,则十位上出现1的次数为(301+1)*10 由于1=1,则百位上出现1次数为(30+0)*100+(43+1) 由于0<1,则千位上出现1次数为(3+0)*1000 注:以百位为例,百位出现1为100199,*100的意思为单步出现了100199,100次,*30是因为出现了30次100~199,+(43+1)是因为左后一次301**不完整导致。如果第i位上的数字为0,则第i位可能出现1的次数由其高位决定,若没有高位,则视为0,此时第i位可能出现1的次数为:其高位数*10(i-1),例如若c为0,则次数为ab*102;
如果第i位上的数字为1,则第i位上可能出现1的次数受其高位和低位影响,若没有,则视为0,此时第i位可能出现1的次数:其高位数*10(i-1)+(低位数+1),例如若c为1,则次数为ab*102+(de+1); 如果第i位上的数字大于1,则第i位上可能出现1的次数受其高位影响,若没有,则视为0,此时第i位可能出现1的次数:(其高位数+1)*10(i-1),例如若c大于1,则次数为(ab+1)*102; function NumberOf1Between1AndN_Solution(n) { // write code here if(n<0) return 0; var high=n,cur,low,tmp,count=0,i=1; while(high!=0){ high=parseInt(n/Math.pow(10,i));//获取高位数 tmp=n%Math.pow(10,i);//获取余下数 cur = parseInt(tmp/Math.pow(10,i-1));//获取第i位 low=tmp%Math.pow(10,i-1);//低位数 if(cur==1){//为1 count+=high*Math.pow(10,i-1)+low+1; }else if(cur==0){//为0 count+=high*Math.pow(10,i-1); }else{ count+=(high+1)*Math.pow(10,i-1) } i++ } return count; } 思路2:暴力解决function NumberOf1Between1AndN_Solution(n)
{ // write code here if(n<0){ return 0; } var count=0; for(var i = 1;i<=n;i++){ var number = i; while(number>0){ if(number%10==1){ count++; } number = Math.floor(number/10); } }return count;
} 32.把数组排成最小的数 题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。代码
思路:排序规则如下:若ab > ba 则 a > b,
若ab < ba 则 a < b, 若ab = ba 则 a = b; 然后从小到大拼接即为所求结果function PrintMinNumber(numbers)
{ // write code here numbers.sort(function(a,b){ var s1=a+''+b; var s2=b+''+a; for(var i=0;i<s1.length;i++){ if(s1.charAt(i)>s2.charAt(i)){ return 1 }else if(s1.charAt(i)<s2.charAt(i)){ return -1; }}
return 1 }) var result=""; numbers.map(function(num){ result=result.concat(num) }) return result; } 33.丑数 题目描述 把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。代码
思路:下一个丑数是由数组中某个丑数A * 2,B * 3,C * 5中的最小值得来的。function GetUglyNumber_Solution(index)
{ // write code here if(index==0) return 0 var uglys=[1]; var factor2=0,factor3=0,factor5=0; for(var i=1;i<index;i++){ uglys[i]=Math.min(uglys[factor2]*2,uglys[factor3]*3,uglys[factor5]*5) if(uglys[i]==uglys[factor2]*2) factor2++; if(uglys[i]==uglys[factor3]*3) factor3++; if(uglys[i]==uglys[factor5]*5) factor5++; } return uglys[index-1] } 34.第一个只出现一次的字符 题目描述 在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置代码
思路:使用map结构,将每次出现字符和出现次数放入map中function FirstNotRepeatingChar(str) {
var map={}; var strArr=str.split(''); strArr.map(function(s){ if(!map[s]){ map[s]=1 }else{ map[s]++ } }) for(var i=0;i<str.length;i++){ if(map[str[i]]==1){ return i } } return -1; } 35.数组中的逆序对 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 输入描述: 题目保证输入的数组中没有的相同的数字 数据范围: 对于%50的数据,size<=10^4 对于%75的数据,size<=10^5 对于%100的数据,size<=2*10^5代码
思路:利用归并排序思想,先求出每个组中逆序对数,然后合并、排序并统计,function InversePairs(data)
{ if(!data||data.length<2) return 0;var copy = data.slice(),
count = 0; count = mergeSort(data,copy,0,data.length-1); return count%1000000007; }function mergeSort(data,copy,start,end){
if(end===start) return 0; var mid = (end-start)>>1, left = mergeSort(copy,data,start,start+mid), right = mergeSort(copy,data,start+mid+1,end), count = 0, p = start+mid,//前一个数组的最后一个下标 q = end,//后一个数组的下标 copyIndex = end;//辅助数组下标,从最后一个算起while(p>=start&&q>=start+mid+1){
if(data[p]>data[q]){ count+=q-start-mid; copy[copyIndex--] = data[p--]; }else{ copy[copyIndex--] = data[q--]; } }while(p>=start){
copy[copyIndex--] = data[p--]; }while(q>=start+mid+1){
copy[copyIndex--] = data[q--]; } return left+right+count; } 36.两个链表的第一个公共节点 题目描述 输入两个链表,找出它们的第一个公共节点。代码
思路:两个指针,遍历后比较节点的值function FindFirstCommonNode(pHead1, pHead2)
{ // write code here var p1=pHead1; var p2=pHead2; while(p1!=p2){ p1=(p1==null?pHead2:p1.next) p2=(p2==null?pHead1:p2.next) } return p1; } 37.数字在排序数组中出现的次数 题目描述 统计一个数字在排序数组中出现的次数。代码
思路1:暴力解决方法,遍历function GetNumberOfK(data, k)
{ // write code here var count=0; data.forEach(function(a){ if(a==k) count++; if(a>k) return count; }) return count } 思路2:二分查找方法:先看中间元素与k相比,小于则在左半部分,大于则在右半部分,function GetNumberOfK(data, k)
{ // write code here var l = 0,r = data.length,mid; while(l < r){ mid = Math.floor((l+r)/2); if(data[mid] < k ){ while(data[mid] == data[mid+1]){ mid++; } l = ++mid; }else if(data[mid] > k){ while(data[mid] == data[mid-1]){ mid--; } r = --mid; }else{ var sign1 = mid,sign2 = mid; while(sign1 <= r && data[sign1] == data[sign1+1]){ sign1++; } while(sign2 >= l && data[sign2] == data[sign2-1]){ sign2--; } return sign1-sign2+1; } } return 0; } 38.二叉树的深度 题目描述 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。代码
思路:递归求左子树和右子树深度,然后比较,最终返回最大值加1/* function TreeNode(x) {
this.val = x; this.left = null; this.right = null; } */ function TreeDepth(pRoot) { // write code here if(pRoot==null) return 0; var left=TreeDepth(pRoot.left); var right=TreeDepth(pRoot.right); return (left>right)?left+1:right+1; } 39.平衡二叉树 题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树。代码
思路:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。遍历左子树和右子树的深度,然后比较二者差值function IsBalanced_Solution(pRoot)
{ // write code here if(pRoot==null) return true; var left=TreeDepth(pRoot.left); var right=TreeDepth(pRoot.right); if((left-right)>1||(left-right)<-1){ return false; } return IsBalanced_Solution(pRoot.left)&&IsBalanced_Solution(pRoot.right) } function TreeDepth(root){ if(root==null) return 0; var left=TreeDepth(root.left); var right=TreeDepth(root.right); return (left>right)?left+1:right+1; } 40.数组中只出现一次的数字 题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。代码
思路:将数组同去重后的数组进行比较,求出数组中元素出现的次数Array.prototype.uniq = function () {
var res = []; var json = {};for (var i = 0; i < this.length; i++) {
if (!json[this[i]]) { res.push(this[i]); json[this[i]] = 1; } }return res;
}function FindNumsAppearOnce(array)
{ // write code here // return list, 比如[a,b],其中ab是出现一次的两个数字 var a = array; var b = a.uniq(); var arr = []; var k = 0;for (var i = 0; i < b.length; i++) {
for (var j = 0; j < a.length; j++) { if (a[j] == b[i]) { k++; } } if (k === 1) { arr.push(b[i]); } k = 0; // 重新赋值0,继续下一次循环 }return arr;
} 41.和为S的连续正数序列 题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序代码
思路:设定两个指针,如果和大于sum,左指针向后移位,如果小于,右指针向后移位。 如果两个指针碰在一起,则跳出, 左指针一直小于sum的一半,function FindContinuousSequence(sum)
{ // write code here if(sum<2) return[]; var result=[]; var a=1,b=2,s=3; while(a<=Math.floor(sum/2)){ if(s<sum){ b++; s+=b; }else if(s>sum){ s-=a a++; }else{ var temp=[]; for(var i=a;i<=b;i++){ temp.push(i) } result.push(temp) if(a+1<b){ s-=a; a++ }else{ break; } } } return result; } 42.和为sum的两个数字 题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。 输出描述: 对应每个测试案例,输出两个数,小的先输出。代码
思路:两头乘积最小,所以两个指针,一个从头,一个从尾分别遍历,当第一次出现和为S时,乘积最小function FindNumbersWithSum(array, sum)
{ // write code here if(array.length<2) return []; var result=[]; var start=0,end=array.length-1; while(start<end){ var s=array[start]+array[end]; if(s<sum){ start++ }else if(s>sum){ end--; }else{ return [array[start],array[end]] } } return []; } 43.左旋转字符串 题目描述 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!代码
思路:通过切分字符串,然后将后部分的字符串贴在前部分字符串前面function LeftRotateString(str, n)
{ // write code here if(str==null||str.length==0) return ''; if(n>str.length) n-=str.length; var left=str.slice(0,n); var right=str.slice(n); return right+left; } 44.翻转单词顺序序列 题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?代码
思路1:通过Array封装好的函数reverse()function ReverseSentence(str)
{ // write code here var array = str.split(' '); return array.reverse().join(' '); } 思路2:通过交换首尾指针所指向的元素function ReverseSentence(str) {
if(!str||!str.trim()) return str; var strArr = str.split(" "), //获取单词数组 len = strArr.length;var start = 0,
end = len - 1, temp; while (start < end) { temp = strArr[start]; strArr[start] = strArr[end]; strArr[end] = temp; ++start; --end; } return strArr.join(" ").trim(); } 45.孩子们的游戏(圆圈中最后剩下的数) 题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)代码
思路1:创立数组,每当到m-1位置时,删除数组中元素,然后将位置计0重新开始function LastRemaining_Solution(n, m)
{ // write code here if(n<1||m<1) return -1; var arr=[]; for(var i=0;i<n;i++) arr.push(i); var idx=0; var start=0; while(arr.length>1){ for(var i=1;i<m;i++){//找到第m个位置 idx=(idx+1)%arr.length; } arr.splice(idx,1) } return arr[0]; } 思路2:在这 n个数字中, 第一个被删除的数字是(m-1)%n。为了简单起见,我们把(m- 1)%n 记为 k,那么删除k之后剩下的 n-1 个数字为 0,1,… ,k-1,k+1,… ,n-1,并且下一次删除从数字 k+1 开始计数。相当于在剩下的序列中, k+1 排在最前面,从而形成 k+1,… ,n- 1,0,I,… ,k-1 。该序列最后剩下的数字也应该是关于 n 和 m 的函数。由于这个序列的规律和前面最初的序列不一样(最初的序列是从 0 开始的连续序列),因此该函数不同于前面的函数,记为 f’(n-1,m)。最初序列最后剩下的数字 f(n, m)一定是删除一个数字之后的序列最后剩下的数字,即 f(n, m)=f’(n-1, m)。接下来我们把剩下的这 n-1 个数字的序列 k-1, …,n-1,0,1,… ,k-1 做一个映射,映射的结果是形成一个从 0 到 n-2 的序列:
function LastRemaining_Solution(n, m) { var last=0; for(var i=2;i<=n;i++){ last=(last+m)%i } return last } 46.扑克牌顺子 题目描述 LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子……LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。代码
思路:首先先排序,然后统计大小王个数,然后遍历,看缺多少个数才能连续,如果超过大小王个数则失败。function IsContinuous(numbers)
{ // write code here if(numbers==null||numbers.length<=0) return false; numbers.sort(function(a,b){ return a-b }) var numOf0 =0,numOfGap=0,len=numbers.length; numbers.map(function(num){ if(num==0){ numOf0++; } }) var small=numOf0,big=small+1; while(big<len){ if(numbers[small]==numbers[big]){ return false } numOfGap+=numbers[big]-numbers[small]-1; small=big; big++ } return numOfGap>numOf0?false:true; } 47.1+2+3+……+n 题目描述 求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。代码
思路:因为不能用for、if等来判断边界,所以只能通过短路运算来判断,function Sum_Solution(n)
{ // write code here var sum=n; //短路运算,n!==0,如果等于0,则不会进行后续的运算 var a=(n!=0)&&((sum+=Sum_Solution(n-1))>0) return sum } 48.把字符串转换成整数 题目描述 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0输入描述:
输入一个字符串,包括数字字母符号,可以为空 输出描述: 如果是合法的数值表达则返回该数字,否则返回0示例1
输入 +2147483647 1a33输出
2147483647 0代码
思路:递归,function StrToInt(str)
{ // write code here if(str==null || str.length<=0) return 0; var len=str.length,index=0,flag=false; var arr=str.split('') if(arr[0]=='+'){ index++ }else if(arr[0]=='-'){ flag=true index++ } var num=strToIntCore(arr,index,len,flag) return num; } function strToIntCore(arr,start,length,minus){ var number=0; if(arr==null||start>=length) return number; while(start<length){ if(arr[start]>='0'&& arr[start]<='9'){ var flag=minus?-1:1; number=number*10+(arr[start]-'0')*flag start++ }else{ number=0; break; } } return number; } 49.不用加减乘除做加法 题目描述 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。代码
思路:不考虑进位直接相加可以看出,1+0=1,1+1=0,0+0=0,这是异或操作(^),对加 0 、0 加 1 、1 加 0 而言, 都不会产生进位,只有 1 加 1 时,会向前产生一个进位。此时我们可以想象成是两个数先做位与运算,然后再向左移动一位function Add(num1, num2)
{ // write code here var sum,carry; do{ sum=num1 ^ num2; carry=(num1&num2)<<1; num1=sum; num2=carry; }while(num2!=0) return sum; } 50.数组中重复数字 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。代码
思路:ES5使用对象,ES6使用mapfunction duplicate(numbers, duplication)
{ // write code here //这里要特别注意~找到任意重复的一个值并赋值到duplication[0] //函数返回True/False if(numbers==null||numbers.length<2) return false var obj={}; numbers.map(function(num){ if(!obj[num]){ obj[num]=1 }else{ duplication.push(num) } }) if(duplication.length>1) { duplication.shift();return true; } else return false } 51.构建乘积数组 题目描述 给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…A[i-1]*A[i+1]…*A[n-1]。不能使用除法。代码
思路:构建前向乘积数组C[i]=A[0]A[1]…*A[i-1],即C[i]=C[i-1]*A[i-1];构建后向乘积数组D[i]=A[n-1]A[n-2]…A[n-i+1],即D[i]=D[i+1]*A[i+1];
通过C[i],D[i]来求B[i]:B[i]=C[i]*D[i]
function multiply(array)
{ // write code here var len=array.length; var arr=[]; if(array==null||len<=0) return ; arr[0]=1; for(var i=1;i<len;i++){ arr[i]=arr[i-1]*array[i-1]//完成所有元素中i前面的乘法 } var temp=1 for(var i=len-2;i>=0;i--){ temp*=array[i+1] arr[i]*=temp } return arr; } 52.正则表达式匹配 题目描述 请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”abaca”匹配,但是与”aa.a”和”ab*a”均不匹配代码
思路:贪心算法//s, pattern都是字符串
function match(s, pattern) { // write code here if (s == null || pattern == null) { return false; } var str=s.split(''); var pat=pattern.split('') var strIndex = 0; var patternIndex = 0; return matchCore(s, strIndex, pat, patternIndex);}
function matchCore(str,strIndex,pattern,patternIndex){ if (strIndex == str.length && patternIndex == pattern.length) { return true; } //pattern先到尾,匹配失败 if (strIndex != str.length && patternIndex == pattern.length) { return false; } //模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位 if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') { if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符 || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符 || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个 } else { return matchCore(str, strIndex, pattern, patternIndex + 2); } } //模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { return matchCore(str, strIndex + 1, pattern, patternIndex + 1); } return false;}
53.表示数值的字符串 题目描述 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”±5”和”12e+4.3”都不是。代码
思路1:正则表达式function isNumeric(s)
{ // write code herereturn s.match(/[\+\-]?[0-9]*(\.[0-9]*)?([eE][\+-]?[0-9]+)?/g)[0]==s;
} 思路2:function isNumeric(s)
{ // write code herevar i = 0;
if(s.charAt(i)== '+' || s.charAt(i)== '-' || isNum(s.charAt(i))){ while(s.charAt(++i)!='\0' && isNum(s.charAt(i))); if(s.charAt(i)=='.'){ if(isNum(s.charAt(++i))){ while(s.charAt(++i)!='\0' && isNum(s.charAt(i))); if(s.charAt(i)=='e'||s.charAt(i)=='E'){ i++; if(s.charAt(i)=='+' || s.charAt(i)=='-' || isNum(s.charAt(i))){ while(s.charAt(++i)!='\0' && isNum(s.charAt[i])); if(s.charAt[i]=='\0') return true; else return false; }else return false; }else if(s.charAt(i)=='\0') return true; else return false; }else if(s.charAt(++i)=='\0') return true; else return false; }else if(s.charAt[i]=='e'||s.charAt[i]=='E'){ i++; if(s.charAt(i)=='+' || s.charAt(i)=='-' || isNum(s.charAt(i))){ while(s.charAt(++i)!='\0' && isNum(s.charAt(i))); if(s.charAt(i)=='\0') return true; else return false; }else return false; }else if(s.charAt(i)=='\0') return true; else return false; }else return false; } function isNum(ch){ if(ch>='0'||ch<='9'){ return true }else{ return false } } 54.字符流中第一个不重复的字符 题目描述 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符“google”时,第一个只出现一次的字符是”l”。输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。代码
思路:设置一个数组存放初次出现的元素。如果这个元素没有过,就放进数组;如果这个元素有过了,就把他从数组删除。输出数组的第一个元素。var map = {};
//Init module if you need function Init() { // write code here map = {}; } //Insert one char from stringstream function Insert(ch) { // write code here if (!map[ch]) { map[ch] = 1; } else { map[ch] ++; } } //return the first appearence once char in current stringstream function FirstAppearingOnce() { for (var ch in map) { if (map.hasOwnProperty(ch)) { if (map[ch] === 1) { return ch; } } } return '#'; } 55.链表中环的入口结点 题目描述 一个链表中包含环,请找出该链表的环的入口结点。代码
思路:通过map来存储每次访问的结点,如果有重复,则是链表入口结点。function EntryNodeOfLoop(pHead)
{ // write code here var cur = pHead ,prev,obj={},lt; while(cur != null){ lt = cur.val; if(!obj[lt]){ obj[lt] = 1; cur = cur.next; }else{ return cur; } } } 56.删除链表中重复的结点 题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5代码
思路:需要三个指针,第一个指针first的next指向头结点,第二个指针pre指向first,确保每次指向的是重复结点的前一个结点,第三个指针head来遍历,遇到重复结点,则向后,直到找到当前结点不等于下一个结点时,将pre的next指向head。function deleteDuplication(pHead)
{ // write code here if(pHead==null||pHead.next==null) return pHead; var first={ val:0, next:pHead } var head=pHead,pre=first; while(head!=null&&head.next!=null){ if(head.val==head.next.val){ while(head.next&&head.next.val==head.val){ head=head.next; } pre.next=head.next; }else{ pre.next=head; pre=pre.next; } //pre=head; head=head.next;}
return first.next; } 57.二叉树的下一个结点 题目描述 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。代码
思路:二叉树为空,则返回空;
节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点; 节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。 function GetNext(pNode) { // write code here if(pNode==null) return null; if(pNode.right!=null) {//节点右孩子存在情况 pNode=pNode.right; while(pNode.left!=null){//沿着向左节点找到叶子节点 pNode=pNode.left; } return pNode; } while(pNode.next!=null){ var pRoot=pNode.next; if(pRoot.left==pNode){ return pRoot } pNode=pNode.next; } return null}
1 58.对称二叉树 题目描述 请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。代码
思路:采用递归方法,先判断根节点的左子树和右子树是否为空,同时为空,则返回true,一个为空,则为false。同时不为空,则判断其值是否相等,然后递归判断左子树和右子树的function isSymmetrical(pRoot)
{ // write code here return isSymmetricalCore(pRoot,pRoot); function isSymmetricalCore(left,right){ if(left==null&&right==null) return true; if(left==null||right==null) return false; if(left.val!=right.val) return false; return isSymmetricalCore(left.left,right.right)&&isSymmetricalCore(left.right,right.left) }}
59.按之字形顺序打印二叉树 题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。代码
思路:按深度遍历二叉树,深度(从0开始)为奇数的反转加入数组。function Print(pRoot)
{ // write code here var queue = [];//存储节点队列 var result = []; if(pRoot==null) return result; var nextLevel = 0; queue.push(pRoot); var toBePrinted = 1;//每一行节点个数 var level = 0;//深度 var arr = []; while(queue.length){ var temp = queue.shift(); toBePrinted--; arr.push(temp.val); if(temp.left){ queue.push(temp.left); nextLevel++; } if(temp.right){ queue.push(temp.right); nextLevel++; } if(toBePrinted==0){ toBePrinted=nextLevel; nextLevel=0; level++; if(level%2==0){ arr.reverse(); } result.push(arr); arr = []; } } return result; } 60.把二叉树打印成多行 题目描述 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。代码
思路:采用队列数据结构,同时需要两个变量分别存储这一层和下一层结点个数。当前层结点个数为零时,则输出结点function Print(pRoot)
{ // write code here var queue = [];//存储节点队列 var result = []; if(pRoot==null) return result; var nextLevel = 0; queue.push(pRoot); var toBePrinted = 1;//每一行节点个数 var arr = []; while(queue.length){ var temp = queue.shift(); toBePrinted--; arr.push(temp.val); if(temp.left){ queue.push(temp.left); nextLevel++; } if(temp.right){ queue.push(temp.right); nextLevel++; } if(toBePrinted==0){ toBePrinted=nextLevel; nextLevel=0; result.push(arr); arr = []; } } return result;}
61.序列化二叉树 题目描述 请实现两个函数,分别用来序列化和反序列化二叉树代码
思路:序列化,将节点值存入数组中,空节点则使用特殊标记存入数组中。
反序列化,从数组中获取元素,为number类型则生成节点,为特殊标记,则为空节点 var arr=[]; function Serialize(pRoot) { // write code here if(pRoot==null){ arr.push('#') return; } arr.push(pRoot.val); Serialize(pRoot.left) Serialize(pRoot.right) } function Deserialize(s) { // write code here if(arr==null){ return null; }if(arr.length<1){
return null; } var root=null; var temp=arr.shift(); if(typeof temp=='number'){ root=new TreeNode(temp); root.left=Deserialize(arr); root.right=Deserialize(arr); } return root; } 62.二叉搜索树的第k个结点 题目描述 给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。代码
思路:二叉搜索树,若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;任意节点的左、右子树也分别为二叉查找树; 所以采用中序遍历的方法,遍历后的结果就是从小到大顺序的结果function KthNode(pRoot, k)
{ // write code herevar arr=[];
if(pRoot===null||k<1){ return null; } function midInorder(root){ if(root.left!==null){ midInorder(root.left); } arr.push(root); if(root.right!==null){ midInorder(root.right); } } midInorder(pRoot); return arr[k-1]; } 63.滑动窗口的最大值 题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。代码
思路:算出滑动窗口个数数组元素个数-滑动窗口大小+1,然后遍历循环,将队列头元素取出,存入新元素。function maxInWindows(num, size)
{ // write code here if(size==0) return[]; var result=[],windows=[]; for(var i=0;i<size;i++){ windows.push(num[i]) }; var len=num.length-size+1; for(var i=0,j=size;i<len;i++){ result.push(Math.max.apply(null,windows)); windows.shift(); windows.push(num[j++]); } return result; } 64.数据流中的中位数 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。代码
var arr=[] function Insert(num) { // write code here arr.push(num); arr.sort() } function GetMedian(){ // write code here var len=arr.length; if(len%2!=0){ return arr[Math.floor(len/2)]; }else{ return (arr[len/2-1]+arr[len/2])/2 } }12345678910111213141516 思路2:大小顶堆,数据流传输时,第偶个数,存入大顶堆中,然后将大顶堆中最大元素取出放入小顶堆中,第奇个数,存入小顶堆,然后将小顶堆中最小元素取出放入大顶堆中。最后小顶堆存放的元素都大于大顶堆存放元素。当数量为偶数时候,访问大顶堆中最大元素和小顶堆中最小元素平均值,当数量为奇数时,访问小顶堆中最小元素。
var arr1=[],arr2=[],count=0;
function Insert(num) { // write code here if(count%2==0){ arr1.push(num); arr1.sort(function(a,b){ return a-b; }) arr2.push(arr1.pop()); arr2.sort(function(a,b){ return b-a; }) }else{ arr2.push(num); arr2.sort(function(a,b){ return b-a; }) arr1.push(arr2.pop()); arr1.sort(function(a,b){ return a-b; }) } count++ } function GetMedian(){ // write code hereif(count%2!=0){
return arr2[arr2.length-1] }else{ return (arr1[arr1.length-1]+arr2[arr2.length-1])/2 } } 65.矩阵中的路径 题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。代码
思路:回溯法function hasPathCore(matrix, rows, cols, row, col, path, pathIndex, visited) {
var hasPath = false; if (row < rows && col < cols && row >= 0 && col >= 0 && visited[row][col] === false) { if (matrix[row * cols + col] === path[pathIndex]) { visited[row][col] = true; if (pathIndex === path.length - 1) { hasPath = true; } else { hasPath = hasPathCore(matrix, rows, cols, row - 1, col, path, pathIndex + 1, visited) || hasPathCore(matrix, rows, cols, row + 1, col, path, pathIndex + 1, visited) || hasPathCore(matrix, rows, cols, row, col - 1, path, pathIndex + 1, visited) || hasPathCore(matrix, rows, cols, row, col + 1, path, pathIndex + 1, visited);if (!hasPath) {
visited[row][col] = false; } } } } return hasPath; }function hasPath(matrix, rows, cols, path)
{ // write code here if (path.length <= 0) { return true; } var visited = []; var temp = []; var i, j; for (i = 0; i < rows; i++) { temp = []; for (j = 0; j < cols; j++) { temp.push(false); } visited.push(temp); } for (i = 0; i < rows; i++) {; for (j = 0; j < cols; j++) { if (hasPathCore(matrix, rows, cols, i, j, path, 0, visited)) { return true; } } } return false; } 66.机器人的运动范围 题目描述 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?代码
思路:回溯法function movingCount(threshold, rows, cols)
{ // write code here var count=0; if(threshold<1||rows<1||cols<1){ return count; } var visited=[]; for(var i=0; i<rows; i++){ visited[i]=[]; for(var j=0; j<cols; j++){ visited[i][j]=false; } } count = movingCountSum(threshold,0,0,rows,cols,visited); return count; } function movingCountSum(threshold,m,n,rows,cols,visited){ var count = 0; if(m>=0&&m<rows&&n>=0&&n<cols&&!visited[m][n]&&getSum(m,n)<=threshold){ visited[m][n]=true; count = 1+movingCountSum(threshold,m,n-1,rows,cols,visited)+ movingCountSum(threshold,m,n+1,rows,cols,visited)+ movingCountSum(threshold,m-1,n,rows,cols,visited)+ movingCountSum(threshold,m+1,n,rows,cols,visited); } return count; } function getSum(m,n){ var str = [].concat(m,n).join(''); var sum=0; for(var i=0; i<str.length; i++){ sum+=Number(str[i]); } return sum; } ———————————————— 版权声明:本文为CSDN博主「hello_yqq」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_42235173/article/details/90897252