剑指Offer——数学问题

剑指Offer——数学问题

1、第7题 斐波那契数列

题目描述:
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39

解题思路:
这道题的思路就不多介绍了,我们这里给出递归和非递归的实现方式。

代码实现:
递归方式(这里在牛客网上会超时)

#include<iostream>
using namespace std;

int Fibonacci(int n)
{
	if (n == 0)
		return 0;
	if(n == 1)
		return 1;
	return Fibonacci(n - 1) + Fibonacci(n - 2);
}
int main()
{
	int res = Fibonacci(10);
	cout << res << endl;
	return 0;
}

非递归方式

class Solution {
public:
    int Fibonacci(int n) {
      int *number = new int[n+1];
      number[0] = 0;
      number[1] = 1;
      int index = 2;
      while(index <= n)
      {
          number[index] = number[index -1]+number[index -2];
          index ++;
      }
        return number[n];
        
    }
};
2、第8题 跳台阶问题

题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

解题思路:

如果当前处于的是第k个台阶:
如果 k<= 1,则说明跳跃到当前台阶的方式只能是从前一个台阶跳上来。
如果 k>1 则跳跃到当前台阶的方式有两种,一种是从前一个台阶跳跃一个到达,第二种方式从前两个台阶跳跃两个方式到达。
显然,上面描述的是一个递归的过程。也就是说jump(k) = jump(k-1)+jump(k-2)。

代码实现:

class Solution {
public:
    int jumpFloor(int number) {
        if(number == 0)
            return 0;
        return jump(number);
    }
    int jump(int n)
    {
        if(n <= 1)
            return 1;
        return jump(n-1)+jump(n-2);
    }
};
3、第9题、变态跳台阶问题

题目描述:

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路:
这道题需要一点点的数学推理:对于一个k阶的台阶有:

如果k≤1的时候,只有一种方式来到达。在返回1。
如果k>1的时候,跳跃到k阶的方式包括从第1阶开始,第二阶开始,…,第k-1阶开始,也就是说jump(k)= jump(1)+jump(2) +,…,+jump(k-2)+jump(k-1),而jump(1)+jump(2) +,…,+jump(k-2)=jump(k-1)。则jump(k) = 2 * jump(k-1)。至此,递推公式完成。

代码实现:

class Solution {
public:
    int jumpFloorII(int number) {
         if(number <= 1)
             return number;
        return jumpFloorII(number-1) *2 ;
    }
};
4、第10题 矩形覆盖

题目描述:
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

比如n=3时,2*3的矩形块有3种覆盖方法:
在这里插入图片描述

解题思路:
我们开始逐步的推导一下:
当n等于1的时候,此时只用一种方式进行覆盖。
当n等于2的时候,可以竖着覆盖,同时也可以横着覆盖,此时有两种方式。
当n=3的时候,此时,我们可以倒推,暂时屏蔽掉一个位置,那么此时n=2的时候方式的数量相同,同理屏蔽掉两个位置,则此时和n=1的时候方式相同,也就是说f(3)=f(2)+f(1)
当n=4的时候,是在2个的基础上在增加两个,每两个的覆盖方式有f(2)种,则一共的覆盖方式有f(2)。或者在3的基础上增加一个,此时方式为f(3),则有f(4)=f(3)+f(2)。
所以,我们可以推导出递推公式为f(n) = f(n-1)+f(n-2),也就是斐波那契数列。

代码实现:

class Solution {
public:
    int rectCover(int number) {
        if(number<=2)
            return number;
        return rectCover(number-1)+rectCover(number-2);
    }
};
5、第11题 二进制中1的个数

题目描述:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

思路分析:
这道题的思路还是很简单的,只需要采用一个1,不断的去和数字中的各个位置进行与运算,并记录不为0的情况。

代码实现:

class Solution {
public:
     int  NumberOf1(int n) {
         int flag = 1;
         int count = 0;
         while(flag != 0)
         {
             if((flag & n) != 0)
                 count ++;
             flag = flag<<1;
         }
         return count;
     }
};
6、第12题 数值的整数次方

题目描述:

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

保证base和exponent不同时为0

解题思路:
直接计算

代码实现:

class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent == 0)
            return 1;
        if(base == 0)
            return 0;
        int exp = abs(exponent);
        double res = 0.0;
        res = quickPower(base,exp);
        if(exponent < 0)
            return 1/res;
        else
            return res;
       
    }
    double quickPower(double base,int exponent)
    {
        if(exponent == 1)
            return base;
        return base * quickPower(base,exponent-1);
    }
};
7、第29题 最小的K个数

题目描述:

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

解题思路:

排序,获得结果并输出

代码实现:

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
       vector<int> res;
       if(input.size() ==0 || k>input.size()) return res;
       sort(input.begin(),input.end());
       for(int i=0;i<k;i++)
           res.push_back(input[i]);
       return res;
    }
};
8、第31题 整数中1出现的次数

题目描述:

求出1-13的整数中1出现的次数,并算出100-1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

解题思路:
我们来分析一下一出现的位置:

  1. 当1作为个位数出现的时候,有1,11,21,31,41,51,…,91
  2. 第1作为十位数出现的时候,有10,11,12,13,…,
  3. 当1作为百位出现的时候,有100,101,102,。。。,199

所以,下一步我们是判断给定n的大小,当n为个位数的时候,返回1,当n为十位数的时候,返回的是1出现在十位上和出现在个位数上的和。其他同理。

代码实现:

class Solution {
public:
 int NumberOf1Between1AndN_Solution(int n)
    {
            if(n<=0)
                return 0;
            int count = 0;
            for(int i=1;i<=n;i*=10)
            {
                int divier = i * 10;
                int k = n %(i * 10);
                count += (n/divier) * i;
                if(k>2*i-1)
                    count += i;
                else if(k <i)
                    count += 0;
                else
                    count += (k-i+1);
                    
            }
     return count ;
    }
};
9、第33题 丑数

