剑指OfferC++_41-50

目录

41、和为S的连续正数序列

42、和为S的两个数字

43、左旋转字符串

44、翻转单词顺序列

45、扑克牌顺子

46、孩子们的游戏(圆圈中最后剩下的数)

47、求1+2+3+...+n

48、不用加减乘除做加法

49、把字符串转换成整数

50、数组中重复的数字


41、和为S的连续正数序列

题目描述:输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

两种方法:第一种滑动窗口,第二种方法凑数。

方法1:对于给定的序列是顺序递增的,要看连续的数字和与sum的关系。首先想到的是滑动窗口的方法。
滑动窗口两个指针分别指向前两个数,之后看两个指针对应的数之间的所有数的和。如果大于SUM,a就向右滑动,这样总和就变小。否则如果小于sum,b就向右滑动,总和就变多。这样当总和等于sum的时候就push。并且这个时候两个指针都向右移动。 而如果原来在左边的移动到了右边的位置,就说明当前这个数自己就已经比sum大了。就停止遍历。

方法2:找规律。对于一个连续的序列,和是sum。我们假设序列一共n个。
那么如果这个序列奇数个,sum一定是n的倍数,且sum/n=x。这个序列是x-(n-1)/2 ~ x+(n-1)/2
如果这个序列偶数个,sum/n得到的一定是小数,例如对于序列 12 13 14 15 sum/4得到的是13.5。而一个数除以另一个数得到的是小数,且小数点是0.5代表余数是除数的一半,所以如果是偶数,那么(sum%n)*2==n,且 序列是 sum/n =x x-(n-1)/2 ~ x+(n-1)/2+1

vector<vector<int> > FindContinuousSequence_1(int sum) {
          int a=1,b=2;//初始化为前两个数,a和b分别指向序列得首尾
          vector<vector<int> > kk;
          while(a<b)
          {
              int sumnow=(b+a)*(b-a+1)/2;//等差数列求和
              if(sumnow==sum)
              {
                  vector<int> k;
                  for(int i=a;i<=b;i++)  k.push_back(i);
                  kk.push_back(k);
                  a++;b++;
              }
              else if(sumnow<sum)  b++;
              else  a++;
          }
          return kk; }

vector<vector<int> > FindContinuousSequence_2(int sum) {
         vector<vector<int> > kk;
         if(sum==1 || sum==2)
         {
             vector<int> k;
             k.push_back(sum);
             kk.push_back(k);
             return kk;
         }
         for(int n=sqrt(2*sum);n>=2;n--)//n是几个数的和是sum。  n--,因为序列间按照开始数字从小到大的顺序。而个数越多,每一个数越小
         {
              if((n&1)==1 && sum%n==0)//n&1==1代表当前是奇数个,那么sum%n可以整除得时候成立
                {
                   vector<int> k;
                   for(int i=sum/n-(n-1)/2 ; i<=sum/n+(n-1)/2 ;i++)
                   k.push_back(i);
                   kk.push_back(k);
                }
                if((n&1)==0 && (sum%n)*2==n)//n&1==0当前是偶数个,代表除完结果是0.5,也就是余数是除数得一半
                {
                   vector<int> k;
                   for(int i=sum/n-(n-1)/2 ; i<=sum/n+(n-1)/2+1 ;i++)
                   k.push_back(i);
                   kk.push_back(k);
                }
         }
         return kk;
    }

42、和为S的两个数字

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

方法:前后指针的方法,乘积最小的注意:两个数相加一样,差越大,成绩越小。

 vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        int a1=0,a2=array.size()-1;
        vector<int> k;
        while(a1<a2)
        {
            if((array[a1]+array[a2])<sum) a1++;
            else if((array[a1]+array[a2])>sum)  a2--;
            else{
            k.push_back(array[a1]);
            k.push_back(array[a2]);
            return k;
         } }
        return k;
   }

43、左旋转字符串

题目描述:循环左移(ROL)对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。

