leetcode面试经典题5——169.多数元素(多解法)

169. 多数元素

题目描述

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

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

示例 1:
输入:nums = [3,2,3]
输出:3

示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:
n == nums.length
1 <= n <= 5 * 10^4
-10^9 <= nums[i] <= 10^9

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

哈希表解法

使用哈希表来记录数组中元素出现的次数。

哈希表的基本用法

首先,我们需要了解一下哈希表的基本用法。

import java.util.HashMap;
import java.util.Map;

public class HashTableDemo {
    public static void main(String[] args) {
        // 创建一个哈希表
        Map<String, Integer> map = new HashMap<>();

        // 添加一个键值对
        map.put("apple", 1);
        System.out.println("HashMap: " + map); //正确输出: HashMap: {apple=1}

        // 检查一下这个键是否在哈希表中
        boolean exists = map.containsKey("apple");
        System.out.println("Contains 'apple' key: " + exists); //正确输出: Contains 'apple' key: true

        // 获取这个键对应的值
        Integer value = map.get("apple");
        System.out.println("Value for 'apple': " + value); //正确输出: Value for 'apple': 1

        // 在哈希表中删掉这个键
        map.remove("apple");
        System.out.println("HashMap after remove: " + map); //正确输出: HashMap after remove: {}

        // 添加一些键值对
        map.put("banana", 2);
        map.put("cherry", 3);
        System.out.println("HashMap: " + map); //正确输出: HashMap: {banana=2, cherry=3}

        // 输出哈希表中的键值对
        for(Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
        }
        //正确输出: Key = banana, Value = 2
        //                 Key = cherry, Value = 3

        // 输出哈希表的键值对个数
        int size = map.size();
        System.out.println("Size of HashMap: " + size); //正确输出: Size of HashMap: 2
    }
}

用到的方法:
创建

Map<,> map = new Hash<>();

检查是否包含

boolean con = map.containsKey(key);

插入更新键值对

map.put(key, value);

哈希解法代码

class Solution {
    private Map<Integer, Integer> count(int[] nums) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for(int i = 0; i < nums.length; i++) {
            // 如果哈希表中未包含这个数字
            if(! map.containsKey(nums[i])) {
                // 则添加这个数字,对应的次数为1
                map.put(nums[i], 1);
            } else {
                // 否则更新这个数字的次数加一
                map.put(nums[i], map.get(nums[i]) + 1);
            }
        }
        return map;
    }

    public int majorityElement(int[] nums) {
        Map<Integer, Integer> map = count(nums);
        for(Integer key : map.keySet()) {
            // 遍历哈希表,若次数大于n/2,就返回对应的数字
            if(map.get(key) > nums.length/2) return key;
        }
        // 找不到多数元素返回0
        return 0;
    }
}

排序

思路

排序解法的难点在于理解算法思路,而不是代码过程。
因为题目说多数元素的个数必大于n/2
那么将代码升序(或降序)排序之后,多数元素必定会在数组的中间点。

可以这样想象,如果你把数组按照升序或者降序排列,那么多数元素由于数量超过半数,就一定会占据数组中间的位置。无论它在前半部分的数量还是在后半部分的数量,由于它的数量超过了总数的一半,因此它肯定会延伸到数组的中间,也就是中位数的位置。
比如考虑一个由 7 个元素组成的数组 [2, 2, 2, 2, 5, 5, 5],其中出现最多的元素就是 2,数量为 4 > 7/2,排序后得到的数组是 [2, 2, 2, 2, 5, 5, 5],你可以看到元素 “2” 在数组的中间。
这就是为什么在排序后,多数元素会出现在数组的中间。因此,只需要返回排序后数组的中位数就好。在编程实现上,这通常意味着返回索引为 n/2 的元素(在 Java 中,数组的索引从 0 开始)。

代码

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

这里面用到了Arrays类的一个方法,即排序方法Arrays.sort(),参数是数组,默认升序排序。

分治法

思路

分治法的过程 : 先分解,后合并。
也就是将数组分成两半,两个子问题来处理,然后再将两个子问题得到的结果合并,得到大问题的答案。

对应在这个问题当中,可以得到
大问题:求整个数组的多数元素
子问题:将数组进行划分,左右两个子数组的多数元素
然后再依次进行划分直到子数组的长度为一时,则唯一的元素就是它的多数元素,进行返回,也就是递归条件

代码

基于这里,可以得到我们的递归函数中划分部分代码

private int Rec(int[] nums, int start, int end) {
        // 当数组只有一个元素时,结束递归
        if(start == end) return nums[start];

        // 算出中间点,进行划分
        int mid = (start + end) /2;

        // 得到左子数组的多数元素
        int left = Rec(nums, start, mid);

        // 得到右子数组的多数元素
        int right = Rec(nums, mid, end);

        // 合并过程
    }

加上合并即全部代码:

class Solution {
    // 计算子数组中多数元素出现的个数
    private int count(int[] nums, int start, int end, int num){
        int count = 0;
        for(int i = start; i <= end; i++) {
            if(nums[i] == num) count++;
        }
        return count;
    }
    private int Rec(int[] nums, int start, int end) {
        // 当数组只有一个元素时,结束递归
        if(start == end) return nums[start];

        // 算出中间点,进行划分
        int mid = (start + end) /2;
        // int mid = (end - start) / 2 + start;

        // 得到左子数组的多数元素
        int left = Rec(nums, start, mid);

        // 得到右子数组的多数元素
        // 注意这里起点时mid+1
        int right = Rec(nums, mid+1, end);

        // 合并过程
        // 若左右子数组的多数元素相同,则直接返回
        if(left == right) return left;
        // 否则算出左右多数元素在整个数组出现的次数,次数多的则是大数组的多数元素
        int leftcount = count(nums, start, end, left);
        int rightcount = count(nums, start, end, right);
        // if(leftcount > rightcount) return left;
        // else return right;
        return leftcount > rightcount ? left : right;
    }
    public int majorityElement(int[] nums) {
        return Rec(nums, 0, nums.length-1);
    }
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值