《剑指Offer》做题总结(三)

31.整数中1出现的次数

题目描述:

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

思路:

这一题开始的时候没看清楚,以为只要出现1的数字就记一下,结果并不是这样,而是这么些数字里面只要有1就计一下。因此需要改变思路,对每一个数遍历过去,然后对这个数进行含有1的个数进行判断,这个思路还是比较通俗易懂。然后就是对每一个数字进行判断。除以10的余数代表个位上的1数目,对10整除相当于求剩下的位数上的1的个数。

代码:

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)

    {
    if(n<1)return 0;
        int temp;
        int count;
        for(int i=1;i<=n;++i)
            {
            temp=i;
            while(temp)
                {
                if(temp%10==1)
                    count++;
                temp=temp/10;

                
            }
            
        }
        return count;
    }
};


32.把数组排成最小的数

题目描述:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

思路:

这一道题,其实让我学到很多一个就是sort函数的使用情景,还有一个就是to_string 函数的存在,讲真,我从来没奢望C++能提供这么简明暴力的函数直接给我们用,实在是太方便了。还有一个就是sort函数,这个sort呢,比较有意思,你直接写一个sort(begin(),end())就是一个升序的排序函数,如果你编写一个cmp函数(这个cmp函数就是一个判断的函数,一般的打头的是static bool),然后将这个函数放到sort里面做第三个参数。

代码:

class Solution {
public:
    static bool cmp(int a,int b)

            {
            string A="";
            string B="";
            A+=to_string(a);
            A+=to_string(b);
            B+=to_string(b);
            B+=to_string(a);
            
            
            return A<B;
            
            
        }
    string PrintMinNumber(vector<int> numbers) {
        string res="";
        sort(numbers.begin(),numbers.end(),cmp);

        for(int i=0;i<numbers.size();i++)
            {
            res+=to_string(numbers[i]);
            
        }
        return res;
        
    }
};

33.丑数

题目描述:

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

思路:

这一道题,思维很重要,什么思维呢,就是求这个数并不一定需要将这个数分解开来做,而是将这个数慢慢求出来。首先丑数是由2,3,5这三个数组成也就是说小于7 的数都是丑数。所以可以先判断7,然后根据2,3,5来求到不同的值,来进行判断。

代码:

class Solution {
public:
    int GetUglyNumber_Solution(int index) {

    if(index<7)
            return index;
        vector<int>res(index);
        res[0]=1;
        int t2=0;
        int t3=0;
        int t5=0;
        int i=1;
        for(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];        
    }

};


34.第一个只出现一次的字符

题目描述:

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

思路:

这一道题让我见识到了就是,string类型的字符直接可以当数值,直接放在数组里面当下标毫无问题,也就是说,我设置一个用来记录字符出现次数的数组只要设置在ASCII码的范围内就行,而其中采用hash表(hash表说白了就是有下标和数值相对应的一个数组而已)。因此这个题还是好解决的。

代码:

class Solution {
public:
    int FirstNotRepeatingChar(string str) {

        if(str.length()<=0)
            return -1;
        int hash[200]={0};
        for(int i=0;i<str.length();i++)
            {
            hash[str[i]]++;
            
        }
        for(int i=0;i<str.length();i++)
            {
            if(hash[str[i]]==1)
                return i;
            
        }
        return -1;
    }
};

35.数组中的逆序对

题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

思路:

这一道题开始我以为还是比较简单的,实际上如果采用传统的遍历,会导致超出计算时间,复杂度O(n2)。因此采用归并排序:

    看到这个题目,我们的第一反应是顺序扫描整个数组。没扫描到一个数组的时候,逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和O(n)这个数字比较,因此这个算法的时间复杂度为O(n^2)。
        我们以数组{7,5,6,4}为例来分析统计逆序对的过程。每次扫描到一个数字的时候,我们不拿ta和后面的每一个数字作比较,否则时间复杂度就是O(n^2),因此我们可以考虑先比较两个相邻的数字。