注意:左旋转的次数可能比字符串本身的长度长。最好想的方法是手动让其循环左移动K位。但是这样的方法不好。两个方法,第一种:先求得循环一次移动的位数,之后将这个字符串加上自己,之后截取。第二种方法:(yx)=(xTyT)T,并且在string中可以使用swap交换两个位置的值。

 string LeftRotateString_1(string str, int n) {
      if(str.length()==0)   return ""; //一定注意临界条件
      n=n>str.length()?n%str.length():n;
      str+=str;
      return str.substr(n,str.length()/2);//substr(a,b) a是起点位置,b是长度 }
 
string LeftRotateString_2(string str, int n) { //(xy)=(XTYT)T,在string中使用swap可以换两个位置的值
    if(str.length()==0)  return "";
    n=n>str.length()?n%str.length():n;
    for(int i=0;i<n/2;i++) swap(str[i],str[n-i-1]);
    for(int i=n;i<((str.length()-n)/2+n);i++) swap(str[i],str[str.length()-i-1+n]);
    for(int i=0;i<str.length()/2;i++) swap(str[i],str[str.length()-i-1]);
    return str;}

44、翻转单词顺序列

将一个英语句子翻转 英语单词之间用空格隔开,使用substr(' ',位置)。

string ReverseSentence(string str) {
    string u="";
    int l=str.length(); int s=0;
    while(1)
    {
        int e=str.find(' ',s);
        u=' '+str.substr(s,e-s)+u;
        s=e+1;
        if(e==-1)  break;
    }
    return u.substr(1,u.length()-1);
    }

45、扑克牌顺子

题目:他随机从中抽出了5张牌,一共1-14,还有0,.0可以看作任何数字。可以连输出true,否则就输出false。
解题思路:最开始肯定是想看看抽到的牌是不是0,能不能连起来。但是可以换一个下想法,如果是连着的那么说明有两点是必要的: 1. 除0外没有重复的数   2. max - min < 5 如果满足这两个那么就一定是顺子。
 

 bool IsContinuous( vector<int> numbers ) {
    if(numbers.size()!=5)    return false;
    int a[15]={0}; int minn=15; int maxx=-1; //a的作用相当于hash
    for(int i=0;i<numbers.size();i++)
    { 
        if(numbers[i]==0)   continue;
        if(a[numbers[i]]!=0)return false;
        a[numbers[i]]++;
        if(minn>numbers[i]) minn=numbers[i];
        if(maxx<numbers[i]) maxx=numbers[i];
        if((maxx-minn)>=5)  return false;
    }
    return true;
    }

46、孩子们的游戏(圆圈中最后剩下的数)

题目:围成一个圈。随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,想下,哪个小朋友会得到这份礼品呢?

解题思路:约瑟夫环问题:这种问题有两种解法。第一个方法:使用循环链表 链表的data存的是编号,next指向下一个。当遇到m-1,上一个的next就指向他的Next。第二个方法公式法:对于约瑟夫链表假设0-10 n=11 m=3 则序列过程如下:  

f(n,m)=(f(n-1,m)+m)%n  下一个结果就是上一个左移m位

0 1 /2/ 3 4 5 6 7 8 9 A
3 4 /5/ 6 7 8 9 A 0 1
6 7 /8/ 9 A 0 1 3 4
9 A /0/ 1 3 4 6 7
1 3 /4/ 6 7 9 A
6 7 /9/ A 1 3
A 1 /3/ 6 7   1 3 6 7 A 如果上一个是A,那么这个是上一个左移3个的结果
6 7 /A/ 1      1 6 7 A
1 6 /7/
/1/ 6
/6/          看出如果序列里只有一个数,那么这个数就是结果

相当于在只有一个数的时候拿着这个数,等多一个数那么我们就要基于原来的那个数在走m次,再多一个数就要再多走m次。所以这个位置等于上一个位置之后再往右走m个。
 

 int LastRemaining_Solution(int n, int m)
    { if(n==0) return -1;
      if(n==1) return 0;
      else     return (LastRemaining_Solution(n-1,m)+m)%n;
    }

47、求1+2+3+...+n

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

方法1:使用递归但是不用if!,就相当于递归不能判断结束标志,也就是说,if(n==0) return 0; else return n+f(n-1) 要找一个方法去判断n是不是1.所以使用 int ans =n; n&& ans=n+f(n-1)  因为对于&& 存在短路原理。如果n为0 ,直接n&& ans=n+f(n-1)就为0,直接退出。不会执行f(n-1)。
方法2:使用等差数列求和公式但是不用乘法 , 使用>>1作为除2,使用数组的sizeof做乘法

int Sum_Solution(int n) {
    int ans=n;
    n && (ans+=Sum_Solution(n-1));  //注意一定要有(),这样才可以是表达式
    return ans;
}
int Sum_Solution_1(int n) {
    bool a[n][n+1];
    return (sizeof(a)>>1);
}

48、不用加减乘除做加法

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

方法:可以把这个数看作二进制,运用二进制的加法的方法。

//step1:按位与是查看两个数哪些二进制位都为1,这些都是进位位,结果需左移一位,表示进位后的结果
//step2:异或  :相当于每一位相加,而不考虑进位;  是查看两个数哪些二进制位只有一个为1,这些是非进位位,可以直接加、减,结果表示非进位位进行加操作后的结果
//step3:将得到的进位数和非进位数直接赋值给Num1,num2。让他们相加。最终在没有进位通知。
比如:5+7  得到进位位10 ,非进位直接加的结果是2.所以将10+2就是最终的结果了,
 

int Add(int num1, int num2){
    int yh=num1 ,yy=num2;
    while(1){
    yh=num1^num2; //yh存储的是两个数相加的和不考虑进位的情况     
    yy=(num1&num2)<<1;//yy是进位的情况
    if(yy==0) break;//正常的情况需要将不进位的和与进位情况相加得到的是最终的结果。如果不进位了那么代表着此时的yh就是最终的结果。
    num1=yh;
    num2=yy;; }
    return yh;//如果num2进来的时候就是0,那么直接输出的就是yh。是num1与num2异或的结果,因为一个数与0与得到的是本身
}

49、把字符串转换成整数

题目:将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

题目要考虑很多情况:
/*边界条件考虑清楚:1、首先里面不能有符号2、其次第一位是有变化的,可能是负号,正号,3、并且要排除第一位是0的情况4、要排除一个正号负号后面什么都没有的情况、5、并且要判断如果传进来的string是空的情况

int StrToInt(string str) {
    if(str=="" || str.length()==0) return 0;
    int fuhao=1,sum=0;
    if(str[0]=='+')
        {if(str.length()==1) return 0;}
    else if(str[0]=='-') 
        {if(str.length()==1) return 0;
         else fuhao=-1;}
        else if(str[0]>'9' || str[0]<'1') return 0;
        else sum=str[0]-'0';
        for(int i=1;i<str.length();i++)
        {
            if(str[i]>'9' || str[i]<'0')  return 0;
            sum=sum*10+str[i]-'0';
        }
    return sum*fuhao;
}

50、数组中重复的数字

题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。
也不知道每个数字重复几次。请找出数组中第一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。注意!是第一个第二次出现的数,而不是重复数里的第一个数。

方法:很明显想到hash,但是题目里有一个长度n的数组里的数的范围也是0~n-1,所以可以不需要用到额外的内存,也就是说不需要再开辟一个新的hash表,直接用原数组作为hash表。对于出现过的数对应的位置,可以+n。这样就可以判断是否出现过了(当前这个数所在的位置上的值是否>n)。而对于那个位置的数,通过取余就可以得到原数,之后通过原数找到这个数对应位置上的数是否>n

bool duplicate(int numbers[], int length, int* duplication) {
     for(int i=0;i<length;i++)
        {
            if(numbers[numbers[i]%length]>length)//需要%,因为可能这个i这个数被遍历到过,那么number[i]本身是大于n的
                {
                    *duplication=numbers[i]%length;//如果大于n代表遍历到过直接返回true,,并且duplication等于取余之后的数就可以了。
                    return true;}
            numbers[numbers[i]%length]+=length;//注意先取余再加length
        }
        return false;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值