题目描述:

把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

解题思路:

对于这道题,我们首先设置三个下标参数t2,t3,t5,然后开始循环生成,根据丑数的概念,我们只需要将对这三个质因子做累乘,其结果就是一个丑数,然后比较t2指向的丑数2,t3指向的丑数3,t5指向的丑数乘以5,在比较三个结果的大小,选择最小的结果作为当前位置的丑数即可。

代码实现:

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index<7)
            return index;
        int t2=0,t3=0,t5=0;
        vector<int> res(index);
        res[0] = 1;
        for(int i=1;i<index;i++)
        {
            res[i] = min(res[t2]*2,min(res[t3]*3,res[t5]*5));
            if(res[i] == res[t2] *2) t2++;
            if(res[i] == res[t3] *3) t3++;
            if(res[i] == res[t5] *5) t5++;
        }
        return res[index -1];
    }
};
10、第41题 和为S的连续整数序列

题目描述:

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

解题思路:

本题考查的就是一个连续序列相加的数学公式,根据等差数列的求和公式有: n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2,则我们可以定义两个下标参数,将两个下标之间的数字相加和目标去比较,如果小于目标参数,则前面的参数指针继续向下走,如果大于目标参数,则后面的下标指针向前一个位置。如果相等,则输出结果。

代码实现:

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int>> res;
        if(sum<=0)
            return res;
        int pslow = 1,pfast = 2;
        while(pfast>pslow)
        {
            int temp = (pfast+pslow)*(pfast - pslow +1) / 2;
            if(temp == sum)
            {
                vector<int> a;
                for(int i=pslow;i<=pfast;i++)
                    a.push_back(i);
                res.push_back(a);
                pslow++;
            }
            else if(temp < sum)
                pfast ++;
            else
                pslow ++;
        }
        return res;
    }
};
11、第41题 和为S的两个数字

题目描述:

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

解题思路:

两个位置指针,第一个从前向后low,第二个从后向前high,,当low+high小于sum的时候,low向前移动,当low+high大于sum的时候,high前后移动。当相等的时候与最下的乘积值进行比较。

代码实现:

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> res;
        if(array.size()==0)
            return res;
        int low=0,high = array.size()-1;
        int minv = INT_MAX;
        while(high > low)
        {
            if((array[high] + array[low]) > sum)
                high --;
            else if((array[low] + array[high]) < sum)
                low ++;
            else
            {
                if((array[low] * array[high]) < minv)
                {
                    minv = array[low] * array[high];
                    res.clear();
                    res.push_back(array[low]);
                    res.push_back(array[high]);
                }
                low++;
                high --;
            }
        }
        return res;
            
    }
};
12、第45题 扑克牌的顺子

题目描述:

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

解题思路:
我们需要考虑以下几种情况,

  1. 如果当前数字大于13或者小于0,则返回false;
  2. 如果当前数字为0的时候,则跳过不考虑。
  3. 如果当前数字中出现了重复的情况,则返回false,这里我们采用flag作为标记,如果两次flag相与不为0,则说明有数字重复。
  4. 在遍历的过程中,记录最大值和最小值,如果最大值和最小值之间的差值≥5,则返回false。

代码实现:

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        if(numbers.size() == 0)
            return false;
        int maxv = -1;
        int minv = 13;
        int flag = 1;
        for(int i=0;i<numbers.size();i++)
        {
            if(numbers[i] >13|| numbers[i] < 0) return -1;
            if(numbers[i] == 0)  continue;
            if(flag & (1<<numbers[i])) return false;
            flag |= (1<<numbers[i]);
            if(numbers[i] > maxv)
                maxv = numbers[i];
            if(numbers[i] < minv)
                minv = numbers[i];
        }
        if(maxv-minv >= 5)
            return false;
        return true;
    }
};
13、第46题 孩子们的游戏

题目描述:

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

解题思路:

用链表来模拟环,当到对应的数字的时候,删除该节点,最终只剩一个节点的时候就是我们希望的结果。

代码实现:

struct Person
{
    int val;
    struct Person * next;
    Person(int v):val(v),next(NULL){}
};

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
       if(n<=0||m<=0) 
           return -1;
        Person * head = new Person(0);
        Person * cur = head;
        for(int i=1;i<n;i++)
        {
            Person * p = new Person(i);
            cur->next = p;
            cur = cur->next;
        }
        cur->next = head;
        int k =0;
        while(cur != cur->next)
        {
            if(++k == m)
            {
                cur->next = cur->next->next;
                k = 0;
            }
            else
                cur = cur->next;
        }
        return cur->val;
    }
};
14、第47题、求1+2+3+…+n

题目描述:

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

解题思路:
本题的思路比较巧妙,需要提前理解到C++中的“短路操作”,在表达式1&&表达式2,如果表达式1不满足条件,则表达式2不会被计算。我们采用递归的方式,当n的0的时候递归结束。

代码实现:

class Solution {
public:
    int Sum_Solution(int n) {
        int sum = n;
        n && (sum += Sum_Solution(n-1));
        return sum;
    }
};
15、第48题、不用加减乘除做加法

题目描述:

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

解题思路:

采用异或操作和进位标志来表示进位来完成加法。

代码实现:

class Solution {
public:
    int Add(int num1, int num2)
    {
         int carry = (num1 & num2)<<1;
        int tempr = num1 ^ num2;
        while(carry & tempr)
        {
            num1 = carry;
            num2 = tempr;
            carry = (num1 & num2) << 1;
            tempr = num1 ^ num2;
        }
        return carry^tempr;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值