zoukankan      html  css  js  c++  java
  • 剑指offer第五章

    剑指offer第五章

    1.数组中出现次数超过一半的数

    数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
    例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
    分析:
    思路1:数组排序,排序之后中间的数字一定是出现次数超过数组长度一半的数字,也就是统计学上的中位数,即长度为n的数组中第n/2大的数字(数组中任意第k大的数字)
     1 class Solution {
     2 public:
     3     int MoreThanHalfNum_Solution(vector<int> numbers) 
     4     {
     5         int length=numbers.size();
     6         int ans=0;
     7         if(numbers.empty())
     8             return 0;
     9         sort(numbers.begin(),numbers.end());//排序
    10         int midNum=numbers[length/2];
    11         
    12         int count=0;//统计次数初始化
    13         for(int i=0;i<length;i++)
    14         {
    15             if(numbers[i]==midNum)
    16                 ++count;//次数统计
    17         }
    18         if(count>length/2)//进行判断,是否为要求
    19             ans=midNum;
    20         return ans;
    21     }
    22 };
    思路二:如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。 
    在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。
     1 class Solution {
     2 public:
     3     int MoreThanHalfNum_Solution(vector<int> numbers) 
     4     {
     5         if(numbers.empty()) return 0;
     6         
     7         // 遍历每个元素,并记录次数;若与前一个元素相同,则次数加1,否则次数减1
     8         int result = numbers[0]; 
     9         int times = 1; // 次数
    10         
    11         for(int i=1;i<numbers.size();++i)
    12         {
    13             if(times == 0)
    14             {
    15                 // 更新result的值为当前元素,并置次数为1
    16                 result = numbers[i];
    17                 times = 1;
    18             }
    19             else if(numbers[i] == result)
    20             {
    21                 ++times; // 相同则加1
    22             }
    23             else
    24             {
    25                 --times; // 不同则减1                
    26             }
    27         }
    28         
    29         // 判断result是否符合条件,即出现次数大于数组长度的一半
    30         times = 0;
    31         for(int i=0;i<numbers.size();++i)
    32         {
    33             if(numbers[i] == result) ++times;
    34         }
    35         
    36         return (times > numbers.size()/2) ? result : 0;
    37     }

    2.最小的k个数

    输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
    分析:最简单的思路:把输入的n个整数排序,取最前面的k个数,时间复杂度O(nlogn)
     1 class Solution {
     2 public:
     3     vector<int> GetLeastNumbers_Solution(vector<int> input, int k) 
     4     {
     5         int length=input.size();
     6         vector<int> ans;
     7         if(input.empty()||k>length)
     8             return ans;
     9         sort(input.begin(),input.end());
    10         for(int i=0;i<k;i++)
    11             ans.push_back(input[i]);
    12         return ans;
    13     }
    14 };

    3.连续子数组的最大和

    HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)
    分析:最直观的方法,即枚举出数组的所有子数组并求出他们的和。一个长度为n的数组,总共有n(n+1)/2个子数组,最快也需要O(n^2),时间复杂度比较高
    代码采用下图所示方法:
     1 class Solution {
     2 public:
     3     int FindGreatestSumOfSubArray(vector<int> array) 
     4     {
     5         if (array.empty())
     6             return 0;
     7         int temp1 = array[0], max = array[0];
     8         for (int i = 1; i < array.size(); i++)
     9         {
    10             if (temp1 + array[i] > array[i])
    11                 temp1 += array[i];
    12             else
    13                 temp1 = array[i];
    14             if (temp1 > max)
    15                 max = temp1;
    16         }
    17         return max;
    18         
    19     }
    20 };

     4.从1到n整数1出现的次数

    求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。
    希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
    分析:最直观的方法,就是累加1到n中每个整数1出现的次数。可以每次通过对10求余数判断整数的各位数字是不是1.如果这个数字大于10,除以10之后再判断各位数字是不是1
     1 class Solution {
     2 public:
     3     int numberOf1(int n)
     4     {
     5         int number=0;
     6         while(n)
     7         {
     8             if(n%10==1)
     9             {
    10                 number++;
    11             }
    12             n=n/10;
    13         }
    14         return number;
    15     }
    16     int NumberOf1Between1AndN_Solution(int n)
    17     {
    18         int number=0;//参数初始化
    19         for(int i=1;i<=n;++i)
    20             number+=numberOf1(i);
    21         return number;
    22     }
    23 };

     5.把数组排成最小的数

    输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
    分析:先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。关键就是制定排序规则。
    对vector容器内的数据进行排序,按照 将a和b转为string后若 a+b<b+a  a排在在前 的规则排序, 如 2  21 因为 212 < 221 所以 排序后为 21  2    to_string() 可以将int 转化为string
     
     1 class Solution {
     2 public:
     3     static bool cmp(int a,int b){
     4         string A="";
     5         string B="";
     6         A+=to_string(a);
     7         A+=to_string(b);
     8         B+=to_string(b);
     9         B+=to_string(a);
    10           
    11         return A<B;
    12     }
    13     string PrintMinNumber(vector<int> numbers) {
    14         string  answer="";
    15         sort(numbers.begin(),numbers.end(),cmp);
    16         for(int i=0;i<numbers.size();i++){
    17             answer+=to_string(numbers[i]);
    18         }
    19         return answer;
    20     }
    21 };

     6.丑数

    把只包含因子<spanlang="en-us>">2、<spanlang="en-us>">3和<spanlang="en-us>">5的数称作丑数(<spanlang="en-us>">Ugly Number)。例如<spanlang="en-us>">6、<spanlang="en-us>">8都是丑数,但<spanlang="en-us>">14不是,因为它包含因子<spanlang="en-us>">7。习惯上我们把<spanlang="en-us>">1当做是第一个丑数。求按从小到大的顺序的第<spanlang="en-us>">N个丑数。
    分析:所谓一个数m是另一个数n的因子,是指n能被m整除,也就是n%m==0
    思路1:尴尬  牛客网超时
    根据丑数的定义,丑数只能被2,3,5整除。也就是说如果一个数能被2整除,我们把它连续除以2;如果能被3整除,连续除以3;如果能被5整除,连续除以5.如果最后得到的是1,则就是丑数
     
     1 class Solution {
     2 public:
     3     bool IsUglyNumber(int number)
     4     {
     5         while(number%2==0)
     6             number/=2;
     7         while(number%3==0)
     8             number/=3;
     9         while(number%5==0)
    10             number/=5;
    11         if(number==1)
    12             return true;
    13         else
    14             return false;
    15     }
    16     int GetUglyNumber_Solution(int index) 
    17     {
    18         if(index<=0)
    19             return 0;
    20         int number=0;
    21         int uglyFound=0;
    22         while(uglyFound<index)
    23         {
    24             ++number;
    25             if(IsUglyNumber(number))
    26             {
    27                 ++uglyFound;
    28             }
    29         }
    30         return number;
    31     }
    32 };

    思路2:创建数组保存已经找到的丑数,用空间换时间

    根据丑数的定义, 丑数应该是另一个丑数乘以2、3或者5的结果(1除外)  。因此我们可以创建一个数组,里面的数字是排好序的丑数,每一个丑数都是前面的丑数乘以2、3或者5得到的。  
    假设数组中已经有若干个丑数排好序后放在数组中,并且把 已有最大的丑数记做M  ,我们接下来分析如何生成下一个丑数。该丑数肯定是前面某一个丑数乘以2、3或者5的结果,所以我们首先考虑把已有的每个丑数乘以2。在乘以2的时候,可以得到若干个小于等于M的结果。由于是按顺序生成的,小于或者等于M肯定已经在数组中了,我们不需要再考虑;还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是按照从小到大的顺序生成的,其他更大的结果以后再说。我们把得到的第一个乘以2后大于M的丑数记为M2。同样,我们把已有的每一个丑数乘以3和5,能得到第一个大于M的结果M3和M5。那么下一个丑数应该是M2、M3和M5这三个数的最小者了 。  
    前面分析的时候,提到把已有的丑数分别乘以2、3和5。事实上这不是必须的,因为已有的丑数是按照顺序放在数组中的。对乘以2而言,肯定存在某一个丑数T2,排在它之前的每一个乘以2得到的结果都会小于已有最大的丑数,在它之后的每一个丑数乘以2得到的结果都会太大。我们只需要记下这个丑数的位置,同时每次生成新的丑数的时候,取更新这个这个T2.对于乘以3和5而言,也存在着同样的T3和T5。  
     
     1 class Solution {
     2 public:
     3     int GetUglyNumber_Solution(int index) {
     4       if(index<=0)
     5           return 0;
     6     int *pUglyNumber=new int[index];
     7     pUglyNumber[0]=1;
     8     int NextUglyIndex=1;
     9     int *pMutiply2=pUglyNumber;
    10     int *pMutiply3=pUglyNumber;
    11     int *pMutiply5=pUglyNumber;
    12     
    13     while(NextUglyIndex<index)
    14         {
    15           int min=Min(*pMutiply2*2,*pMutiply3*3,*pMutiply5*5);
    16           pUglyNumber[NextUglyIndex]=min;
    17           while(*pMutiply2*2<=pUglyNumber[NextUglyIndex])  //当前最大丑数pUglyNumber[NextUglyIndex]
    18               pMutiply2++;
    19           while(*pMutiply3*3<=pUglyNumber[NextUglyIndex])
    20               pMutiply3++;
    21           while(*pMutiply5*5<=pUglyNumber[NextUglyIndex])
    22               pMutiply5++;  
    23           NextUglyIndex++;
    24         }
    25     int ugly=pUglyNumber[index-1];
    26     delete[]pUglyNumber;
    27     return ugly;
    28     }
    29     
    30     int Min(int a,int b,int c)
    31       {
    32         int min=a; 
    33         if(min>b)
    34             min=b;
    35         if(min>c)
    36             min=c;
    37         return min;
    38       }
    39 };

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

    在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置 
    分析:先在hash表中统计各字母出现次数,第二次扫描直接访问hash表获得次数
     1 class Solution {
     2 public:
     3     int FirstNotRepeatingChar(string str) 
     4     {
     5       if ( str.length() == 0)
     6             return -1;
     7         unsigned int hashTime[256] = {0};
     8         for(int i =0;i<str.length();++i)
     9             hashTime[str[i]]++;
    10         
    11         for(int i =0;i<str.length();++i)
    12         {
    13             if(hashTime[str[i]] == 1)
    14                 return i;
    15         }
    16         return -1;
    17     }
    18 };

    8.数组中的逆序对

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
    输入描述:
    题目保证输入的数组中没有的相同的数字
    数据范围:
    对于%50的数据,size<=10^4
    对于%75的数据,size<=10^5
    对于%100的数据,size<=2*10^5
    示例1 
    输入
    1,2,3,4,5,6,7,0
    输出
    7
    分析:
    思路一:直接暴力        会超时
     1 class Solution {
     2 public:
     3     int InversePairs(vector<int> d) {
     4         int r = 0;
     5         for(int i = 0; i < d.size(); ++i){
     6             for(int j = 0; j < i; ++j) if(d[j] > d[i]) ++r;
     7         }
     8         return r;
     9     }
    10 };
    思路二:基于归并的思想
    先把数组分隔成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序,实际上就是归并排序。
     
     1 class Solution {
     2 public:
     3     int InversePairs(vector<int> data) 
     4     {
     5       int length=data.size();
     6         if(length<=0)
     7             return 0;
     8       vector<int> copy;
     9       for(int i=0;i<length;i++)
    10           copy.push_back(data[i]);
    11       long long count=InversePairsCore(data,copy,0,length-1);
    12       return count%1000000007;
    13     }
    14     long long InversePairsCore(vector<int> &data,vector<int> &copy,int start,int end)
    15     {
    16       if(start==end)
    17           {
    18             copy[start]=data[start];
    19             return 0;
    20           }
    21       int length=(end-start)/2;
    22       long long left=InversePairsCore(copy,data,start,start+length);
    23       long long right=InversePairsCore(copy,data,start+length+1,end);  
    24       int i=start+length;
    25       int j=end;
    26       int indexcopy=end; 
    27       long long count=0;
    28       while(i>=start&&j>=start+length+1)
    29           {
    30             if(data[i]>data[j])
    31                 { 
    32                   copy[indexcopy--]=data[i--]; 
    33                   count=count+j-start-length;          //count=count+j-(start+length+1)+1;
    34                 }
    35             else
    36                 { 
    37                   copy[indexcopy--]=data[j--]; 
    38                 }
    39           }
    40       for(;i>=start;i--)
    41           copy[indexcopy--]=data[i]; 
    42       for(;j>=start+length+1;j--)
    43           copy[indexcopy--]=data[j];
    44       return left+right+count;
    45     }
    46 };

     9.两个链表的第一个公共点

    输入两个链表,找出他们的第一个公共结点
    分析:用两个指针扫描”两个链表“,最终两个指针到达 null 或者到达公共结点。
     1 /*
     2 struct ListNode {
     3     int val;
     4     struct ListNode *next;
     5     ListNode(int x) :
     6             val(x), next(NULL) {
     7     }
     8 };*/
     9 class Solution {
    10 public:
    11     ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
    12         ListNode *p1 = pHead1;
    13         ListNode *p2 = pHead2;
    14         while(p1!=p2)
    15         {
    16             if(p1==NULL)
    17                 p1=pHead2;
    18             else
    19                 p1=p1->next;
    20             if(p2==NULL)
    21                 p2=pHead1;
    22             else
    23                 p2=p2->next;
    24         }
    25         return p1;
    26     }
    27 };
  • 相关阅读:
    c# 反射取其他项目的资源文件
    【分享】免费建立自己的站点
    c# 自定义类型的DataBindings
    ListView 多行拖拽排序
    linq to sql之组装where条件下的'或'语句
    dotfuscator使用方法
    orderBy 传入属性的字符串
    WCF数据交互时长度超过8192
    ASP.net中aspx与cs函数的互调
    c# 读取excel数据的两种方法
  • 原文地址:https://www.cnblogs.com/lxt1105/p/7412209.html
Copyright © 2011-2022 走看看