(a) 把长度为4的数组分解成两个长度为2的子数组;
(b) 把长度为2的数组分解成两个成都为1的子数组;
(c) 把长度为1的子数组  合并、排序并统计逆序对 
(d) 把长度为2的子数组合并、排序,并统计逆序对;
       在上图(a)和(b)中,我们先把数组分解成两个长度为2的子数组,再把这两个子数组分别拆成两个长度为1的子数组。接下来一边合并相邻的子数组,一边统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中7大于5,因此(7,5)组成一个逆序对。同样在第二对长度为1的子数组{6}、{4}中也有逆序对(6,4)。由于我们已经统计了这两对子数组内部的逆序对,因此需要把这两对子数组  排序  如上图(c)所示,  以免在以后的统计过程中再重复统计。
      接下来我们统计两个长度为2的子数组子数组之间的逆序对。合并子数组并统计逆序对的过程如下图如下图所示。
      我们先用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。如果第一个子数组中的数字大于第二个数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数,如下图(a)和(c)所示。如果第一个数组的数字小于或等于第二个数组中的数字,则不构成逆序对,如图b所示。每一次比较的时候,我们都把较大的数字从后面往前复制到一个辅助数组中,确保  辅助数组(记为copy) 中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。
 过程:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。参考代码如下

代码:

class Solution {
public:
    int InversePairs(vector<int> data) {

       int length=data.size();
        if(length<=0)
            return 0;
       //vector<int> copy=new vector<int>[length];
       vector<int> copy;
       for(int i=0;i<length;i++)
           copy.push_back(data[i]);
       long long count=InversePairsCore(data,copy,0,length-1);
       //delete[]copy;
       return count%1000000007;
    }
    long long InversePairsCore(vector<int> &data,vector<int> &copy,int start,int end)
    {
       if(start==end)
          {
            copy[start]=data[start];
            return 0;
          }
       int length=(end-start)/2;
       long long left=InversePairsCore(copy,data,start,start+length);
       long long right=InversePairsCore(copy,data,start+length+1,end); 
        
       int i=start+length;
       int j=end;
       int indexcopy=end;
       long long count=0;
       while(i>=start&&j>=start+length+1)
          {
             if(data[i]>data[j])
                {
                  copy[indexcopy--]=data[i--];
                  count=count+j-start-length;          //count=count+j-(start+length+1)+1;
                }
             else
                {
                  copy[indexcopy--]=data[j--];
                }          
          }
       for(;i>=start;i--)
           copy[indexcopy--]=data[i];
       for(;j>=start+length+1;j--)
           copy[indexcopy--]=data[j];       
       return left+right+count;
    }
};

36.两个链表的第一个公共节点

题目描述:

输入两个链表,找出它们的第一个公共结点。

思路:

开始的时候我不是很明白,也没思路。通过明白人的点拨提示,发现找出两个链表的公共节点就是提前认定他们的尾巴在一起,然后认定相同的长度时,他们才可以判断是否有公共节点。这样,就好做多了。只要先将长一点的链表去掉多余的部分然后两个链表同步比较就行,还有一个需要注意的就是相同节点意味着,他们的头的位置相同。还有

学到的知识是:简约的写法

    int list_len(ListNode * pHead1){
        if(pHead1==NULL)
            return 0;
        int sum=1;
        while(pHead1=pHead1->next)sum++;   //很惊艳!

        return sum;
        
    }

还有就是指针和引用的区别,感觉挺容易错。

 int list_len(ListNode * pHead1)

 int len1=list_len(pHead1);

代码:

/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {

        int len1=list_len(pHead1);
        int len2=list_len(pHead2);
        if(len1<len2)
            pHead1=walkstep(pHead1,len1-len2);
        else
            pHead2=walkstep(pHead2,len2-len1);
        while(pHead1!=NULL){
            if(pHead1==pHead2)
                return pHead1;
            pHead1=pHead1->next;
            pHead2=pHead2->next;
            
        }
        return NULL;
    }

    int list_len(ListNode * pHead1){
        if(pHead1==NULL)
            return 0;
        int sum=1;
        while(pHead1=pHead1->next)sum++;
        return sum;
        
    }
    ListNode* walkstep(ListNode *pHead1,int step){
        while(step--)
            {
            pHead1=pHead1->next;
        }
        return pHead1;
    }
};


37.数字在排序数组中出现的次数

题目描述:

统计一个数字在排序数组中出现的次数。

思路:

