剑指offer在线编程(四)

1、题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
思路: 
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
        k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2并且从k开始报0。
现在我们把他们的编号做一下转换:
k     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解: 例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情 况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n。
令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]。
递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)
答案:
function LastRemaining_Solution(n, m)
{
    if (n===0) {
    	return -1;
    }
    if (n===1) {
    	return 0;
    }
    return (LastRemaining_Solution(n-1,m)+m)%n;
}
2、题目描述
求1+2+3+...+n,要求不能使用乘除法、for、while、switch、case等关键字及条件判断语句(A?B:C)。
答案:
function Sum_Solution(n)
{
    if (n===1) {
    	return 1;
    }
    return Sum_Solution(n-1)+n;
}
3、题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路:
//step1:按位与是查看两个数哪些二进制位都为1,这些都是进位位,结果需左移一位,表示进位后的结果
//step2:异或是查看两个数哪些二进制位只有一个为1,这些是非进位位,可以直接加、减,结果表示非进位位进行加操作后的结果
//step3:n1&n2是查看有没有进位位了,如果有,需要重复step1、step2;如果没有,保留n1、n2上二进制为1的部分,用异或将之合为一个数,即为最后结果
答案:
function Add(num1, num2)
{
	var a=num1^num2;
	var b=(num1&num2)<<1;
	while (b) {
		num1=a;
		num2=b;
		a=num1^num2;
		b=(num1&num2)<<1;
	}
	return a;
}
4、题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
输入
+2147483647
1a33
输出
+2147483647
0
答案:
function StrToInt(str)
{
	if (str.length===0) {
		return 0;
	}
    var reg=/^(\+|-)?(\d+)$/;
    var match=str.match(reg);
    if (!match) {
    	return 0;
    }
    var num=parseInt(match[0],10);
    if (!num) {
    	return 0;
    }
    return num;
}
5、题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
答案:
function duplicate(numbers, duplication)
{
    // write code here
    //这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    //函数返回True/False
    if (numbers.length===0) {
    	return false;
    }
    for(var i=0;i<numbers.length;i++){
    	if (numbers.indexOf(numbers[i])!==numbers.lastIndexOf(numbers[i])) {
    		duplication[0]=numbers[i];
    		return true;
    	}
    }
    return false;
}
6、题目描述
给定一个数组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]。不能使用除法。
答案:
function multiply(array)
{
    if (array.length===0) {
    	return [];
    }
    var arrB=[];
    for(var i=0;i<array.length;i++){
    	var temp=1;
    	for(var j=0;j<array.length;j++){
    		if (j!==i) {
    			temp*=array[j];
    		}
    	}
    	arrB.push(temp);
    }
    return arrB;
}
7、题目描述
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
答案:
function match(s, pattern)
{
    var reg=new RegExp("^"+pattern+"$");
    return reg.test(s);
}
注意:此处不可用reg="/^"+pattern+"$/",因为这样得到的reg是一个字符串,而字符串是没有test方法的;我们需要得到的是一个正则对象,像这样reg=new RegExp("^"+pattern+"$")才是。
8、题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
答案:
function isNumeric(s)
{
    var reg=/^(\+|-)?(\d+)?(\.?\d+)?((e|E)(\+|-)?\d+)?$/;
    return reg.test(s);
}
9、题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符"google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
答案:
function Init()
{
    this.obj={};
    this.Insert=Insert;
    this.FirstAppearingOnce=FirstAppearingOnce;
}
//Insert one char from stringstream
function Insert(ch)
{
    if(!this.obj[ch]){
        this.obj[ch]=1;
    }else{
        this.obj[ch]++;
    }
}
//return the first appearence once char in current stringstream
function FirstAppearingOnce()
{
    for(var attr in obj){
        if(obj[attr]==1){
        	return attr;
        }
    }
    return "#";
}
10、题目描述
一个链表中包含环,请找出该链表的环的入口结点。
答案:
function ListNode(x){
    this.val = x;
    this.next = null;
}
function EntryNodeOfLoop(pHead)
{
	if (!pHead||!pHead.next) {
		return null;
	}
    var arr=[];var index=0;
    while (pHead) {
    	arr.push(pHead);
    	pHead=pHead.next;
    	index=arr.indexOf(pHead);
    	if (index!=-1) {
    		return arr[index];
    	}
    	arr.push(pHead);
    	pHead=pHead.next;
    }
    return null;
}
思路:将节点添加到数组中,然后在数组中查找当前节点的下一个节点,如果找到了,则找到的这个节点就是环的入口;如果没找到返回null。
11、题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
答案:
function ListNode(x){
    this.val = x;
    this.next = null;
}
function deleteDuplication(pHead)
{
    if(pHead===null){
    	return null;
    } 
    if(pHead!==null&&pHead.next===null){
    	return pHead;
    } 
    let first={ val:-1,next:pHead },
    	cur=pHead,
    	pre=first;
    first.next=pHead;
    while(cur!==null&&cur.next!==null){
        if(cur.val===cur.next.val){
            let val=cur.val;
            while(cur!==null&&cur.val===val){
                cur=cur.next;
                pre.next=cur;
            }
        }else{
            pre=cur;
            cur=cur.next;
        }
    }
    return first.next;
}
12、题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
答案:
function TreeLinkNode(x){
    this.val = x;
    this.left = null;
    this.right = null;
    this.next = null;
}
function GetNext(pNode)
{
    if(pNode==null){
        return null;
    }
    //1 节点右孩子存在,找右孩子结点的左节点
    if(pNode.right!=null){
        var p=pNode.right;
        while(p.left!=null){
            p=p.left;
        }
        return p;
    }
    //2当前节点是父节点的第一个左节点并且该节点没有右子树
    while(pNode.next!=null){
        if(pNode==pNode.next.left){
            return pNode.next;
        }
        pNode=pNode.next;
    }
    return null;
}
13、题目描述
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路:对称二叉树,左子树的左子树和右子树的右子树,左子树的右子树和右子树的左子树相同即可
答案:
function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
}
function isSymmetrical(pRoot)
{
    if(pRoot==null){
        return true;
    }
    return Symmetrical(pRoot.left,pRoot.right);
}
function Symmetrical(left,right){
    if(left==null&&right==null){
        return true;
    }
    if(left!=null&&right!=null){
        if(left.val==right.val){
            return Symmetrical(left.left,right.right)&&Symmetrical(left.right,right.left);
        }
    }
}
14、题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
答案:
function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
}
function Print(pRoot)
{
    if(!pRoot){
        return [];
    }
	var que=[],res=[],arr=[];
    var temp=0,count=1,level=0;
    que.push(pRoot);
    while(que.length){
        var p=que.shift();
        count--;
        arr.push(p.val);
        if(p.left){
            que.push(p.left);
            temp++;
        }
        if(p.right){
            que.push(p.right);
            temp++;
        }
        if(count==0){
            count=temp;
            temp=0;
            level++;
            if(level%2==0){
                arr.reverse();
            }
            res.push(arr);
            arr=[];
        }
    }
    return res;
}
思路:层次遍历,将当前节点如队,队头出队添加到数组,如果队头有左孩子节点,将其入队;如果有右孩子结点,将其入队。队不空时循环出队。
15、题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
答案:
function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
}
function Print(pRoot)
{
    if(!pRoot){
        return [];
    }
	var que=[],res=[],arr=[];
    var temp=0,count=1;
    que.push(pRoot);
    while(que.length){
        var p=que.shift();
        count--;
        arr.push(p.val);
        if(p.left){
            que.push(p.left);
            temp++;
        }
        if(p.right){
            que.push(p.right);
            temp++;
        }
        if(count==0){
            count=temp;
            temp=0;
            res.push(arr);
            arr=[];
        }
    }
    return res;
}
说明:类似上题。
16、题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
答案:
function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
}
var arr=[];
function Serialize(pRoot)
{	
    if (!pRoot) {
    	arr.push('#');
    	return;
    }
    arr.push(pRoot.val);
    Serialize(pRoot.left);
    Serialize(pRoot.right);
}
function Deserialize()
{
    if (!arr) {
    	return null;
    }
    var temp=arr.shift();
    if (typeof temp=='number') {
    	var root=new TreeNode(temp); //创建节点
    	root.left=Deserialize();
    	root.right=Deserialize();
    }
    return root;
}
17、题目描述
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
答案:
function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
}
function KthNode(pRoot, k)
{
    if (pRoot==null) {
    	return null;
    }
    var arr=[];
    midSort(pRoot);
    if (k>arr.length) {
    	return null;
    }
    return arr[k-1];
    function midSort(pRoot){
		if (pRoot) {
			midSort(pRoot.left);
			arr.push(pRoot);
			midSort(pRoot.right);
		}
	}
}
思路:中序遍历二叉排序树的结果刚好是非递减顺序。
18、题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
答案:
var arr=[];
function Insert(num)
{
    arr.push(num);
    arr.sort();
}
function GetMedian(){
	if (!arr) {
		return null;
	}
	if (arr.length%2==1) {
		return arr[parseInt(arr.length/2)];
	}else{
		return (arr[parseInt(arr.length/2)]+arr[parseInt(arr.length/2)-1])/2;
	}
}
说明:记得排序哟,也要记得parseInt取整哟!
19、题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
例如,如果输入数组{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]}。
答案:
function maxInWindows(num, size)
{
    if (!num||!size||size>num.length) {
    	return [];
    }
    var res=[];
    for(var i=0;i+size<=num.length;i++){
    	var arr=num.slice(i,i+size);
    	res.push(Math.max(...arr));
    }
    return res;
}
20、题目描述 【回溯法典型案例】
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:回溯算法
   这是一个可以用回朔法解决的典型题。首先,在矩阵中任选一个格子作为路径的起点。如果路径上的第i个字符不是ch,那么这个格子不可能处在路径上的第i个位置。如果路径上的第i个字符正好是ch,那么往相邻的格子寻找路径上的第i+1个字符。除在矩阵边界上的格子之外,其他格子都有4个相邻的格子。重复这个过程直到路径上的所有字符都在矩阵中找到相应的位置。
  由于回朔法的递归特性,路径可以被开成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。
  由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。当矩阵中坐标为(row,col)的格子和路径字符串中相应的字符一样时,从4个相邻的格子(row,col-1)左,(row-1,col)上,(row,col+1)右,(row+1,col)下中去定位路径字符串中下一个字符。如果4个相邻的格子都没有匹配字符串中下一个的字符,表明当前路径字符串中字符在矩阵中的定位不正确,我们需要回到前一个,然后重新定位。
  一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置。

