力扣剑指Offer 第23天 数学(简单)剑指 Offer 39. 数组中出现次数超过一半的数字 剑指 Offer 66. 构建乘积数组

力扣剑指Offer 第23天 数学(简单)剑指 Offer 39. 数组中出现次数超过一半的数字 剑指 Offer 66. 构建乘积数组

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

题目

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

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

示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
限制:

1 <= 数组长度 <= 50000

题解

摩尔投票法

如果当前数nums[i]不等于候选值candidatecnt--相等则cnt++

cnt==0则切换候选值candidate=nums[i]

直到遍历结束输出最终候选值candidate

class Solution {
    public int majorityElement(int[] nums) {
        int candidate=nums[0],cnt=1;
        for(int i=1;i<nums.length;i++){
            if((nums[i]==candidate?++cnt:--cnt)==0){
                candidate=nums[i];
                cnt++;
            }
        }
        return candidate;
    }
}
排序

排序后的数组中,最中间的数值一定是多数值

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

使用HashMap记录对应数数量,若某个数数量达到nums.length/2(向上取整)

  • 向上取整用Math.ceil(double a)
  • 向下取整用Math.floor(double a)
class Solution {
    public int majorityElement(int[] nums) {
        Map<Integer,Integer> map=new HashMap<>(64);
        int half=nums.length/2+1;
        for(int i:nums){
            Integer cnt=map.get(i);
            if(cnt==null)cnt=0;
            map.put(i,++cnt);
            if(cnt==half)return i;
        }
        return 0;
    }
}

剑指 Offer 66. 构建乘积数组

题目

给定一个数组 A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中 B[i] 的值是数组A 中除了下标i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:
输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
提示:

所有元素乘积之和不会溢出 32 位整数

a.length <= 100000

题解

数学拆解
0123
B[0]-A[1]A[2]A[3]
B[1]A[0]-A[2]A[3]
B[2]A[0]A[1]-A[3]
B[3]A[0]A[1]A[2]-

通过上表可以将问题拆分为求A[i]左边元素的积与A[i]右边元素的积

  • 从上到下,左下方三角形中,计算B[1]~B[3]的乘积
  • 从下到上,右上角三角形中,计算B[2]~B[0]的乘积
  • 技巧:基于上一个B值计算下一个B值
  • 最后将左右的积相乘即可得到需要的答案
class Solution {
    public int[] constructArr(int[] a) {
        if(a.length==0)return a;
        int[] b=new int[a.length],c=new int[a.length];
        b[0]=1;
        for(int i=1;i<a.length;i++)
            b[i]=b[i-1]*a[i-1];
        c[a.length-1]=1;
        for(int i=a.length-2;i>=0;i--)
            c[i]=c[i+1]*a[i+1];
        for(int i=0;i<b.length;i++)
            b[i]*=c[i];
        return b;
    }
}
线段树
						[1,7]
					  /	      \
				[1,4]		   [5,7]
				/	\			/	\
			[1,2]	[3,4]	[5,6]	[7,7]

使用线段树维护各个A区间的乘积

B[i]=tree(0,i-1)*tree(i+1,A.length-1)

class Solution {
    private int[] tree;
    private int[] a;
    private static final int ROOT = 1;
    private void build(int l,int r,int p){
        if(l==r){
            tree[p]=a[l];
            return;
        }
        int m=l+r>>1;
        build(l,m,p<<1);
        build(m+1,r,p<<1|1);
        tree[p]=tree[p<<1]*tree[p<<1|1];
    }
    private int query(int l,int r,int p,int ql,int qr){
        if(ql>r||qr<l)return 1;
        if(ql<=l&&qr>=r)return tree[p];
        int m=l+r>>1;
        return query(l,m,p<<1,ql,qr)*query(m+1,r,p<<1|1,ql,qr);
    }
    public int[] constructArr(int[] a) {
        if(a.length==0)return a;
        tree = new int[a.length*4];
        this.a=a;
        build(0,a.length-1,ROOT);
        for(int i=0;i<a.length;i++){
            a[i]=query(0,a.length-1,ROOT,0,i-1)*query(0,a.length-1,ROOT,i+1,a.length-1);
        }
        return a;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值