这个题没啥说了。。。但是要提升效率,还需要采用一些特殊的算法。这个题目还有一个就是有序,有序意味着可以采用二分法来解决问题。得到这个位置的值后朝前朝后遍历得到最终存在的值的个数。

代码:

//暴力匹配,时间复杂度第O(n)
//观察数组本身的特性可以发现,排序数组这样做没有充分利用数组的特性,可以使用二分查找,找出数据,然后进行左右进行统计
//具体算法设计:     二分查找找到k的所在位置
//                 在原数组里面分别左右对k的出现次数进行统计
classSolution {
public:
intBinarySearch(vector<int> data, intlow, inthigh, intk)
{
    while(low<=high)
    {
        intm = (high + low) / 2;
        if(data[m] == k)returnm;
        elseif(data[m] < k) low = m+ 1;
        elsehigh = m - 1;
    }
     return - 1 ;
}
     int GetNumberOfK(vector< int > data , int k) {
         if (data.size()== 0 ) return 0 ;
          int len=data.size();
         int KeyIndex= 0 ;
         
         KeyIndex=BinarySearch(data, 0 ,len- 1 ,k);
        if (KeyIndex==- 1 return 0 ;
         int sumber= 1 ;
         int m=KeyIndex- 1 ;
         int n=KeyIndex+ 1 ;
       
        while (m>= 0 &&data[m]==k)
         {
                 m--;sumber++;
             }
         while (n<len&&data[n]==k)
         {
                n++; sumber++;
             }
         return sumber;
     }
};

 

38.二叉树的深度

题目描述:

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

思路:

这个题一般遍历的思想可能就会导致运行时间超过规定的要求,看见大家都用的递归,比较简洁,如果到最后是空那么久返回0,如果是一般情况就是那个大的分支+1;不好想啊

代码:


/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)

    {
    if(!pRoot)return 0;
        return max(1+TreeDepth(pRoot->left),1+TreeDepth(pRoot->right));
    }

};

39.平衡二叉树

题目描述:

输入一棵二叉树,判断该二叉树是否是平衡二叉树

思路:

作为一个半路出家的(虽然原来是在正道上的,平衡二叉树是AVL什么的,我是真不懂,f**k)复习一波平衡二叉树,平衡二叉树也就是AVL,采用递归的思想将各个节点遍历得到最终的结果。以后还得继续学一波

代码:

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)

        {
        if(pRoot==NULL)
            return 0;
        int left=TreeDepth(pRoot->left);
        int right=TreeDepth(pRoot->right);
        
        return (left>right)?(left+1):(right+1);
        
    }

    bool IsBalanced_Solution(TreeNode* pRoot) {
if(pRoot==NULL)

            return true;
        int right=TreeDepth(pRoot->right);
        int left=TreeDepth(pRoot->left);
        int diff=left-right;
        if(diff>1 || diff<-1)
            return false;
        
        return IsBalanced_Solution(pRoot->right) && IsBalanced_Solution(pRoot->left);
    }
};

 

40.数组中只出现过一次的数字

题目描述:

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

思路:

首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。

 这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0 。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了。
 有了上面简单问题的解决方案之后,我们回到原始的问题。如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其它数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。
 我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其它数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0 ,也就是说在这个结果数字的二进制表示中至少就有一位为1 。我们在结果数字中找到第一个为1 的位的位置,记为第N 位。现在我们以第N 位是不是1 为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N 位都为1 ,而第二个子数组的每个数字的第N 位都为0 。
 现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其它数字都出现了两次。因此到此为止,所有的问题我们都已经解决。

代码:

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {

if(data.size()<2)
            return;
        int size=data.size();
        int temp=data[0];
        for(int i=1;i<size;i++)
            temp=temp^data[i];
        if(temp==0)
            return;
        int index=0;

        while((temp&1)==0)
            {
            temp=temp>>1;
            ++index;
            
        }
        *num1=*num2=0;
        for(int i=0;i<size;i++)
            {
            if(IsBit(data[i],index))
                *num1^=data[i];
            else
                *num2^=data[i];
        }
    }
    bool IsBit(int num,int index)
        {
        num=num>>index;
        return (num&1);
    }
};

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

题目描述:

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

