【精选】JAVA算法题(二十一)

一、丑数

题目:

/**
 * 编写一个程序判断给定的数是否为丑数。
 * 丑数就是只包含质因数 2, 3, 5 的正整数。
 *
 * 示例 1:
 * 输入: 6
 * 输出: true
 * 解释: 6 = 2 × 3
 *
 * 示例 2:
 * 输入: 8
 * 输出: true
 * 解释: 8 = 2 × 2 × 2
 *
 * 示例 3:
 * 输入: 14
 * 输出: false
 * 解释: 14 不是丑数,因为它包含了另外一个质因数 7。
 *
 * 说明:
 *     1 是丑数。
 *     输入不会超过 32 位有符号整数的范围: [−2^31,  2^31 − 1]。
 */

可以直接根据丑数的判定规则进行判断,只包含2,3,5这三个质因数,那我就把这三个数除尽看看剩不剩其他的数就好了。

    public static boolean method1(int num) {
        if (num<1) return false;
        while (num%5==0){
            num/=5;
        }
        while (num%3==0){
            num/=3;
        }
        while (num%2==0){
            num>>=1;
        }
        return num == 1;
    }

二、消失的数

题目:

/**
 * 给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。
 *
 * 示例 1:
 * 输入: [3,0,1]
 * 输出: 2
 *
 * 示例 2:
 * 输入: [9,6,4,2,3,5,7,0,1]
 * 输出: 8
 *
 * 说明:
 * 你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?
 */

一种简单的解决方法,把数组排序,然后遍历判断找出

    public static int method1(int[] nums) {
        Arrays.sort(nums);
        int i=0;
        for (;i<nums.length;i++){
            if (nums[i]!=i){
                return i;
            }
        }
        return i+1;
    }

你还可以借助位运算,一个数异或另一个数两次等于它本身,一个数异或它自己等于0,0异或任何数等于任何数

    public static int method2(int[] nums) {
        int res = nums.length;
        for (int i = 0; i < nums.length; ++i){
            res ^= nums[i];
            res ^= i;
        }
        return res;
    }

三、移动零

题目:

/**
 * 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
 *
 * 示例:
 * 输入: [0,1,0,3,12]
 * 输出: [1,3,12,0,0]
 *
 * 说明:
 * 必须在原数组上操作,不能拷贝额外的数组。
 * 尽量减少操作次数。
 */

第一个想到的就是快慢指针,把快指针扫描到的非0的数字赋值到慢指针的位置,快指针走完就把从慢指针当前所在位置到数组尾全部赋值为0。

    public static void method1(int[] nums) {
        int fast = 0, slow = 0, count = 0;
        for (; fast < nums.length; fast++) {
            if (nums[fast] != 0) {
                count++;
                if (fast != slow) {
                    nums[slow] = nums[fast];
                }
                slow++;
            }
        }
        for (int i = count; i < nums.length; i++) {
            nums[i] = 0;
        }
    }

换一种写法就是当快指针扫描到的非0的数字就和慢指针为0的数字交换,我在这里还加了一个判断,快慢指针位置不相等,是因为我交换的操作没有借助第三个变量,因为其中一个数已知,只需要把快指针的数赋值到慢指针上,然后慢指针的数赋值为0就可以了。

    public static void method2(int[] nums) {
        int fast = 0, slow = 0;
        for (; fast < nums.length; fast++) {
            if (nums[fast] != 0) {
                if (fast != slow) {
                    nums[slow] = nums[fast];
                    nums[fast]=0;
                }
                slow++;
            }
        }
    }

四、寻找中位数

题目:

/**
 * 中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
 *
 * 例如,
 * [2,3,4] 的中位数是 3
 * [2,3] 的中位数是 (2 + 3) / 2 = 2.5
 *
 * 设计一个支持以下两种操作的数据结构:
 *     void addNum(int num) - 从数据流中添加一个整数到数据结构中。
 *     double findMedian() - 返回目前所有元素的中位数。
 *
 * 示例:
 * addNum(1)
 * addNum(2)
 * findMedian() -> 1.5
 * addNum(3)
 * findMedian() -> 2
 *
 * 进阶:
 *     如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
 *     如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
 */

你可以在插入时做文章把它在插入时就整理成有序数组,使用二分查找插入,然后找中位数时就分两种情况,一种是长度为奇数一种是长度为偶数,也可以在寻找中位数时做文章,可以使用Arrays.sort来排序数组。

        List<Integer> list;

        public MedianFinder() {
            list = new ArrayList();
        }

        public void addNum(int num) {
            int left = 0;
            int right = list.size() - 1;
            while (left < right) {
                int mid = left + (right - left) / 2;
                int val = list.get(mid);
                if (val < num) left = mid + 1;
                else right = mid;
            }
            if (list.size() > 0 && num > list.get(list.size() - 1))
                list.add(num);
            else
                list.add(left, num);
        }

        public double findMedian() {
            int len = list.size();
            if (len % 2 == 1)
                return (double) list.get(len / 2);
            else
                return list.get(len / 2) / 2.0
                     + list.get(len / 2 - 1) / 2.0;
        }

还有一种解法就是题目中提到的进阶,假设所有的数都在0-100范围内,那你就可以定义一个长度为101的数组,存储所有数字出现的次数,定义一个count变量,在添加数字的时候对其加一,实现计数,这样就实现了排序和计数,在找中位数时根据总数量进行判断更改。假设所有的数99%出现在0-100内呢,你就要多定义两个长度来保存小于0的数出现的次数和大于100的数出现的次数,再找中位数时要注意下标的变化。

    private int[] nums=new int[103];
    private int count=0;
    public MedianFinder() {

    }

    public void addNum(int num) {
        if (num>100){
            nums[102]=nums[102]+1;
        }else if (num<0){
            nums[0]=nums[0]+1;
        } else {
            nums[num+1]=nums[num+1]+1;
        }
        count++;
    }

    public double findMedian() {
        int sum=0;
        if (count%2==0){
            int index=count/2;
            for (int i=0;i<103;i++){
                sum+=nums[i];
                if (sum==index){
                    return (i+i-1)/2.0;
                }else if (sum>index){
                    return i-1;
                }
            }
        }else {
            int index=count/2+1;
            for (int i=0;i<103;i++){
                sum+=nums[i];
                if (sum>=index){
                    return i-1;
                }
            }
        }
        return 0;
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幽蓝丶流月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值