1.猜密码(排列组合问题 —- 回溯)
// 都是数字,都不重复 // 给出范围、最小数字数量?? // 可能的组合:1、数字在范围内的;2、数字和字母分别递增排序;3、每个数字数量大于最小数字数量;4、空返回None // 按字典序输出
// 同:类似洗扑克牌解法,在n张里抽
function guessNum(arr, minRange) {
// 边界:重复、空、范围不够
if (arr.length < minRange || Array.from(new Set(arr)).length !== arr.length) {
return;
}
if (!arr) return "None";
// 思路:遍历删n个元素,递归到单次目标范围,剩余数组sort
let res = [];
// 1、从最小数字范围开始
for (let range = minRange; range < arr.length; range++) {
// 1-1、要删去多少个元素
let diff = arr.length - range;
// 1-2、遍历删去所有可能组合
for (let j = 0; j < arr.length; j++) {
let restArr = iterator(arr, diff, j);
// 2、对剩余数组,按递增排序后,得到单次有效组合,加入结果
res.push(restArr.sort((a, b) => a - b));
}
// 1-3、递归组合
function iterator(arr, diff, j) {
// 完成目标范围的递归,返回组合后的数组
if (!diff) return arr;
// 深拷贝数组
let cloneArr = [...arr];
// 删除单个元素后
cloneArr.splice(j, 1);
return iterator(cloneArr, diff--);
}
}
// 3、结果按字典序排序输出
// 规律:长度同,比较前n个最小的
// 前面是从最小范围开始,得到的res数组里的元素长度是由小到大的
// 递归组合是按由小到大组合去删去某个元素,得到的剩余数组应该是由大到小
// 那么在长度变化的一段起切片,翻转数组后再拼接即可
for (let k = 0; k < res.length - 1; k++) {
// 长度变化
if (res[k].length !== res[k+1].length) {
// 切片后拼接
let cutArr = res.splice(0,k+1).reverse()
res = cutArr.concat(...res)
}
}
return res;
}
2.跳格子游戏
地上共有N个格子,你需要跳完地上所有的格子,但是格子间是有强依赖关系的,跳完前一个格子后,后续的格子才会被开启,格子间的依赖关系由多组steps数组给出,steps[0]表示前一个格子,steps[1]表示steps[0]可以开启的格子:
比如[0,1]表示从跳完第0个格子以后第1个格子就开启了,比如[2,1],[2,3]表示跳完第2个格子后第1个格子和第3个格子就被开启了
请你计算是否能由给出的steps数组跳完所有的格子,如果可以输出yes,否则输出no
说明:
1.你可以从一个格子跳到任意一个开启的格子
2.没有前置依赖条件的格子默认就是开启的
3.如果总数是N,则所有的格子编号为[0,1,2,3....N-1]连续的数组
输入描述:
输入一个整数N表示总共有多少个格子,接着输入多组二维数组steps表示所有格子之间的依赖关系
输出描述:
如果能按照steps给定的依赖顺序跳完所有的格子输出yes
否则输出no
示例1
输入
3
0 1
0 2
输出
yes
说明
总共有三个格子[0,1,2],跳完0个格子后第1个格子就开启了,跳到第0个格子后第2个格子也被开启了,按照0->1->2或者0->2->1的顺序都可以跳完所有的格子
示例2
输入
2
1 0
0 1
输出
no
说明
总共有2个格子,第1个格子可以开启第0格子,但是第1个格子又需要第0个格子才能开启,相互依赖,因此无法完成
示例3
输入
6
0 1
0 2
0 3
0 4
0 5
输出
yes
说明
总共有6个格子,第0个格子可以开启第1,2,3,4,5个格子,所以跳完第0个格子之后其他格子都被开启了,之后按任何顺序可以跳完剩余的格子
示例4
输入
5
4 3
0 4
2 1
3 2
输出
yes
说明
跳完第0个格子可以开启格子4,跳完格子4可以开启格子3,跳完格子3可以开启格子2,跳完格子2可以开启格子1,按照0->4->3->2->1这样就跳完所有的格子
示例5
输入
4
1 2
1 0
输出
yes
说明
总共4个格子[0,1,2,3],格子1和格子3没有前置条件所以默认开启,格子1可以开启格子0和格子2,所以跳到格子1之后就可以开启所有的格子,因此可以跳完所有格子
备注:
1 <= N <500
steps[i].length=2
0<=step[i][0],step[i][1]<N
3.欢乐的周末
小华和小为是很要好的朋友,他们约定周末一起吃饭。通过手机交流,他们在地图上选择了多个聚餐地点(由于自然地形等原因,部分聚餐地点不可达),求小华和小为都能到达的聚餐地点有多少个?
输入描述:
第一行输入m和n,m代表地图的长度,n代表地图的宽度。
第二行开始具体输入地图信息,地图信息包含:
0 为通畅的道路
1 为障碍物(且仅1为障碍物)
2 为小华或者小为,地图中必定有且仅有2个 (非障碍物)
3 为被选中的聚餐地点(非障碍物)
输出描述:
可以被两方都到达的聚餐地点数量,行末无空格。
示例1
输入:
4 4
2 1 0 3
0 1 2 1
0 3 0 0
0 0 0 0
输出:
2
说明:
第一行输入地图的长宽为3和4。
第二行开始为具体的地图,其中:3代表小华和小明选择的聚餐地点;2代表小华或者小明(确保有2个);0代表可以通行的位置;1代表不可以通行的位置。此时两者能都能到达的聚餐位置有2处
示例2
输入:
4 4
2 1 2 3
0 1 0 0
0 1 0 0
0 1 0 0
输出:
0
说明:
第一行输入地图的长宽为4和4。
第二行开始为具体的地图,其中:3代表小华和小明选择的聚餐地点;2代表小华或者小明(确保有2个);0代表可以通行的位置;1代表不可以通行的位置。由于图中小华和小为之间有个阻隔,此时,没有两人都能到达的聚餐地址,故而返回0
备注:
地图的长宽为m和n,其中:
4 <= m <= 100
4 <= n <= 100
聚餐的地点数量为 k,则
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main{
public static int endX; //餐厅横坐标
public static int endY; //餐厅纵坐标
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int lenX = sc.nextInt();
int lenY = sc.nextInt();
int[][] migong = new int[lenX][lenY]; //迷宫数组
List<int[]> hw = new ArrayList<>(); //小华和小为的坐标
List<int[]> canGuan = new ArrayList<>(); //餐厅的坐标
for ( int i=0; i<lenX; i++) {
for ( int j=0; j<lenY; j++) {
migong[i][j] = sc.nextInt();
if (migong[i][j] == 2) {
int[] h = { i, j};
hw.add(h);
} else if (migong[i][j] == 3) {
int[] c = { i, j};
canGuan.add(c);
}
}
}
int[] xh = hw.get(0); //小华的位置坐标
int[] xw = hw.get(1); //小为的位置坐标
int res = 0;
for (int i=0;i<canGuan.size();i++) {
int temp[][] = copy(migong); //复制原数组(否则会导致数组改变)
endX = canGuan.get(i)[0]; //餐馆横坐标
endY = canGuan.get(i)[1]; //餐馆纵坐标
if ( forEnd(xh[0],xh[1],temp) == 1 ) {
temp = copy(migong);
if ( forEnd(xw[0],xw[1],temp) == 1 ) {
res++;
}
}
}
System.out.println(res);
}
public static int[][] copy(int[][] nums){
int x = nums.length;
int y = nums[0].length;
int[][] res = new int[x][y];
for ( int i=0; i<x; i++) {
for ( int j=0; j<y; j++) {
res[i][j] = nums[i][j];
}
}
return res;
}
public static int forEnd(int x,int y,int[][] ints){
int U = x - 1; //向上
int D = x + 1; //向下
int L = y - 1; //向左
int R = y + 1; //向右
if(x==endX && y==endY){ //到达餐馆返回1
return 1;
}
if (U>=0) { //边界处理
if (ints[U][y] != 1) { //只要非1都能通过
ints[x][y] = 1; //能通过则本格置为1,表示已经走过
if (forEnd(U,y,ints)==1) { //递归处理,若值为1表示可以到达直接return 1
return 1;
}
}
}
if (D<ints.length) {
if (ints[D][y] != 1) {
ints[x][y] = 1;
if (forEnd(D,y,ints)==1) {
return 1;
}
}
}
if (L>=0) {
if (ints[x][L] !=1 ) {
ints[x][y] = 1;
if (forEnd(x,L,ints)==1) {
return 1;
}
}
}
if (R<ints[0].length) {
if (ints[x][R] != 1) {
ints[x][y] = 1;
if (forEnd(x,R,ints)==1) {
return 1;
}
}
}
return 0;
}
}
4.解压报文 —- leetcode 394
为了提升数据传输的效率,会对传输的报文进行压缩处理。输入一个压缩后的报文,请返回它解压后的原始报文。
压缩规则:n[str],表示方括号内部的 str 正好重复 n 次。注意 n 为正整数(0 < n <= 100),str只包含小写英文字母,不考虑异常情况。
const decodeString = (s) => {
let numStack = []; // 存倍数的栈
let strStack = []; // 存 待拼接的str 的栈
let num = 0; // 倍数的“搬运工”
let result = ''; // 字符串的“搬运工”
for (const char of s) { // 逐字符扫描
if (!isNaN(char)) { // 遇到数字
num = num * 10 + Number(char); // 算出倍数
} else if (char == '[') { // 遇到 [
strStack.push(result); // result串入栈
result = ''; // 入栈后清零
numStack.push(num); // 倍数num进入栈等待
num = 0; // 入栈后清零
} else if (char == ']') { // 遇到 ],两个栈的栈顶出栈
let repeatTimes = numStack.pop(); // 获取拷贝次数
result = strStack.pop() + result.repeat(repeatTimes); // 构建子串
} else {
result += char; // 遇到字母,追加给result串
}
}
return result;
};