思路:

这一题的问题开始想着直接应用for循环就可以解决了,后来发现不是那么回事,问题在于,需要设置一个left_parameter,一个right_parameter,这样的好处在于可以通过调整左右两边的数值左右移动,获得最终的sum。

代码:

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        int l=1,r=1,sumx=1;
        vector<vector<int>>ans;
        while(l<=r){
            r++;
            sumx+=r;
            while(sumx>sum)
                {
                sumx-=1;
                l++;
            }
            if(sumx==sum && l!=r)
                {
                vector<int>tmp;
                for(int i=1;i<=r;i++)
                    tmp.push_back(i);
                ans.push_back(tmp);
            }
        }
        return ans;
    }
};

42.和为S的两个数字

题目描述:

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

思路:

这一题还是用两个变化的边界,然后逐渐趋近最终的值,其中需要注意的是如果是这个是一个递增数列,因此和大于想要的那个值,就把后边的尾巴朝前面去一点,如果小于那个和,则把头朝后面去一点。最终得到最终的值。

代码:

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int>ans;
        int i=0;
        int j=array.size()-1;
        int Sum;
        while(i<j)
            {
            Sum=array[i]+array[j];
            if(Sum>sum)
                {
                j--;
            }else if(Sum<sum)
                {
                i++;
            }else if(Sum==sum)
                {
                ans.push_back(array[i]);
                ans.push_back(array[j]);
                break;
            }
            
        }
        return ans;
    }

};

43.左旋转字符串

题目描述:

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

思路:

这一题有两个思路,但是我事先是不知道的,一个就是原本的C++的内置函数,然后调用函数,用到擦除函数和求子函数,然后实现字符串的颠倒顺序。还有一个是坚持题目的本质,自己写一个字符串的颠倒函数,这个题目本质上就是将原本的字符串分为两个,然后一边颠倒一次,然后整体再颠倒一次,然后就达到整体的左旋转的目的。

使用C++  string的substr函数 和 erase函数  是很有用的,即通过substr可以求到 

代码:

class Solution {
public:
    void fun(string &s,int start,int end)
        {
        char temp;
        while(start<end)
            {
            temp=s[start];
            s[start]=s[end];
            s[end]=temp;
            start++;
            end--;
                
        }

    }
    string LeftRotateString(string str, int n) {
        int len=str.length();
        if(len==0 || n==0)
            return str;
        string &temp=str;
        fun(temp,0,n-1);
        fun(temp,n,len-1);
        fun(temp,0,len-1);
            
        return str;   
        
       
    }
};

44.翻转单词顺序列

题目描述:

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

思路:

这一题开始的时候把题意搞错了,以为只要把所有的字符颠倒顺序,就能得到最终的结果,然后果断采用了之前的 颠倒顺序的算法。感觉还是自己比较懒吧,后来没好好动脑筋想想这个问题,其实就是事先根据这个字符串里面的空格键把每个单独的字符串事先全部反向一下,然后再全部反向然后就事先了最终的目的。但是有一个不太明白的地方就是,为什么最终是0-size。

感悟:这一题还是比较有感悟的后来自己重新写了一遍程序,就出现了好多错误,这里面有一些值得记忆得到地方。1.首先就是string 的substr函数(0,n),代表的是0-n-1的字符。 2.需要注意的是字符串是“”,表示为0的字符串,而' '表示空格键,表示一个字符,这两者是不一样的。3。自己编写的翻转函数最终结尾一定是end-1.

代码:

class Solution {
public:
    void fun(string & str ,int l,int r)
        {
        char temp;
        while(l<r)
            {
            temp=str[l];
            str[l]=str[r];
            str[r]=temp;
            l++;
            r--;
            
        }
        
    }
    string ReverseSentence(string str) {
        int len=str.length();
        int mark=0;
        string temp;
        if(len==0)
            return "";
        str+=' ';
        for(int i=0;i<=len;i++)
            {
            if(str[i]==' ')
            {
               fun(str,mark,i-1);
                mark=i+1;
                
            }
        }
        str=str.substr(0,len);
        fun(str,0,len-1);
        return str;
    }
};

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的运气如何。为了方便起见,你可以认为大小王是0。

思路:

