LeetCode 第23天

滑动窗口的平均值

给定一个整数数据流和一个窗口大小,根据该滑动窗口的大小,计算滑动窗口里所有数字的平均值。
实现 MovingAverage 类:

  1. MovingAverage(int size) 用窗口大小 size 初始化对象。
  2. double next(int val) 成员函数,next 每次调用的时候都会往滑动窗口增加一个整数,

请计算并返回数据流中最后 size 个值的移动平均值,即滑动窗口里所有数字的平均值。

分析:

使用栈来实现。当调用next时,先stack.push(val),如果stack的长度小于size,则最后求平均值时要除以stack.size()。

class MovingAverage {
    int size;
    Stack<Integer> stack;
    /** Initialize your data structure here. */
    public MovingAverage(int size) {
        this.size = size;
        stack = new Stack<>();
    }

    public double next(int val) {
        stack.push(val);
        int sum = 0;
        if (stack.size() < size){
            int len = stack.size();
            while (len > 0){
                sum += stack.get(len-1);
                len--;
            }
            return (double)sum/stack.size();
        }else {
            int len = size;
            while (len > 0){
                sum += stack.get(stack.size()-len);
                len--;
            }
            return (double)sum/size;
        }
    }
}

前n个数字二进制中1的个数

给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

分析:

  1. 使用API来实现,Integer.bitCount()
class Solution {
    public int[] countBits(int n) {
        int[] res = new int[n+1];
        for (int i = 0; i <= n; i++) {
            res[i] = Integer.bitCount(i);
        }
        return res;
    }
}
  1. 动态规划+位运算
    对于所有的数字,只有奇数和偶数两种:
  • 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
  • 偶数:二进制表示中,偶数中 1 的个数一定和除以 2之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。

则状态转移方程:dp[i]=dp[i-1]+1,当i为奇数; dp[i]=dp[i/2]。
可以合并为dp[i] = dp[i/2] + i%2,其中i/2通过i>>1得到;i%2通过i&1得到。


class Solution {
public:
    vector<int> countBits(int n) 
    {
        vector<int> res(n + 1, 0);
        for (int i = 0; i <= n; i++)
        {
            res[i] = res[i >> 1] + (i & 1);
        }
        return res;
    }
};

只出现一次的数字

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

分析:

可以用哈希表,但没有使用。先把数组排序,然后从0开始遍历数组,如果当前位置i的值与i+2位置的值相等,则i += 3,否则直接返回i的值。要注意当数字出现在最后一个位置时的情况。

class Solution {
    public int singleNumber(int[] nums) {
        Arrays.sort(nums);
        int i = 0;
        while (i+2 < nums.length){
            if (nums[i] == nums[i+2]) i += 3;
            else break;
        }
        return nums[i];

    }
}

单词长度的最大乘积

给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。

分析:

  • 暴力解法,双遍历,一一比较。
public int maxProduct(String[] words) {
        int ans = 0;
        for (int i = 0; i < words.length; i++) {
            String word1 = words[i];
            for (int j = i + 1; j < words.length; j++) {
                String word2 = words[j];
                if (!hasSameChar(word1, word2)) {
                    ans = Math.max(ans, word1.length() * word2.length());
                }
            }
        }
        return ans;
    }
private boolean hasSameChar(String word1, String word2) {
	for (char c : word1.toCharArray()) {
    	if (word2.indexOf(c) != -1) return true;
		}	
	return false;
	}
  • 对上述的hasSameChar进行改进优化时间复杂度,因为全是小写字母,所以创建一个长度为26的数组,记录words[i]中出现的字母,当words[j]中的字符在数组的值为1时代表这一字符已经在words[i]中出现过了。
    private boolean hasSameChar(String word1, String word2) {
        int[] count = new int[26];
        for (char c : word1.toCharArray()) count[c - 'a'] = 1;
        for (char c : word2.toCharArray()) {
            if (count[c - 'a'] == 1) return true;
        }
        return false;
    }
  • 再次对hasSameChar进行改进优化时间复杂度,可以把数组改为一个26位的数字,一个int类型的数字,其前26位,如果为1则表示该字符出现过,比如dab=000…01011,使用两个数记录两个字符串中出现过的字符,然后利用与运算判断是否有重复。
    private boolean hasSameChar(String word1, String word2) {
        int bitMask1 = 0, bitMask2 = 0;
        for (char c : word1.toCharArray()) bitMask1 |= (1 << (c - 'a'));
        for (char c : word2.toCharArray()) bitMask2 |= (1 << (c - 'a'));
        return (bitMask1 & bitMask2) != 0;
    }
  • 上述还是要对两个字符串分别比较,可以对words进行预处理,预先计算出每个word的数字,再一一比较,这将大大降低时间复杂度。
// 位运算 + 预计算
// 时间复杂度:O((m + n)* n)
// 空间复杂度:O(n)
public int maxProduct(String[] words) {
    // O(mn)
    int n = words.length;
    int[] masks = new int[n];
    for (int i = 0; i < n; i++) {
        int bitMask = 0;
        for (char c : words[i].toCharArray()) {
            bitMask |= (1 << (c - 'a'));
        }
        masks[i] = bitMask;
    }

    // O(n^2)
    int ans = 0;
    for (int i = 0; i < words.length; i++) {
        String word1 = words[i];
        for (int j = i + 1; j < words.length; j++) {
            String word2 = words[j];
            if ((masks[i] & masks[j]) == 0) {
                ans = Math.max(ans, word1.length() * word2.length());
            }
        }
    }
    return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值