Leetcode(8)——数学相关问题

格式:

题号+题名+简单思路+code



T204: 计数质数

  • 埃拉托色尼筛选法
  • 若一个数是质数,则其从2开始的倍数应该是合数
  • 范围可以缩小到 i 2 i^2 i2
  • O(nloglogn)时间复杂度
  • ( n ∗ ∑ P r i m e N u m b e r 1 2 + 1 3 + 1 5 + 1 7 + . . . + 1 m a x P r i m e n*\sum_{PrimeNumber}{\frac{1}{2}+\frac{1}{3}+\frac{1}{5}+\frac{1}{7}+...+\frac{1}{maxPrime}} nPrimeNumber21+31+51+71+...+maxPrime1)
func countPrimes(n int) int {
    isPrime:=make([]bool,n)
    for i:=0;i<len(isPrime);i++ {
        isPrime[i]=true
    }
    for i:=2;i*i<n;i++ {
        if isPrime[i] {
            for j:=i*i;j<n;j+=i {
                isPrime[j]=false
            }
        }
    }
    count:=0
    for i:=2;i<n;i++ {
        if isPrime[i] {
            count++
        }
    }
    return count
}




T535: TinyURL的加密

  • 随机数
  • 哈希表
  • 不受int范围影响,如用数字加字母来作为加密,有 ( 10 + 26 ∗ 2 ) m (10+26*2)^m (10+262)m

参考

public class Codec {
    String alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    HashMap<String, String> map = new HashMap<>();
    Random rand = new Random();
    String key = getRand();

    public String getRand() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            sb.append(alphabet.charAt(rand.nextInt(62)));
        }
        return sb.toString();
    }

    public String encode(String longUrl) {
        while (map.containsKey(key)) {
            key = getRand();
        }
        map.put(key, longUrl);
        return "http://tinyurl.com/" + key;
    }

    public String decode(String shortUrl) {
        return map.get(shortUrl.replace("http://tinyurl.com/", ""));
    }
}




T771: 宝石与石头

  • 考点主要在由于只使用了字母,所以可以用数组构造哈希表
  • 字母在ASCII中的跨度为 65 ∼ 122 65 \sim 122 65122
func numJewelsInStones(J string, S string) int {
    jewel:=make([]bool, 58)
    for i := range J {
        jewel[J[i]-'A']=true
    }
    count:=0
    for i := range S {
        if jewel[S[i]-'A'] {
            count++
        }
    }
    return count
}




T136: 只出现一次的数字

  • 要求O(N)时间复杂度;O(1)空间复杂度
  • 位运算
  • 异或运算的特点:① a ⊗ a = 0 a \otimes a=0 aa=0;② a ⊗ 0 = a a \otimes 0=a a0=a;③ ⊗ 运 算 满 足 交 换 律 和 结 合 律 \otimes 运算满足交换律和结合律
func singleNumber(nums []int) int {
    ans:=0
    for i:=0;i<len(nums);i++ {
        ans^=nums[i]
    }
    return ans
}




T137: 只出现一次的数字Ⅱ

  • 设置三个变量,分别表示二进制位上出现的次数;最后返回出现一次的变量
  • T136相当于有两个变量
func singleNumber(nums []int) int {
    var one, two, three int
    for i := range nums {
        two|=one & nums[i]
        one^=nums[i]
        three=two&one
        one&=^three
        two&=^three
    }
    return one
}




T260: 只出现一次的数字Ⅲ

  • 设这俩只出现一次的数为m, n;先通过一次遍历找出m^n
  • 此时m^n必然有一个位置,一个数在该位置为1,另一个数为0;设最后为1的位为二级制的第i位
  • 那么我们根据第i位为0/1将数组分成两组,这时相同的数也会分到同一组,此时变成T136
func singleNumber(nums []int) []int {
    tmp:=0
    for i:=range nums {
        tmp^=nums[i]
    }
    m,n:=0,0
    bit:=1
    for tmp&bit==0 {
        bit<<=1
    }
    for i:=range nums {
        if nums[i]&bit==0 {
            m^=nums[i]
        } else {
            n^=nums[i]
        }
    }
    return []int{m, n}
}




T7: 整数反转

  • 正向取余→反向乘十
class Solution:
    MAXINT=(1<<31)-1
    MININT_=1<<31
    def reverse(self, x: int) -> int:
        t=abs(x)
        ans=0
        while t!=0:
            i=t%10
            ans=ans*10+i
            t=t//10
        if ans>self.MAXINT:
            if x>0:
                return 0
            if x<0 and ans>self.MININT_:
                return 0
        return ans if x>0 else -ans




T371: 两整数之和

  • ①a^b求出所有不需要进位的1
  • ②a&b求出所有需要进位的1
  • 只要有需要进位的1,就再次进行循环①②
