美文网首页
剑指offer题目练习(四)

剑指offer题目练习(四)

作者: MichealXXX | 来源:发表于2020-05-29 12:12 被阅读0次
    题目三十一

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

    思路:我们可以维护一个丑数数组,因为1是最小所以先将一放进去,丑数的因子一定包含2,3,5因此,我们可以设置三个队列Q2,Q3,Q5,分别装因子含2,3,5的丑数,比如现在最小丑数是1,那么就让1分别乘以2,3,5,将这三个数分别装入三个队列,然后将三个队列中最小的那一个,加入丑数数组中,接下来2再分别乘以2,3,5以此类推,但是在编程的时候,无须创建队列,使用指针模拟即可。

    int GetUglyNumber_Solution(int index) {
            if (index < 7){
                return index;
            }
            //初始化一个index大小的数组
            vector<int>res(index);
            res[0] = 1;
            int p2 = 0;
            int p3 = 0;
            int p5 = 0;
            for (int i = 1;i < index;i++){
                res[i] = min(res[p2]*2,min(res[p3]*3,res[p5]*5));
                if (res[i] == res[p2]*2){
                    p2++;
                }
                if (res[i] == res[p3]*3){
                    p3++;
                }
                if (res[i] == res[p5]*5){
                    p5++;
                }
            }
            return res[index-1];
        }
    
    题目三十二

    在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

    思路:使用哈希表记录每个字符出现的位置即可

    int FirstNotRepeatingChar(string str) {
            map<char,int>mp;
            if (str.size() <= 0){
                return -1;
            }
            for (int i = 0;i < str.size();i++){
                mp[str[i]]++;
            }
            
            int res = -1;
            
            for (int i = 0;i < str.size();i++){
                if (mp[str[i]] == 1){
                    return i;
                }
            }
            
            return res;
        }
    
    题目三十三

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

    思路:这道题比较难,推荐去书上结合图片看解答

    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;
       }
    };
    

    题目三十四
    输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

    思路:题目说两个链表的第一个公共节点,说明两个链表在某个节点之后合并了,形成了Y型,我们可以统计两个链表的长度,长的一方先走多出的部分,然后两个一起走。

    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
            if (pHead1 == nullptr || pHead2 == nullptr){
                return nullptr;
            }
            
            ListNode *p1 = pHead1;
            ListNode *p2 = pHead2;
            int len1 = 0;
            int len2 = 0;
            while (p1){
                p1 = p1->next;
                len1++;
            }
            while (p2){
                p2 = p2->next;
                len2++;
            }
            int diff = 0;
            if (len1 > len2){
                diff = len1-len2;
                p1 = pHead1;
                p2 = pHead2;
            }else{
                diff = len2-len1;
                p1 = pHead2;
                p2 = pHead1;
            }
            
            for (int i = 0;i < diff;i++){
                p1 = p1->next;
            }
            while (p1 != nullptr && p2!= nullptr){
                if (p1->val == p2->val){
                    return p1;
                }
                p1 = p1->next;
                p2 = p2->next;
            }
            
            return nullptr;
        }
    
    题目三十五

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

    思路:注意是排序整数数组,所以统计次数的问题可以变成这个数字+0.5所在的位置减去这个数字-0.5所在的位置就是次数。

    class Solution {
    public:
        int GetNumberOfK(vector<int> data ,int k) {
            return numberIndexAtArray(data,k+0.5) - numberIndexAtArray(data,k-0.5);
        }
        
        int numberIndexAtArray(vector<int> data,double num){
            int start = 0;
            int end = data.size()-1;
            while (start <= end){
                int mid = (end-start)/2+start;
                if (data[mid] > num){
                    end = mid-1;;
                }else{
                    start = mid+1;
                }
            }
            return start;
        }
    };
    
    题目三十六

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

    思路:递归即可

    int TreeDepth(TreeNode* pRoot)
        {
            if (pRoot == nullptr){
                return 0;
            }
            return max(TreeDepth(pRoot->left)+1,TreeDepth(pRoot->right)+1);
        }
    
    题目三十七

    输入一棵二叉树,判断该二叉树是否是平衡二叉树。在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树

    思路:平衡二叉树的概念是任意节点的子树的高度差都小于等于1,我们可以使用递归来判断每个节点的子树高度差值。

    class Solution {
    public:
        bool IsBalanced_Solution(TreeNode* pRoot) {
            if (pRoot == nullptr){
                return true;
            }
            int left = treeDeep(pRoot->left);
            int right = treeDeep(pRoot->right);
            int diff = 0;
            if (left-right > 1 || left-right < -1){
                return false;
            }
            return IsBalanced_Solution(pRoot->left)&&IsBalanced_Solution(pRoot->right);
        }
        
        int treeDeep(TreeNode *pRoot){
            if (pRoot == nullptr){
                return 0;
            }
            int left = treeDeep(pRoot->left);
            int right = treeDeep(pRoot->right);
            
            return max(left+1,right+1);
        }
    };
    
    题目三十八

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

    思路:使用异或运算,两个相同数字的异或运算等于0,数组中的数字依次与下一个异或,如果最终为0,那么肯定都是成对出现,否则结果肯定是两个只出现一次的数字的异或值,通过判断这个异或值二进制中1的位置来将数组分割成两个,再从这两个数组中使用异或找出只出现一次的数字。

    class Solution {
    public:
        void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
            if (data.size() < 2){
                return;
            }
            int temp = 0;
            for (int i = 0;i < data.size();i++){
                temp = temp^data[i];
            }
            if (temp == 0){
                return;
            }
            
            int count = 0;
            //找出temp二进制1的位置
            while ((temp & 1) == 0){
                temp = temp>>1;
                count++;
            }
            *num1 = *num2 = 0;
            for (int i = 0;i<data.size();i++){
                if (isNumber(data[i],count)){
                    *num1 ^= data[i];
                }else{
                    *num2 ^= data[i];
                }
            }
        }
        //判断数组中当前数字二进制中1的位置是否与temp中1的位置相等
        bool isNumber(int number,int index){
            number = number>>index;
            return (number&1);
        }
    };
    
    题目三十九

    计算出9~16的和,正确答案是100。有多少种连续的正数序列的和为100(至少包括两个数)。比如另一组连续正数和为100的序列:18,19,20,21,22,如何快的找出所有和为S的连续正数序列

    思路:可以使用双指针的方式,比如设置两个指针,分别指向一个区间内的第一个值和最后一个值,如果这个区间的值大于目标值,那么右移第一个指针,小于则右移第二个指针。

    vector<vector<int> > FindContinuousSequence(int sum) {
            vector<vector<int> >res;
            int pLow = 1;
            int pHigh = 2;
            while (pLow < pHigh){
                //区间内求和公式(a0+an)*n/2
                if ((pLow+pHigh)*(pHigh-pLow+1)/2 == sum){
                    vector<int> temp;
                    for (int i = pLow;i <= pHigh;i++){
                        temp.push_back(i);
                    }
                    res.push_back(temp);
                    pLow++;
                }else if ((pLow+pHigh)*(pHigh-pLow+1)/2 > sum){
                    pLow++;
                }else{
                    pHigh++;
                }
            }
            return res;
        }
    
    题目四十

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

    思路:依旧可以使用双指针方式,但是要求输出乘积最小的,所以在这个递增数组中我们的初始指针分别指向首尾,根据指向数字的和移动指针。

    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
            vector<int> res;
            if (array.size() < 2){
                return res;
            }
            int begin = 0;
            int end = array.size() - 1;
            int cursum = 0;
            while (begin <= end){
                cursum = array[begin]+array[end];
                if (cursum == sum){
                    res.push_back(array[begin]);
                    res.push_back(array[end]);
                    break;
                }else if (cursum < sum){
                    begin++;
                }else{
                    end--;
                }
            }
            return res;
        }
    

    相关文章

      网友评论

          本文标题:剑指offer题目练习(四)

          本文链接:https://www.haomeiwen.com/subject/klxxzhtx.html