判断是否为质数(暴力+厄拉多塞筛法)和丑数12(暴力+堆+动规)

1 质数

 计算质数:统计所有小于非负整数 n 的质数的数量。

1 暴力

 判断素数的常规方法是判断这个数是否只能被1和自身整除,常规方法是从1-n遍历,利用自己编写的isprime(i)函数查看这个数是否为素数,时间复杂度位 O ( n 2 ) O(n^2) O(n2)
 在isprime(i)函数函数中,只需遍历到sqrt(n)即可。也就是说,如果在 [2,sqrt(n)] 这个区间之内没有发现可整除因子,就可以直接断定 n 是素数了,因为在区间 [sqrt(n),n] 也一定不会发现可整除因子。因为当n=12时,
  2*6=12
  6*2=12

    bool isPrime(int x) {
        if (x<2) return false;
        for (int i=2;i*i<=x;i++)
            if (x%i==0) return false;
        return true;
    }
    int countPrimes(int n) {
        int count=0;
        for (int i=2;i<n;i++)
            if (isPrime(i)) count++;
        return count;
    }

2 厄拉多塞筛法

  简便方法是:先开辟一个1xn的数组,假设从1-(n-1)的数都是素数,记为true,反过来求出不是素数的的元素,记为false,最后只需遍历数组中true的个数即为质数的个数
  判断不是素数的方法是: 2是素数,则 2*2 2*3 2*4不是素数
  3是素数,则 3*2 3*3 3*4不是素数
  4不是素数,已经被2更新过
  5是素数,则 5*2 5*3 5*4不是素数 … 。

 int countPrimes(int n) {
     if(n<=1)
        return false;
        vector <bool> v(n,true);
        v[0]=v[1]=false;
        for(int i=1; i<n;i++)
        {
            if(v[i]==true)
            {
                int j=i,k=2;
                while(j<n)
                {
                    j=i*(k++);
                    if(j<n)
                    v[j]=false;
                }
            }
        }
        int count=0;
        for(auto e:v)
        {
            if(e==true)
            count++;
        }
        return count;

我们还可以继续优化,当我们知道
  n=24
  3*12 =24 2是质数,则12肯定不是质数。
  3*8 =24 3是质数,则8肯定不是质数。
将for循环改成如下:

for(int i=1; i*i<n;i++)

2 丑数

  如果只是让我们判断丑数,则和leetcode 263.题一样。
编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。1是丑数。
  思路:我们知道,任何一个数都可以写成如下形式:
n u m = p 1 k 1 p 2 k 2 p 3 k 3 p 4 k 4 . . . num=p^{k_1}_1p^{k_2}_2p^{k_3}_3p^{k_4}_4... num=p1k1p2k2p3k3p4k4...
其中 p 1 , p 2 . . . p_1,p_2... p1,p2...是素数, k 1 , k 2 . . . k_1,k_2... k1,k2...是任意数。
则我们得到丑数可以表示为 n u m = 2 k 1 3 k 2 5 k 3 num=2^{k_1}3^{k_2}5^{k_3} num=2k13k25k3,所以当我们判断一个数是否为丑数时,只需不停的整除2 3 5,如果最后结果1,则是丑数,否则返回false

 bool isUgly(int num) {
        //不停的整除2 3 5,如果最后结果1,则是丑数,否则返回false
        if(num<=0)
        return false;
        if(num==1)
        return true;
        while(num%2==0) num/=2;
        while(num%3==0) num/=3;
        while(num%5==0) num/=5;
        if(num!=1)
        return false;
        else
        return true;

1 暴力

而当我们把题目变为编写一个程序,找出第 n 个丑数。暴力解法就是循环判断,这在leetcode会超时。

bool isugly(int num)
    {
        if(num<=1)
            return true;
        while(num%2==0) num/=2;
        while(num%3==0) num/=3;
        while(num%5==0) num/=5;
        if(num==1)
            return true;
        return false;
    }
     int nthUglyNumber(int n) {
      int num=1,flag=0;
        while(1)
        {
            if(isugly(num++))
                flag++;
            if(flag==n)
                break;
        }
        return --num;
    }

暴力方法也可以是列举出[1-INT_MAX]中所有的丑数,然后返回v[n-1]。

 int nthUglyNumber(int n) {
      vector<int> v;
      for(long i=1; i<=INT_MAX;i*=2)
      {
           for(long j=i; j<=INT_MAX;j*=3)
           {
               for(long k=j; k<=INT_MAX;k*=5)
               v.push_back(k);
           }
      }
      sort(v.begin(),v.end());
      return v[n-1];
    }

这个方法没有重复元素,所以不要去重。

2 堆

利用优先队列有自动排序的功能
每次取出队头元素,存入队头元素*2、队头元素*3、队头元素*5。但注意,像12这个元素,可由4乘3得到,也可由6乘2得到,所以要注意去重

  int nthUglyNumber(int n) {
         //建立一个小根堆,向堆里插入元素。
     priority_queue<double,vector<double>,greater<double>> pq;
     pq.push(1);
     int ans=1;
     for(int i=1;i<n;i++)
     {
         pq.push(ans*2);
         pq.push(ans*3);
         pq.push(ans*5);
         ans=pq.top();
         pq.pop();
          while(!pq.empty()&&pq.top()==ans)
          pq.pop();
     } 
     return ans;
    }

3 动 规

  我们所有的丑数都是 n u m = 2 k 1 3 k 2 5 k 3 num=2^{k_1}3^{k_2}5^{k_3} num=2k13k25k3, 通过之前的丑数乘以 2, 3, 5 生成的,所以丑数序列可以看成下边的样子。
1, 1×2, 1×3, 2×2, 1×5, 2×3, 2×4, 3×3…。
我们可以把丑数分成三组,用丑数序列分别乘 2, 3, 5 。
乘 2: 1×2, 2×2, 3×2, 4×2, 5×2, 6×2, 8×2,9×2,…
乘 3: 1×3, 2×3, 3×3, 4×3, 5×3, 6×3, 8×3,9×3,…
乘 5: 1×5, 2×5, 3×5, 4×5, 5×5, 6×5, 8×5,9×5,…
我们需要做的就是把上边三组按照顺序合并起来。
合并有序数组的话,可以通过归并排序的思想,利用三个指针,每次找到三组中最小的元素,然后指针后移。
当然,最初我们我们并不知道丑数序列,我们可以一边更新丑数序列,一边使用丑数序列。状态转移方程为:
d p [ i ] = m i n ( m i n ( x 1 , x 2 ) , x 3 ) dp[i]=min(min(x1,x2),x3) dp[i]=min(min(x1,x2),x3)
= m i n ( m i n ( 2 ∗ d p [ i n d e x 2 ] , 3 ∗ d p [ i n d e x 3 ] ) , 5 ∗ d p [ i n d e x 5 ] ) =min(min(2*dp[index2],3*dp[index3]),5*dp[index5]) =min(min(2dp[index2],3dp[index3]),5dp[index5])

int nthUglyNumber(int n) {
      //归并排序
      vector<int> dp(n,1);
      int index2=0,index3=0,index5=0;
      for(int i=1;i<n;i++)
      {
        int x1=2*dp[index2];
        int x2=3*dp[index3];
        int x3=5*dp[index5];
        int ele=min(min(x1,x2),x3);
        dp[i]=ele;
        if(ele==x1)
           index2++;
        if(ele==x2)
           index3++;
         if(ele==x3)
           index5++;           
      }
      return dp[n-1];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值