func getSum(a int, b int) int {
    x:=a^b
    y:=a&b
    for y!=0 {
        y<<=1
        tmp:=x
        x=tmp^y
        y=tmp&y
    }
    return x
}




T13: 罗马数字转整数

class Solution:
    dic={"I":1, "V":5, "X":10, "L":50, "C":100, "D":500, "M":1000, "IV":4, "IX":9, "XL":40, "XC":90, "CD":400, "CM":900}
    def romanToInt(self, s: str) -> int:
        ans=0
        i=0
        while i < len(s):
            if i+1 < len(s) and s[i:i+2] in self.dic:
                ans+=self.dic[s[i:i+2]]
                i+=1
            else:
                ans+=self.dic[s[i]]
            i+=1
        return ans




T12: 整数转罗马数字

  • 记忆化递归
dic={"I":1, "V":5, "X":10, "L":50, "C":100, "D":500, "M":1000, "IV":4, "IX":9, "XL":40, "XC":90, "CD":400, "CM":900}
r_dic={dic[key]:key for key in dic}
searchList=sorted(list(r_dic.keys()))

class Solution:
    memo={}
    def intToRoman(self, num: int) -> str:
        if num==0:
            return ""
        if num in self.memo:
            return self.memo[num]
        tmp=self.binarySearch(num)
        ans=r_dic[tmp]+self.intToRoman(num-tmp)
        self.memo[num]=ans
        return ans
    def binarySearch(self, num: int) -> int:    # 二分搜索找到不大于当前值的最大值
        lo=0
        hi=len(searchList)-1
        while lo<=hi:
            mid=(hi-lo)//2+lo
            if searchList[mid]>num:
                hi=mid-1
            elif searchList[mid]<num:
                lo=mid+1
            else:
                return searchList[mid]
        return searchList[lo-1]




T338: 比特位计数

  • 类似T12,都使用了DP的思想;O(Nlog2M)时间复杂度
  • f ( n ) = f ( n − 2 [ l o g 2 ( n ) ] ) + 1 f(n)=f(n-2^{[log_2(n)]})+1 f(n)=f(n2[log2(n)])+1
func countBits(num int) []int {
    memo:=make([]int, num+1)
    for i:=1;i<len(memo);i++ {
        tmp:=i
        margin:=1
        for tmp!=1 {
            tmp>>=1
            margin<<=1
        }
        memo[i]=1+memo[i-margin]
    }
    return memo
}




T9: 回文数

class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x<0:
            return False
        tmp=x
        ans=0
        while tmp!=0:
            ans=ans*10+tmp%10
            tmp=tmp//10
        return ans==x




T476: 数字的补数

观察规律可得补数与取反的差别

func findComplement(num int) int {
    tmp:=num
    c:=0
    for tmp!=0 {
        tmp>>=1
        c++
    }
    return 1<<c+(^num)
}




T78: 子集

  • 位运算,通过1的位置来表示是否选该元素
func subsets(nums []int) [][]int {
    ans:=[][]int{}
    i:=0
    for i < 1<<len(nums) {
        tmp:=i
        index:=0
        res:=[]int{}
        for tmp!=0 {
            if tmp%2==1 {
                res=append(res, nums[index])
            }   
            index++
            tmp=tmp>>1
        }
        i++
        ans=append(ans, res)
    }
    return ans
}




T401: 二进制手表

  • 回溯法剪枝得到小时或者分钟亮n个时的所有不重复值;再组合起来
var hour []int=[]int{1, 2, 4, 8}
var minute []int=[]int{1, 2, 4, 8, 16, 32}

func readBinaryWatch(num int) []string {
    ans:=[]string{}
    for i:=0;i<=num;i++ {
        j:=num-i
        ans_hour:=[]int{}
        readBinaryWatchAssist(i, 0, 11, hour, 0, &ans_hour)
        ans_minute:=[]int{}
        readBinaryWatchAssist(j, 0, 59, minute, 0, &ans_minute)
        for idx_h:=0;idx_h<len(ans_hour);idx_h++ {
            for idx_m:=0;idx_m<len(ans_minute);idx_m++ {
                ans=append(ans, strconv.Itoa(ans_hour[idx_h])+":"+fmt.Sprintf("%02s", strconv.Itoa(ans_minute[idx_m])))
            }
        }
    }
    return ans

}

func readBinaryWatchAssist(num int, sum int, limit int, list []int, idx int, ans_hour *[]int) {
    if sum>limit {
        return
    }
    if num==0 {
        *ans_hour=append(*ans_hour, sum)
        return
    }
    if idx==len(list) {
        return
    }
    
    for i:=idx;i<len(list);i++ {
        readBinaryWatchAssist(num-1, sum+list[i], limit, list, i+1, ans_hour)
    }
} 




T886: 可能的二分法

  • 二分图问题
