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)
答案:
求1+2+3+...+n,要求不能使用乘除法、for、while、switch、case等关键字及条件判断语句(A?B:C)。
答案:
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路:
//step1:按位与是查看两个数哪些二进制位都为1,这些都是进位位,结果需左移一位,表示进位后的结果
//step2:异或是查看两个数哪些二进制位只有一个为1,这些是非进位位,可以直接加、减,结果表示非进位位进行加操作后的结果
//step3:n1&n2是查看有没有进位位了,如果有,需要重复step1、step2;如果没有,保留n1、n2上二进制为1的部分,用异或将之合为一个数,即为最后结果
答案:
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
输入
+2147483647
1a33
输出
+2147483647
0
答案:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
答案:
给定一个数组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]。不能使用除法。
答案:
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
答案:
8、题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
答案:
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符"google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
答案:
一个链表中包含环,请找出该链表的环的入口结点。
答案:
11、题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
答案:
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
答案:
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路:对称二叉树,左子树的左子树和右子树的右子树,左子树的右子树和右子树的左子树相同即可
答案:
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
答案:
15、题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
答案:
16、题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
答案:
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
答案:
18、题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
答案:
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]}。
答案:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如 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个相邻的格子都没有匹配字符串中下一个的字符,表明当前路径字符串中字符在矩阵中的定位不正确,我们需要回到前一个,然后重新定位。
一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置。
答案:
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7=18。但是,它不能进入方格(35,38),因为3+5+3+8=19。请问该机器人能够达到多少个格子?
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。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;
}