华为研发工程师编程题——明明的随机数
题目
[编程题]明明的随机数——来自牛客的华为研发工程师编程题,难度:🌟🌟🌟🌟
对于明明生成的 n 个 1 到 500 之间的随机整数,你需要帮助他完成以下任务:
∙删去重复的数字,即相同的数字只保留一个,把其余相同的数去掉;
∙然后再把这些数从小到大排序,按照排好的顺序输出。
你只需要输出最终的排序结果。
输入描述:
第一行输入一个整数 n (1≦n≦1000),代表明明生成的数字个数。
此后 n 行,第 i 行输入一个整数 ai (1≦ai ≦500),代表明明生成的随机整数。
输出描述:
输出若干行,每行输出一个整数,代表输入数据排序后的结果。第一行输出最小的数字。
示例1
输入
3
2
2
1
输出
1
2
解题
数组+插入排序
由于一开始没想起插入排序最好用链表来做,所以先写了数组版,思路如下:
- 读取输入的第一行,获得随机整数的总数
- 新建一个空数组nums,用来收集各输入数,存入nums的元素要遵循递增(即从小到大、不允许重复)的规则
- ACM输入的第一个整数存入nums第一位,后面的输入数逐个插入nums或被去重,直到处理完所有输入数。每次处理输入数都遵循以下处理流程:
- 处理流程第一步,读取下一个输入数作为新的待插入元素newNum,选取nums最后一个元素作为新的被比较元素compareNum,比较newNum与compareNum的大小
- 若
newNum = compareNum
,说明newNum是重复的数字,不必保留。回到处理流程第一步 - 若
newNum > compareNum
,则newNum大于compareNum及其左边的所有数,应把newNum插入nums中当前compareNum的后一位(nums.splice(compareNum的index + 1, 0, newNum)
)。回到处理流程第一步 - 若
newNum < compareNum
,且compareNum是nums中的第一位,说明newNum比nums中所有元素都要小,应把newNum插入nums的第一位(nums.unshift(newNum)
)。回到处理流程第一步 - 若
newNum < compareNum
,且compareNum不是nums中的第一位,说明newNum如果要插入的话,它的索引会比compareNum的索引更小,但还不知道具体是多少。应选取当前compareNum的前一位作为新的compareNum。回到处理流程第二步
- 处理完所有输入数后,nums就是一个包含所有输入数的数值且按递增顺序排列的数组了,现在遍历nums并打印出来。
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const inputLines = [];
rl.on("line", function (line: string) {
inputLines.push(line.trim());
});
rl.on("close", function () {
const numsLength = parseInt(inputLines[0]);
// 数组nums是递增序列
const nums = [];
nums.push(parseInt(inputLines[1]));
// 遍历输入数
// 若新输入数按顺序插入了nums,或与已有元素重合了,则处理下一个输入数
for (let newNumIdx = 2; newNumIdx <= numsLength; newNumIdx++) {
const newNum = parseInt(inputLines[newNumIdx]);
// 遍历nums
let compareNumIdx = nums.length - 1;
while (compareNumIdx >= 0) {
const compareNum = nums[compareNumIdx];
if (newNum === compareNum) {
// 退出nums遍历,去读取下一个输入数
break;
} else if (newNum > compareNum) {
// 插入新元素
nums.splice(compareNumIdx + 1, 0, newNum);
// 退出nums遍历,去读取下一个输入数
break;
} else {
if (compareNumIdx === 0) {
// 在队首插入新元素
nums.unshift(newNum);
// 退出nums遍历,去读取下一个输入数
break;
} else {
// 选取原被比较数的前一位作为新被比较数
compareNumIdx--;
}
}
}
}
nums.forEach((num) => {
console.log(num);
});
});
遇到的问题:
- ACM输入问题一定要记得把输入字符串中的数字部份转为整数类型,否则把数字字符串当数字使用会出问题。比如1000>2是对的,但’1000’>'2’是错的,如果按从小到大排,程序会把’1000’排在’2’的前面。
- 流程比较复杂,主要是要熟悉插入排序的实现。
- 各循环的灵活度排名:while > for > forEach/map。
如果想从数组的非开头元素开始遍历,或在非结尾元素结束遍历,或希望每次跳过一个元素,则for比forEach更方便控制。若循环中不同条件下索引可能增加n也可能减少n(最好别这样),则while比for更方便控制。
视情况选择用哪种,一般用for循环。
链表+插入排序
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const inputLines = [];
class LineNode {
val?: number; // default:public
next: LineNode | null = null;
last: LineNode | null = null;
constructor(val?: number) {
this.val = val;
this.next = null;
this.last = null;
}
}
rl.on("line", function (line: string) {
inputLines.push(line.trim());
});
rl.on("close", function () {
const numsLength = parseInt(inputLines[0]);
// 递增序列
const lineHeadNode = new LineNode();
// 遍历输入数
// 若新输入数按顺序插入了nums,或与已有元素重合了,则处理下一个输入数
for (let newNumIdx = 2; newNumIdx <= numsLength; newNumIdx++) {
const newNum = parseInt(inputLines[newNumIdx]);
// 遍历nums
let compareNumIdx = nums.length - 1;
while (lineHeadNode.last.) {
const compareNum = nums[compareNumIdx];
if (newNum === compareNum) {
// 退出nums遍历,去读取下一个输入数
break;
} else if (newNum > compareNum) {
// 插入新元素
nums.splice(compareNumIdx + 1, 0, newNum);
// 退出nums遍历,去读取下一个输入数
break;
} else {
if (compareNumIdx === 0) {
// 在队首插入新元素
nums.unshift(newNum);
// 退出nums遍历,去读取下一个输入数
break;
} else {
// 选取原被比较数的前一位作为新被比较数
compareNumIdx--;
}
}
}
}
nums.forEach((num) => {
console.log(num);
});
});