func possibleBipartition(N int, dislikes [][]int) bool {
    graph:=make(map[int][]int,N)
    for i:=1;i<=N;i++ {
        graph[i]=[]int{}
    }
    for i:=0;i<len(dislikes);i++ {
        a:=dislikes[i][0]
        b:=dislikes[i][1]
        graph[a]=append(graph[a],b)
        graph[b]=append(graph[b],a)
    }
    marked:=make([]bool,N+1)
    color:=make([]bool,N+1)
    for i:=1;i<=N;i++ {
        if !marked[i] {
            if !dfs(graph,color,marked,i) {
                return false
            }
        }
    }
    return true
}

func dfs(graph map[int][]int, color []bool, marked []bool, root int) bool {
    marked[root]=true
    for _,w := range graph[root] {
        if !marked[w] {
            color[w]=!color[root]
            if !dfs(graph,color,marked,w) {
                return false
            }
        } else if color[w]==color[root] {
            return false
        }
    }
    return true
}




T421: 数组中两个数的最大异或值

  • O(N)时间复杂度
  • 首先我们将数组中每个数按照最大长度为 L = [ log ⁡ 2 ( max ⁡ { n u m s } ) ] + 1 L=[\log_2(\max \{nums\})]+1 L=[log2(max{nums})]+1左补零,从而得到所有数的相同长度的二进制表示
  • 然后存入字典树Trie中,每个存入操作的时间复杂度为 O ( L ) ≈ O ( 1 ) O(L)\approx O(1) O(L)O(1)
  • 针对每一个存入操作,同时计算其可以构成的最大异或值,根据DP的思想,最终得到全局最大值
  • 具体计算时,从左到右每一位都检查Trie相应节点有无异或值(0/1),若有则相应增加complement并跳到其异或节点,无则使用原节点,不进行其他操作

参考官方题解图
在这里插入图片描述

在这里插入图片描述

import "math"

type Node struct {
    next [2]*Node
}

type Trie struct {
    root *Node
}

func (t *Trie) Insert(zero int, x []int) {
    node:=t.root
    for i:=0;i<zero+len(x);i++ {
        if i<zero {
            if node.next[0]==nil {
                node.next[0]=&Node{}
            }
            node=node.next[0]
        } else {
            tmp:=x[len(x)-1-i+zero]
            if node.next[tmp]==nil {
                node.next[tmp]=&Node{}
            }
            node=node.next[tmp]
        }
    }
}

func findMaximumXOR(nums []int) int {
    maxNum:=0
    for i:=0;i<len(nums);i++ {
        if maxNum<nums[i] {
            maxNum=nums[i]
        }
    }
    trie:=Trie{&Node{}}
    L:=int(math.Trunc(math.Log2(float64(maxNum)))+1)
    ans:=0
    for i:=0;i<len(nums);i++ {
        x:=nums[i]
        tmp:=[]int{}
        for x!=0 {
            tmp=append(tmp, x%2)
            x/=2
        }
        trie.Insert(L-len(tmp), tmp)
        complement:=0
        node:=trie.root
        pow:=L-1
        for j:=0;j<L-len(tmp);j++ {
            if node.next[1]!=nil {
                complement+=1<<pow
                node=node.next[1]
            } else {
                node=node.next[0]
            }
            pow--
        }
        for j:=len(tmp)-1;j>=0;j-- {
            c:=tmp[j]^1
            if node.next[c]!=nil {
                complement+=1<<pow
                node=node.next[c]
            } else {
                node=node.next[tmp[j]]
            }
            pow--
        }
        if complement>ans {
            ans=complement
        }
    }
    return ans
}




剑指62: 循环删除剩下的最后一个数字

  • 约瑟夫环
  • 可以推广到对数组循环删除第m个(0,1,2,…,n-1理解成索引)
    在这里插入图片描述
class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        k=1
        index=0
        while k<n:
            index=(index+m)%(k+1)
            k+=1
        return index




T172: 阶乘后的零

  • 首先一个数阶乘后面的零的个数只与该数阶乘后因数分解中2,5对的个数有关
  • 而因为一个数的阶乘在一定范围后2因子的个数超过5因子的个数,所以一个数的阶乘后面零的个数与该数阶乘5因子的个数相等
  • 一个数的阶乘可以表示为 5 × ( n / 5 ) ∗ 5 × ( n / 5 − 1 ) ∗ . . . ∗ 5 ∗ a 5 \times (n/5)*5\times(n/5-1)*...*5*a 5×(n/5)5×(n/51)...5a,其中a中无法分解出5因子
  • 因此一个树阶乘的5因子个数为 n / 5 + ( n / 5 ) ! 中 包 含 的 5 因 子 个 数 n/5+(n/5)!中包含的5因子个数 n/5+(n/5)!5
  • 递归解法
func trailingZeroes(n int) int {
    if n<5 {
        return 0
    }
    n/=5
    return n+trailingZeroes(n)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值