这一题比较有意思,就是用的矩阵存储相关的数值次数,其实用的还不是很熟练。不过感觉多做做会好。

感悟:学到的东西还是比较多的,一个就是用max,min,分别赋值最小最大然后求的相反的效果。

代码:

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {

        if(numbers.empty())return false;
        int max=-1;
        int min=14;
        int count[14]={0};
        int len=numbers.size();
        for(int i=0;i<len;i++)
            {
            count[numbers[i]]++;
            if(numbers[i]==0) continue;
            if(count[numbers[i]]>1)return false;
            if(numbers[i]>max) max=numbers[i];
            if(numbers[i]<min) min=numbers[i];

            
        }
        if((max-min)<5)
            return true;
        return false;
    }

};


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

题目描述:

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

思路:

这一题没怎么理解明白,看到一个比较神的代码就抄过来了,罪过。

代码:

class Solution {
public:
    int LastRemaining_Solution(int n, int m)

    {
        
        if(n==0) return -1;
        int s=0;
        for(int i=2;i<=n;i++)
            {
            s=(s+m)%i;
        }
        return s;
    }

};


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

题目描述:

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

思路:

这一题拿到手感觉还是比较新奇的,一般计算这种题目无非就是,传统的计算需要循环,然而这里不允许,后来想到借助递归来实现,但是递归会有一个问题,最终的终止条件如何判断。因此需要使用&& 或者||的条件为真为假,来使最终的运行终止。不容易。

代码:

class Solution {
public:
    int Sum_Solution(int n) {
        int sum=n;
        sum && (sum+=Sum_Solution(n-1));
        
        return sum;
        
        
    }
};

48. 不用加减乘除做加法

题目描述:

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

思路:

很神奇的一道题目。

感悟:

二进制的异或相当于十进制的相加。

二进制计算进位值,与操作然后左移一位  相当于十进制的求进位。



代码:

class Solution {
public:
    int Add(int num1, int num2)
    {
//不允许用加减乘除,无非就是就是用与非等逻辑语句。
        return num2?Add(num1^num2,(num1&num2)<<1):num1;
    }

};


49. 把字符串转换成整数

题目描述:

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

思路:

很神奇的一道题目。总之也是应用二进制的方法进行计算相关的数值

感悟:

比较好玩的就是用二进制进行乘除的操作   。

(res << 1) + (res << 3) = res * 2 + res * 8 = res * 10 。 字符'0'到'9'的ascii值的低4个二进制位刚好就是0到9所以str[i]&0xf等于str[i]-'0'。 位运算会比乘法运算效率高那么一点点点点...

代码:

class Solution {
public:
    int StrToInt(string str) {
        int n=str.size();
        int s=1;
        long long res=0;
        if(!n) return 0;
        if(str[0]=='-')s=-1;
        for(int i=(str[0]=='-' || str[0]=='+')?1:0;i<n;++i)
            {
            if(!('0'<=str[i] && str[i]<='9'))
                return 0;
            res=(res<<1)+(res<<3)+(str[i]& 0xf);
        }
        return res* s;
        
    }

};

50. 数组中重复的数字

题目描述:

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

思路:

这一题思路还是很清晰就是用哈希表,哈希表说白了就是一个一对一的数组。但是在说题目简单的同时不得不感叹自己的基础薄弱。

感悟:

一个就是数组的初始化,开始想了好多开辟数组的方法最后看到别人的,也是服。int cuncu[255]={0};直接就是一个0数组,简洁明了。

还有一个就是判断存储的数组里面有没有重复的,自己想复杂了,只要有一个重复的存在就行。使用一个count赋值到duplication就行。

还有一个基础薄弱的地方就是使用>=都能用错,也是服了。

代码:

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    bool duplicate(int numbers[], int length, int* duplication) {
        if(length==0 ||numbers==NULL) return false;
        int cuncu[255]={0};   //md 动态规划也忘了
        for(int i=0;i<length;i++)
            {
            cuncu[numbers[i]]++;
            
        }
        int count=0;
        for(int i=0;i<length;i++)
            {
            if(cuncu[numbers[i]]>=2)
               {
                duplication[count++]=numbers[i];
                return true;
            }
                
        }
        return false;
            
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值