剑指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("");
}
posted @ 2020-04-17 19:43  木子呆头  阅读(137)  评论(0编辑  收藏  举报