算法-数学运算技巧

(1)用算法高效寻找素数

在这里插入图片描述

class Solution {
    public int countPrimes(int n) {
        boolean[] isPrim=new boolean[n];
        Arrays.fill(isPrim,true);
        //优化1:只需要判断到sqrt(n)就能知道是否为质数
        for(int i=2;i*i<n;i++){
            if(isPrim[i]){
            	//优化2:从i*i开始遍历,避免重复遍历
                for(int j=i*i;j<n;j+=i){
                    isPrim[j]=false;
                }
            }
        }
        int count=0;
        for(int i=2;i<n;i++){
            if(isPrim[i]){
                count++;
            }
        }
        return count;
    }
}

(2)高效进行模幂运算

在这里插入图片描述

class Solution {
    int base=1337;
    public int superPow(int a, int[] b) {
        LinkedList<Integer> list=new LinkedList<>();
        for(int i:b){
            list.add(i);
        }
        return helper(a,list);
    }
    public int helper(int a,LinkedList<Integer> list){
        if(list.size()==0) return 1;
        int last=list.removeLast();
        int part1=mypow(a,last);
        int part2=mypow(helper(a,list),10);
        return (part1*part2)%base;
    }

    public int mypow(int a,int k){
        if(k==0) return 1;
        a=a%base;
        if(k%2==1){
            return( a*mypow(a,k-1))%base;
        }else{
            int p=mypow(a,k/2);
            return (p*p)%base;
        }
    }
}

幂运算mypow采用快速幂运算:
在这里插入图片描述

取模是根据:
(ab)%k=((a%k)(b%k))%k

总的幂运算helper是采用递归:
在这里插入图片描述

(3)寻找缺失元素

在这里插入图片描述

class Solution {
    public int missingNumber(int[] nums) {
        int n=nums.length;
        //1.普通遍历方法
        // if(nums[0]!=0) return 0;
        // if(nums[n-1]!=n) return n;
        // int res=-1;
        // for(int i=0;i<n-1;i++){
        //     if(nums[i+1]-nums[i]>1){
        //         res=i+1;
        //     }
        // }
        // return res;
        //2.等差数列求和
        int sum_expect=(0+n)*(n+1)/2;
        int sum=0;
        for(int i:nums){
            sum+=i;
        }
        return sum_expect-sum;
    }
}

(4)高效寻找缺失和重复的数字

在这里插入图片描述
1.暴力方法求解:

class Solution {
    public int[] findErrorNums(int[] nums) {
        int n=nums.length;
        int dup=-1,missing=-1;
        for(int i=1;i<=nums.length;i++){
            int count=0;
            for(int j=0;j<nums.length;j++){
                if(nums[j]==i){
                    count++;
                }
            }
            if(count==2){
                dup=i;
            }
            if(count==0){
                missing=i;
            }
            //优化:若找到,提前结束,不用遍历全部
            if (dup > 0 && missing > 0)
                break;
        }
        return new int[]{dup,missing};
        
    }
}

2.利用排序

class Solution {
    public int[] findErrorNums(int[] nums) {
        int dup=-1,missing=1;
        Arrays.sort(nums);
        for(int i=1;i<nums.length;i++){
            if(nums[i]-nums[i-1]==0){
                dup=nums[i];
            }
            if(nums[i]-nums[i-1]>1){
                missing=nums[i-1]+1;
            }
        }
        return new int[]{dup,nums[nums.length - 1] != nums.length ? nums.length : missing};     
    }
}
//时间复杂度:O(n\log n)O(nlogn),排序需要 O(n\log n)O(nlogn) 的时间。

3.使用map

class Solution {
    public int[] findErrorNums(int[] nums) {
        int dup=-1,missing=1;
        Map<Integer,Integer> mapping=new HashMap<>();
        for(int i:nums){
            mapping.put(i,mapping.getOrDefault(i,0)+1);
        }
        for(int i=1;i<=nums.length;i++){
            if(mapping.containsKey(i)){
                if(mapping.get(i)>1){
                    dup=i;
                }
            }else{
                missing=i;
            }
        }
        return new int[]{dup,missing};   
    }
}

4.labuladong巧解:将访问过的位置变为负数

