剑指offer - 时间效率
1.数组中出现次数超过一半的数字
问题描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为 9 的数组[1,2,3,2,2,2,5,4,2]。由于数字 2 在数组中出现了 5 次,超过数组长度的一半,因此输出 2。如果不存在则输出 0。
function MoreThanHalfNum_Solution(numbers) {
// write code here
var len = numbers.length;
var obj = {};
numbers.forEach((item) => {
if (!obj[item]) {
obj[item] = 1;
} else {
obj[item]++;
}
});
for (let key in obj) {
if (obj[key] > len / 2) {
return key;
}
}
return 0;
}
2.最小的 K 个数
问题描述:
输入 n 个整数,找出其中最小的 K 个数。例如输入 4,5,1,6,2,7,3,8 这 8 个数字,则最小的 4 个数字是 1,2,3,4,。
方法一:快排
function GetLeastNumbers_Solution(input, k) {
// write code here
if (k > input.length) return [];
quickSort(input);
return input.slice(0, k);
}
function quickSort(input, left = 0, right = input.length - 1) {
if (left >= right) return;
var baseId = left;
var baseVal = input[baseId];
var i = left;
var j = right;
while (i < j) {
while (j > i && input[j] >= baseVal) {
j--;
}
while (i < j && input[i] <= baseVal) {
i++;
}
[input[i], input[j]] = [input[j], input[i]];
}
[input[baseId], input[j]] = [input[j], input[baseId]];
quickSort(input, left, j - 1);
quickSort(input, j + 1, right);
return input;
}
方法二:冒泡排序
function GetLeastNumbers_Solution(input, k) {
// write code here
if (k > input.length) return [];
for (let j = input.length - 1; j >= input.length - k; j--) {
for (let i = 0; i < j; i++) {
if (input[i] < input[i + 1]) {
[input[i], input[i + 1]] = [input[i + 1], input[i]];
}
}
}
var res = [];
while (k > 0) {
res.push(input.pop());
k--;
}
return res;
}
方法三:sort 排序
function GetLeastNumbers_Solution(input, k) {
// write code here
if (input.length < k) {
return [];
}
input.sort((a, b) => a - b);
return input.slice(0, k);
}
3.连续子数组的最大和
问题描述:
HZ 偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为 8(从第 0 个开始,到第 3 个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是 1)
/*
举例:array = [1, -2, 3, 10, -4, 7, 2, -5]
初始化 max = 1, temp = 1
i = 1时, temp = -1, max = 1
i = 2时, temp = 3, max = 3
i = 3时, temp = 13, max = 13
i = 4时, temp = 9 , max = 13
i = 5时, temp = 16, max = 16
i = 6时, temp = 18, max = 18
i = 7时, temp = 13, max = 18;
*/
function FindGreatestSumOfSubArray(array) {
// write code here
var max = array[0],
sum = array[0];
for (let i = 1; i < array.length; i++) {
if (sum <= 0) {
sum = array[i]; //sum是负数说明前面的数没有贡献,重新赋值一个数
} else {
sum += array[i]; //sum不是负数说明当前值有贡献
}
if (sum > max) {
max = sum;
}
}
return max;
}
4.整数中 1 出现的次数(从 1 到 n 整数中 1 出现的次数)
题目描述:
求出 1~13 的整数中 1 出现的次数,并算出 100~1300 的整数中 1 出现的次数?为此他特别数了一下 1~13 中包含 1 的数字有 1、10、11、12、13 因此共出现 6 次,但是对于后面问题他就没辙了。ACMer 希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中 1 出现的次数(从 1 到 n 中 1 出现的次数)。
方法一:对于每个数来说求个位只需对 10 取余(%10),然后将原数除以 10 取整,再对 10 取余拿到十位...依次类推
function NumberOf1Between1AndN_Solution(n) {
// write code here
var count = 0;
for (let i = n; i > 0; i--) {
for (let j = i; j > 0; j = parseInt(j / 10)) {
//对j/10取整
if (j % 10 === 1) {
count++;
}
}
}
return count;
}
方法二:笨方法,将 1 到 n 转换成字符串拼接起来,计算 1 的个数
function NumberOf1Between1AndN_Solution(n) {
// write code here
var str = "";
for (let i = 0; i <= n; i++) {
str += i;
}
var res = str.split("").filter((ele) => ele === "1");
return res.length;
}
5.把整数排成最小的数
问题描述:
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为 321323。
解题思路:
将数组中的数字连接起来,排成一个最小的数字。将'大数'往后放'小数'往前放,如何定义'大数'和'小数'?比如说有两个数 a 和 b,如果 ab>ba 则 a 是'大数'b 是'小数',要排成 ba。
于是,这道题目变成了一个排序问题,将能把组合出来的数字变大的数字往后排。我们这里需要自己定义一个比大小的比较方法。用冒泡排序,可以解决此题。
function PrintMinNumber(numbers) {
// write code here
numbers.sort((a, b) => {
return "" + a + b > "" + b + a ? 1 : -1; // ab 和 ba ASCII码值大的排在后面
// return [a,b].join("")-[b,a].join("");
});
return numbers.join("");
}