JS算法之数组中出现次数超过一半的数字及最小的k个数

437 阅读2分钟

这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战

数组中出现次数超过一半的数字

剑指Offer 39.数组中出现次数超过一半的数字

难度:简单

题目:leetcode-cn.com/problems/sh…

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制:1 <= 数组长度 <= 50000

题解

由本题可知,数字出现的次数超过数组长度的一半(这里简称“众数”)可得出关键三个解法:

  • 哈希表法:遍历数组nums,用Map统计出数字的数量,即可找出众数,时间空间复杂度均为O(N)。
  • 数组排序法:将数组nums排序,数组中点一定为众数。
  • 摩尔投票法:核心理念为票数正负抵消,时间复杂度为O(N),空间复杂度为O(1)。

法一 哈希表法

/**
 * @param {number[]} nums
 * @return {number}
 */
var majorityElement = function(nums) {
	var map = new Map();
  for(let i = 0;i < nums.length;i++){
    // 将数组元素和出现次数都存进map中
    if(map[nums[i]] === undefined){
    	map[nums[i]] = 1;   
    }else{
      map[nums[i]]++;
    }
  }
  // 遍历map中对应个数超过nums长度一半都元素
  for(let item in map){
    if(map[item] >= Math.ceil(nums.length/2)){
      return item;
    }
  }
};

法二 数组排序法

var majorityElement = function(nums) {
  let num = nums.sort();
  return num[Math.floor(num.length/2)];// 四舍,避免只输入一个数字时,五入是undefined
}

法三 摩尔投票法

原理类似于同归于尽,众数与非众数间一一抵消,最终留下的将会是众数(votes > 0)。

var majorityElement = function(nums) {
  let x = 0, votes = 0; // x为众数,votes为合计数
  for(let i = 0;i < nums.length;i++){
    if(!votes){ // 若votes为0,则将众数设为当前下标值
      x = nums[i];
      votes++;
    }else{ // 下标值是否为众数,是则加1,否则减1
      votes += nums[i] === x ? 1 : -1;
    }
  }
  return x;
}

最小的k个数

剑指Offer 40.最小的k个数

难度:简单

题目:leetcode-cn.com/problems/zu…

输入整数数组arr,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4.

示例1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:

  • 0 <= k <= arr.length <= 10000
  • 0 <= arr[i] <= 10000

题解

法一 直接调api

/**
 * @param {number[]} arr
 * @param {number} k
 * @return {number[]}
 */
var getLeastNumbers = function(arr, k){
  // sort使用快排
  return arr.sort((a, b) => a - b).slice(0, k);
}
  • 时间复杂度:O(NlogN)
  • 空间复杂度:O(logN)

法二 快排数组划分

由题目知,我们只需要找出TopK,因此相对于法一,我们不需要对全部元素进行排序,而且对k个元素的顺序也没有要求,所以对快排进行优化,使其不用排序整个数组。

步骤:

  1. 哨兵划分
    • 通过一趟排序,对数组进行划分,基准数为arr[i],左数组区间为[ll ,i1i-1],右数组区间为[i+1i+1rr]。
  2. 递归或返回:(k+1代表的是前k小后的那一个数字)
    • k<ik < i,代表第k+1k+1小的数字在左子数组中,则递归左子数组
    • k>ik > i,代表第k+1k+1小的数字在右子数组中,则递归右子数组
    • k=ik = i,代表arr[k]为第k+1k+1小的数字,则返回数组前k个数字即可
var getLeastNumbers = function(arr, k){
  if(k >= arr.length) return arr;
  return quickSort(arr, k, 0, arr.length - 1);
}
function quickSort(arr, k, l, r){
    let i = l, j = r;
    while(i < j){
      while(i < j && arr[j] >= arr[l]) j--;
      while(i < j && arr[i] <= arr[l]) i++;
      [arr[i],arr[j]] = [arr[j],arr[i]];
    }
    [arr[i],arr[l]] = [arr[l],arr[i]];
    if(i > k) return quickSort(arr, k, l, i - 1);
    if(i < k) return quickSort(arr, k, i + 1, r);
    return arr.slice(0, k);
  }
  • 时间复杂度:O(N)
  • 空间复杂度:O(logN)

坚持每日一练!前端小萌新一枚,希望能点个哇~