class Solution {
    public int[] findErrorNums(int[] nums) {
        int dup=-1;
        //思路:将数组nums中访问过的位置变为负数,那么当访问到某个值为负数时,这个就是重复元素 
        for(int i=0;i<nums.length;i++){
            int index=Math.abs(nums[i])-1;
            if(nums[index]<0){
                dup=Math.abs(nums[i]);
            }else{
                nums[index]*=-1;
            }
        }
        //大于0的位置则为缺失元素
        int missing=-1;
        for(int i=0;i<nums.length;i++){
            if(nums[i]>0){
                missing=i+1;
            }
        }
        return new int[]{dup,missing};
    }
}

(5)巧用异或

在这里插入图片描述

class Solution {
    public int[] singleNumbers(int[] nums) {
        if(nums.length < 2)
            return nums;
        //总体思路:使用分组异或
        int[] result=new int[2];

        //1.先求出所有元素异或结果,此时所得结果即为两个出现一次数字的异或结果
        int ret=nums[0];
        for(int i=1;i<nums.length;i++){
            ret^=nums[i];
        }
        //2.找到异或结果中最右边不为0的位置
        int bitIndex=0;
        for(int i=0;i<32;i++){
            if((ret>>i&1)==1){
                bitIndex=i;
                break;
            }
        }
        //3.根据找到的这个位置进行分组,分组之后异或的结果就是两个只出现一次的数字
        for(int j:nums){
            if((j>>bitIndex&1)==1){
                result[0]^=j;
            }else{
                result[1]^=j;
            }
        }
        return result;
    }
}

(6)在无限序列中随机抽取元素

A.取链表中随机一个节点的值
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    ListNode head;

    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    public Solution(ListNode head) {
        this.head=head;//定义构造函数
    }
    
    /** Returns a random node's value. */
    public int getRandom() {
        Random r=new Random();
        int i=0,res=-1;
        ListNode p=head;
        while(p!=null){
            //这样的话概率为1/i
            if(r.nextInt(++i)==0){
                res=p.val;
            }
            p=p.next;
        }
        return res;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(head);
 * int param_1 = obj.getRandom();
 */

B.随机取一个数在数组中的索引
在这里插入图片描述

class Solution {
    int[] nums;
    public Solution(int[] nums) {
        this.nums=nums;
    }
    
    public int pick(int target) {
        Random r=new Random();
        //1.暴力法,如果数组很大则需要占用很多空间
        // List<Integer> index=new LinkedList();
        // for(int i=0;i<nums.length;i++){
        //     if(nums[i]==target){
        //         index.add(i);
        //     }
        // }
        // int sz=index.size();
        // return index.get(r.nextInt(sz));
        //2.蓄水池抽样
        int count=0,res=0;
        for(int i=0;i<nums.length;i++){
            //以1/count的概率进行抽样,无所谓总共有多少target,截止到当前这个target要保证抽样概率为1/count
            if(nums[i]==target){
                if(r.nextInt(++count)==0){
                    res=i;
                }
            }
        }
        return res;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(nums);
 * int param_1 = obj.pick(target);
 */

(7)高效对有序数组/链表去重

A.数组
在这里插入图片描述

class Solution {
    public int removeDuplicates(int[] nums) {
        //使用快慢指针方法,最后nums[0~slow]的元素无重复
        if(nums.length==0) return 0;
        int slow=0,fast=1;
        while(fast<nums.length){
            if(nums[fast]!=nums[slow]){
                slow++;
                nums[slow]=nums[fast];
            }
            fast++;
        }
        return slow+1;
    }
}

B.链表
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null) return null;
        ListNode slow=head,fast=head.next;
        while(fast!=null){
            if(fast.val!=slow.val){
                slow.next=fast;
                slow=slow.next;
            }
            fast=fast.next;
        }
        slow.next=null;
        return head;
    }
}

(8)智力题

A.
在这里插入图片描述

class Solution {
    public boolean canWinNim(int n) {
        // 如果上来就踩到 4 的倍数,那就认输吧
        // 否则,可以把对方控制在 4 的倍数,必胜
        return n%4!=0;
    }
}

B.
在这里插入图片描述

class Solution {
    public boolean stoneGame(int[] piles) {
        //由于只能在左右两端拿,所以可以在开始时候选奇数堆(1)还是偶数堆(piles.length),
        //通过观察看奇数堆总石子数多还是偶数堆多,就选哪个
        //所以可以总是赢
        return true;
    }
}

C.
在这里插入图片描述

class Solution {
    public int bulbSwitch(int n) {
        //平方数被按了奇数次,所以最后亮着
        //例如:16=1*16=2*8=4*4,所以第16盏灯共被按了5次(1,2,4,8,16)
        return (int)Math.sqrt(n);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值