牛客网华为机试题训练(JavaScript Node环境)
文章目录
前言
-
题目摘录牛客网华为机试题,答案为作者亲自操刀编码。
-
直接拷贝代码即可运行,记得如下运行环境:
一、题目
1. HJ11 数字颠倒
描述:
输入一个整数,将这个整数以字符串的形式逆序输出
程序不考虑负数的情况,若数字含有0,则逆序形式也含有0,如输入为100,则输出为001
输入描述:
输入一个int整数
输出描述:
将这个整数以字符串的形式逆序输出
示例1:
输入:1516000
输出:0006151
答案: (思路:转成字符串数组,反转,再转成字符串)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
rl.on('line', (str) => {
deal(str)
})
function deal(str) {
let result = [...str].reverse().join('');
console.log(result)
}
rl.on('close', () => {
rl.close();
})
2. HJ22 汽水瓶
描述
有这样一道智力题:“某商店规定:三个空汽水瓶可以换一瓶汽水。小张手上有十个空汽水瓶,她最多可以换多少瓶汽水喝?”答案是5瓶,方法如下:先用9个空瓶子换3瓶汽水,喝掉3瓶满的,喝完以后4个空瓶子,用3个再换一瓶,喝掉这瓶满的,这时候剩2个空瓶子。然后你让老板先借给你一瓶汽水,喝掉这瓶满的,喝完以后用3个空瓶子换一瓶满的还给老板。如果小张手上有n个空汽水瓶,最多可以换多少瓶汽水喝?
输入描述:
输入文件最多包含10组测试数据,每个数据占一行,仅包含一个正整数n(1<=n<=100),表示小张手上的空汽水瓶数。n=0表示输入结束,你的程序不应当处理这一行。
输出描述:
对于每组测试数据,输出一行,表示最多可以喝的汽水瓶数。如果一瓶也喝不到,输出0。
示例1
输入:
3
10
81
0
输出:
1
5
40
答案:(思路:空瓶两个则换1瓶; 空瓶1个则换0瓶;多于两个对3取整再加余数为手头的空瓶数,则递归调用当前函数)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
rl.on('line', (str) => {
let num = Number(str);
if (num !== 0) {
let result = getNum(Number(str))
console.log(result)
}
})
rl.on('close', () => {
rl.close();
})
function getNum(num) {
if (num === 2) { //两个空瓶
return 1;
}
if (num === 1) { //一个空瓶
return 0;
}
if (num > 2) {
let q = Math.floor(num / 3);
let r = num % 3;
return q + getNum(q + r);
}
}
3. HJ53 杨辉三角的变形
描述
以上三角形的数阵,第一行只有一个数1,以下每行的每个数,是恰好是它上面的数,左上角数到右上角的数,3个数之和(如果不存在某个数,认为该数就是0)。
求第n行第一个偶数出现的位置。如果没有偶数,则输出-1。例如输入3,则输出2,输入4则输出3。
输入n(n <= 1000000000)
本题有多组输入数据,输入到文件末尾,请使用while(cin>>)等方式读入
输入描述:
输入一个int整数
输出描述:
输出返回的int值
示例1
输入:
4
2
输出:
3
-1
答案一:
(思路:从第一行依次构造整个变形杨辉三角二维数组,没有数值的地方填充0。 缺点:浪费内存,效率低)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
// n 行数
rl.on('line', (str) => {
let n = Number(str)
let resultArray = createTriangle(n);
let pos = getEven(resultArray, n)
console.log(pos)
})
rl.on('close', () => {
rl.close();
})
//构造变形杨辉三角
function createTriangle(n) {
let row = n;
let col = 2 * n - 1;
//初始化二维数组
let array = initArray(row, col);
//初始化第一行元素
let pos = Math.floor(col / 2);
let firstLine = Array(col).fill(0);
firstLine[pos] = 1;
array[0] = firstLine;
// 根据第一行依次初始化后面各行 注意边界值:j=0 和 j=col-1
for (let i = 1; i < row; i++) {
for (let j = 0; j < col; j++) {
if (j === 0) {
array[i][j] = array[i - 1][j] + array[i - 1][j + 1]
} else if (j === col - 1) {
array[i][j] = array[i - 1][j - 1] + array[i - 1][j]
} else {
array[i][j] = array[i - 1][j - 1] + array[i - 1][j] + array[i - 1][j + 1]
}
}
}
return array
}
//初始化二维数组
function initArray(row, col) {
let array = new Array(row);
for (let i = 0; i < row; i++) {
array[i] = new Array(col);
for (let j = 0; j < col; j++) {
array[i][j] = 0;
}
}
return array;
}
//找出二维数组,第n行,第一个偶数
function getEven(array, n) {
let arr = array[n - 1];
let index = arr.findIndex((_el) => {
return _el % 2 === 0;
})
return index === -1 ? '-1' : index + 1;
}
答案二:
(思路:已知,第一行数据,递归调用获取第n行数据。条件:arr[n][m] = arr[n-1][m]+arr[n-1][m-1]+arr[n-1][m-2]。 缺点:函数调用开支大,效率低)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
// n 行数
rl.on('line', (str) => {
let n = Number(str)
let resultArray = getArr(n);
let pos = getEven(resultArray)
console.log(pos)
})
rl.on('close', () => {
rl.close();
})
/**
* 获取第n行数组
* @param row 行号
*/
function getArr(row) {
if (row === 1) { //第一行
return [1]
}
let col = 2 * row - 1;
let arr = new Array(col).fill(0); //当前行数组长度,默认填充0;
let preArr = getArr(row - 1);
for (let j = 0; j < col; j++) {
arr[j] = (preArr[j - 2] || 0) + (preArr[j - 1] || 0) + (preArr[j] || 0);
}
return arr;
}
//找出数组第一个偶数
function getEven(arr) {
let index = arr.findIndex((_el) => {
return _el % 2 === 0;
})
return index === -1 ? '-1' : index + 1;
}
4. HJ2 计算某字母出现次数
描述
写出一个程序,接受一个由字母、数字和空格组成的字符串,和一个字母,然后输出输入字符串中该字母的出现次数。不区分大小写,字符串长度小于500。
输入描述:
第一行输入一个由字母和数字以及空格组成的字符串,第二行输入一个字母。
输出描述:
输出输入字符串中含有该字符的个数。
示例1
输入:
ABCabc
A
输出:
2
答案
(思路:正则匹配即可,忽略大小写)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
let inputStrArr = [];
let index = 0;
rl.on('line', (str) => {
inputStrArr[index] = str;
index++;
if (inputStrArr.length === 2) {
let reg = new RegExp(inputStrArr[1], "gi");
let result = inputStrArr[0].match(reg);
console.log(result)
if (result) {
console.log(result.length)
} else {
console.log(0)
}
}
})
rl.on('close', () => {
rl.close;
})
5. HJ8 合并表记录
描述
数据表记录包含表索引和数值(int范围的正整数),请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算,输出按照key值升序进行输出。
输入描述:
先输入键值对的个数
然后输入成对的index和value值,以空格隔开
输出描述:
输出合并后的键值对(多行)
示例1
输入:
4
0 1
0 2
1 2
3 4
输出:
0 3
1 2
3 4
答案
(思路:每一行都存入数组,如:[“4”, “0 1”, “0 2”, “1 2”, “3 4”],根据数据个数遍历,使用空格符分割,存入到对象,对象key已经存在,则value相加,否则新增对象属性;最后遍历对象输出即可)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.on('close', () => {
rl.close;
})
let inputArr = [];
let index = 0;
rl.on('line', (str) => {
inputArr[index] = str;
index++;
let num = Number(inputArr[0]);
if (num + 1 === inputArr.length) {
let resultObj = {};
for (let i = 1; i <= num; i++) {
let tempArr = inputArr[i].split(' ');
let key = tempArr[0];
if (Object.prototype.hasOwnProperty.call(resultObj, key)) {
resultObj[key] += Number(tempArr[1]);
} else {
resultObj[key] = Number(tempArr[1]);
}
}
printObj(resultObj)
}
})
function printObj(object){
for (const obj in object) {
console.log(obj+" "+ object[obj])
}
}
6. HJ17 坐标移动
描述
开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。
输入:
合法坐标为A(或者D或者W或者S) + 数字(两位以内)
坐标之间以;分隔。
非法坐标点需要进行丢弃。如AA10; A1A; % ; YAD; 等。
下面是一个简单的例子 如:
A10;S20;W10;D30;X;A1A;B10A11;;A10;
处理过程:
起点(0,0)
-
A10 = (-10,0)
-
S20 = (-10,-20)
-
W10 = (-10,-10)
-
D30 = (20,-10)
-
x = 无效
-
A1A = 无效
-
B10A11 = 无效
-
一个空 不影响
-
A10 = (10,-10)
结果 (10, -10)
注意请处理多组输入输出
输入描述:
一行字符串
输出描述:
最终坐标,以逗号分隔
示例1
输入:
A10;S20;W10;D30;X;A1A;B10A11;;A10;
输出:
10,-10
答案
(思路:通过正则匹配找到合法的字符串,放入到数组中,然后遍历数据移动坐标即可)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.on('close', () => {
rl.close;
})
rl.on('line', (str) => {
let dataArr = getIegalStr(str)
let pos = move(dataArr);
console.log(pos.join(','))
})
/**
* 获取符合要求的字符串数据
*/
function getIegalStr(str) {
let arr = str.split(";")
let reg = /^[ADWS]\d{1,2}$/
let result = [];
for (let i = 0; i < arr.length; i++) {
if (reg.test(arr[i])) {
reg.lastIndex = 0;
result.push(arr[i]);
}
}
return result;
}
/**
* 坐标移动
* @param dataArr
* @returns {number[]}
*/
function move(dataArr) {
let origin = [0, 0];
const len = dataArr.length;
for (let i = 0; i < len; i++) {
let str = dataArr[i];
let num = Number(str.slice(1))
switch (str.slice(0, 1)) {
case "A":
origin[0] -= num
break;
case "D":
origin[0] += num
break;
case "W":
origin[1] += num
break;
case "S":
origin[1] -= num
break;
}
}
return origin;
}
7. HJ77 火车进站
描述
给定一个正整数N代表火车数量,0<N<10,接下来输入火车入站的序列,一共N辆火车,每辆火车以数字1-9编号,火车站只有一个方向进出,同时停靠在火车站的列车中,只有后进站的出站了,先进站的才能出站。
要求输出所有火车出站的方案,以字典序排序输出。
输入描述:
有多组测试用例,每一组第一行输入一个正整数N(0
输出描述:
输出以字典序从小到大排序的火车出站序列号,每个编号以空格隔开,每个输出序列换行,具体见sample。
示例1
输入:
3
1 2 3
输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
说明:
第一种方案:1进、1出、2进、2出、3进、3出
第二种方案:1进、1出、2进、3进、3出、2出
第三种方案:1进、2进、2出、1出、3进、3出
第四种方案:1进、2进、2出、3进、3出、1出
第五种方案:1进、2进、3进、3出、2出、1出
请注意,[3,1,2]这个序列是不可能实现的。
答案一:
(思路:获取序列的全排列,从全排列中过滤出合法的出栈序列)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.on('close', () => {
rl.close;
})
let inputArr = [];
rl.on('line', (str) => {
inputArr.push(str)
if (inputArr.length === 2) {
let arr = inputArr[1].split(' ')
let allSeq = getLegalArray(arr);
printArr(allSeq.sort()); //输出字典排序
}
})
/**
* 获取合法的出栈序列
* 步骤 1、获取原始数组的全排列
* 2、从全排列中过滤出合法的序列 即为出栈序列
*
* @param arr 原始数组
* @returns {[]}
*/
function getLegalArray(arr) {
const len = arr.length;
let result = [];
const originArray = JSON.parse(JSON.stringify(arr));
function func(index = 0) {
for (let i = index; i < len; i++) {
// 交换数组元素
swap(arr, i, index);
if (index < len - 1) {
//index+1 后的元素全排列
func(index + 1)
} else {
//console.log(arr);
if (isLegal(arr, originArray)) { //是否合法的出栈序列
result.push(JSON.parse(JSON.stringify(arr)))
}
}
//恢复数组元素原来顺序
swap(arr, i, index);
}
}
func(0);
return result;
}
/**
* 交换数组i、j位置元素
* @param arr
* @param i
* @param j
*/
function swap(arr, i, j) {
if (i === j) {
return;
}
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 从全排列中过滤合法的出栈序列
* 条件: 1、待入栈序列依次入栈
* 2、栈顶元素和出栈序列队头元素比较,相等则出栈;否则,继续入栈
* 3、入栈序列全部入栈,判断栈中元素是否为空,为空:说明出栈序列事合法的序列,否则,不合法
*
* @param popQueue 待判断的出栈序列(二维数组、全排列)
* @param pushQueue 入栈序列
*/
function isLegal(popQueue, pushQueue) {
//栈内元素
const stack = [];
const pop = JSON.parse(JSON.stringify(popQueue));
const push = JSON.parse(JSON.stringify(pushQueue));
for (let i = 0; i < push.length; i++) {
stack.push(push[i]);
//栈顶元素是否和出栈序列队头元素相等
while (stack.length > 0 && pop[0] === stack[stack.length - 1]) {
stack.pop(); //栈顶元素出栈
pop.splice(0, 1); //移除队头元素
}
}
return stack.length === 0;
}
function printArr(arrays) {
for (let i = 0; i < arrays.length; i++) {
console.log(arrays[i].join(' '))
}
}
8. HJ105 记负均正II
描述
输入 n 个整型数,统计其中的负数个数并求所有非负数的平均值,结果保留一位小数,如果没有非负数,则平均值为0
本题有多组输入数据,输入到文件末尾。
输入描述:
输入任意个整数,每行输入一个。
输出描述:
输出负数个数以及所有非负数的平均值
示例 1
输入:
-13
-4
-7
输出:
3
0.0
示例 2
输入:
-12
1
2
输出:
1
1.5
答案 :
const readline = require('readline');
let rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
let numArr = []
let count = 0;
rl.on('line', function f(line) {
let num = Number(line);
if (num >= 0) {
numArr.push(num);
} else {
count++
}
})
rl.on('close', function () {
getAvg();
})
function getAvg() {
let total = 0;
let avg = 0;
numArr.forEach((el) => {
total += el;
})
if (numArr.length > 0) {
avg = (total / numArr.length);
}
avg = avg.toFixed(1)
console.log(count)
console.log(avg);
}
二、其他题目
1. 分糖
小明抓一把糖果并平分出去一半给同学,不够分的可以加一颗或减一颗,加一减一平分都算分一次求最少几次分完(最少几次分到只剩一个)。(输入为第一把糖果的数量)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
let num;
rl.on('line', function (line) {
num = Number(line);
count = 0;
getCount(num);
console.log(Math.min(count))
})
rl.on('close', function (line) {
})
/**
* 递归计数
* @param num
*/
let count = 0;
function getCount(num) {
if (num === 1) {
return;
}
count++;
if (num % 2 === 0) {
num /= 2;
getCount(num)
} else {
if (isAdd(num)) {
num += 1;
} else {
num -= 1;
}
getCount(num)
}
}
/**
*
* @param num
*
* 1、num必为奇数
* 2、num加 1 或 减 1: 求得当前num临近的两个 2的n次幂数; num向就近的次幂数靠拢,即决定了加还是减操作。
* @returns {boolean}
*/
function isAdd(num) {
//求得num底数
let baseNum = Math.log(num) / Math.log(2)
let distanceCeil = Math.abs(Math.pow(2, Math.ceil(baseNum)) - num);
let distanceFloor = Math.abs(Math.pow(2, Math.floor(baseNum)) - num);
console.log(num,distanceCeil,distanceFloor,Math.ceil(baseNum), Math.floor(baseNum))
return distanceCeil < distanceFloor
}
总结
每天记录一点,从小小菜鸟变小菜鸟!!!