答案:
function hasPath(matrix, rows, cols, path)
{
    if (!path) {
    	return true;
    }
    var visited=[];
    for(var i=0;i<rows;i++){
    	var temp=[];
    	for(var j=0;j<cols;j++){
    		temp.push(false);
    	}
    	visited.push(temp);
    }
    for(var i=0;i<rows;i++){
    	for(var j=0;j<cols;j++){
    		if (hasPathCore(matrix,rows,cols,i,j,path,0,visited)) {
    			return true;
    		}
    	}
    }
    return false;
}
function hasPathCore(matrix,rows,cols,row,col,path,pathIndex,visited){
	var pathFlag=false;
	if(row<rows&&row>=0&&col<cols&&col>=0&&visited[row][col]===false) {
		if(matrix[row*cols+col]===path[pathIndex]) {
			visited[row][col]=true;
			if(pathIndex===path.length-1) {
		        pathFlag=true;
		    }else{
		    	pathFlag=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(!pathFlag) { //防止当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。
          			visited[row][col] = false; //此时将已经改为true的节点改回false
        		}
		    }
		}
	}
	return pathFlag;
}
21、题目描述【回溯法典型案例】
地上有一个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)
{
	if (!threshold) {
		return 1;
	}
    var visited=[];
    for(var i=0;i<rows;i++){
    	var temp=[];
    	for(var j=0;j<cols;j++){
    		temp.push(false);
    	}
    	visited.push(temp);
    }
    var count=getMoved(rows,cols,0,0,visited,threshold);
    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]); //利用NUmber函数将每个数字字符转换成数字
    }
    return sum;
}
/*function getSum(m,n){  //尽量不要使用这种方法,少用for循环
	var rowNum=m.toString().split('');
	var colNum=n.toString().split('');
	var sum=0;
	for(var i=0;i<rowNum.length;i++){
		sum+=parseInt(rowNum[i]);
	}
	for(var i=0;i<colNum.length;i++){
		sum+=parseInt(colNum[i]);
	}
	return sum;
}*/
function getMoved(rows,cols,row,col,visited,k){
	var temp=0;
	if(row<rows&&row>=0&&col<cols&&col>=0&&visited[row][col]===false&&getSum(row,col)<=k) {
		visited[row][col]=true;
		temp=1+getMoved(rows,cols,row-1,col,visited,k)+
				getMoved(rows,cols,row+1,col,visited,k)+
				getMoved(rows,cols,row,col-1,visited,k)+
				getMoved(rows,cols,row,col+1,visited,k);
	}
	return temp;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明致成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值