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

    剑指offer第六章

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

    统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在数组中出现了4次,所以输出4
    分析:思路1:直官思路:直接顺序扫描,O(n)
     1 class Solution {
     2 public:
     3     int GetNumberOfK(vector<int> data ,int k) 
     4     {
     5         int count=0;
     6         for(int i=0;i<data.size();i++)
     7         {
     8             if(data[i]==k)
     9                 count++;
    10         }
    11         return count;
    12 
    13     }
    14 };
    思路2:因为是排序数组,所以想到二分查找,关键在于如何用二分查找找到第一个和最后一个k
    找第一个k:先拿数组中间的数和k做比较,中间数字比k大,那么k只能出现在前半段,中间比k小,那么k只能出现在后半段,如果相等,先判断是不是第一个k如果中间数字的前一个不是k,则找到第一个k。如果前一个也是k,也就是说第一个k肯定在数组前半段,下一轮继续在前半段查找。
    找最后一个k:同上
     1 class Solution {
     2     /*二分查找 找到第一个K 和 最后一个K 二者位置相减*/
     3 public:
     4     int GetNumberOfK(vector<int> data ,int k) {
     5         if(data.empty())
     6             return 0;
     7         int number = 0;
     8         int first = GetFirstIndex(data,k,0,data.size()-1);
     9         int last = GetLastIndex(data,k,0,data.size()-1);
    10         if(first>-1 && last>-1)
    11             number = last - first +1;
    12         return number;
    13         
    14     }
    15     int GetFirstIndex(vector<int> &data,int k,int start,int end){
    16         if(start > end)
    17             return -1;
    18         int mid = start+(end-start)/2;
    19         if(data[mid] == k){
    20             if((mid == start) || (data[mid-1] != k))
    21                 return mid;
    22             else
    23                 end = mid-1;
    24         }
    25         else{
    26             if(data[mid] > k)
    27                 end = mid - 1;
    28             else
    29                 start = mid + 1;
    30         }
    31         return GetFirstIndex(data,k,start,end);
    32     }
    33     int GetLastIndex(vector<int> &data,int k,int start,int end){
    34         if(start > end)
    35             return -1;
    36         int mid = start+(end-start)/2;
    37         if(data[mid]==k){
    38             if((mid == end) || (data[mid+1] != k))
    39                 return mid;
    40             else
    41                 start = mid +1;
    42         }
    43         else{
    44             if(data[mid]>k)
    45                 end = mid-1;
    46             else
    47                 start = mid+1;
    48         }
    49         return GetLastIndex(data,k,start,end);
    50     }
    51 };

    2.二叉树的深度

    输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
    分析:递归
     1 /*
     2 struct TreeNode {
     3     int val;
     4     struct TreeNode *left;
     5     struct TreeNode *right;
     6     TreeNode(int x) :
     7             val(x), left(NULL), right(NULL) {
     8     }
     9 };*/
    10 class Solution {
    11 public:
    12     int TreeDepth(TreeNode* pRoot)
    13     {
    14         if(pRoot==NULL)
    15             return 0;
    16         int nLeft=TreeDepth(pRoot->left);
    17         int nRight=TreeDepth(pRoot->right);
    18         int depth=0;
    19         if(nLeft>nRight)
    20             depth=nLeft+1;
    21         else
    22             depth=nRight+1;
    23         return depth;
    24     }
    25 };

    3.平衡二叉树

    输入一颗二叉树,判断该树是不是平衡二叉树。
    如果某二叉树中任意结点的左右子树的深度相差不超过1,那么他就是一颗平衡二叉树
    分析:容易想到的思路:在遍历树的每个结点的时候,调用函数TreeDepth得到他的左右子树的深度,如果每个结点的左右子树的深度相差不超过1,则就是平衡二叉树
     1 class Solution {
     2 public:
     3     bool IsBalanced_Solution(TreeNode* pRoot) 
     4     {
     5         if(pRoot==NULL)
     6             return true;
     7         int left=TreeDepth(pRoot->left);
     8         int right=TreeDepth(pRoot->right);
     9         int diff=left-right;
    10         if(diff>1||diff<-1)
    11             return false;
    12         return IsBalanced_Solution(pRoot->left)&&IsBalanced_Solution(pRoot->right);
    13     }
    14     int TreeDepth(TreeNode* pRoot)
    15     {
    16         if(pRoot==NULL)
    17             return 0;
    18         int nLeft=TreeDepth(pRoot->left);
    19         int nRight=TreeDepth(pRoot->right);
    20         int depth=0;
    21         if(nLeft>nRight)
    22             depth=nLeft+1;
    23         else
    24             depth=nRight+1;
    25         return depth;
    26     }
    27 };

    4.数组中只出现一次的数字

    一个整型数组里除了两个数字之外,其他的数字都只出现了两次,找出这两个数字
    要求:时间复杂度是O(n),空间复杂度O(1)
    分析:首先:位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
     两个相同数字异或=0,一个数和0异或还是它本身。
    当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
    依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,记为第n位,接着把原数组分成两组,分组标准是第n位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
     1 class Solution {
     2 public:
     3     void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) 
     4     {
     5         int length=data.size();
     6         if(length<2)
     7             return;
     8         if(length==2)
     9         {
    10               num1[0] = data[0];
    11               num2[0] = data[1];
    12               return;
    13         }
    14       int bitResult = 0;
    15         for(int i = 0; i < length; ++i)
    16         {
    17             bitResult ^= data[i];
    18         }
    19         int index = findFirst1(bitResult);
    20         *num1=*num2=0;
    21         for(int i = 0; i < length; ++i)
    22         {
    23             if(isBit1(data[i], index))
    24             {
    25                 *num1 ^= data[i];
    26             }
    27             else
    28             {
    29                 *num2 ^= data[i];
    30             }
    31         }
    32     }
    33     int findFirst1(int num)
    34     {
    35         int index=0;
    36         while(((num&1)==0)&&(index<8*sizeof(int)))
    37         {
    38             num=num>>1;
    39             ++index;
    40         }
    41         return index;
    42     }
    43     bool isBit1(int num,int index)
    44     {
    45         num=num>>index;
    46         return (num&1);
    47     }
    48 };

     5.和为s的两个数字

    输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
    分析:先在数组中选择两个数字,如果他们的和等于S,则找到;如果小于S,选择较小的数字后面的数字;如果大于S,选择较大数字前面的数字。因为数组已经是有序的。
     1 class Solution {
     2 public:
     3     vector<int> FindNumbersWithSum(vector<int> array,int sum)
     4     {
     5         int length=array.size();
     6         vector<int> res;
     7         int ahead=length-1;
     8         int behind=0;
     9         while(ahead>behind)
    10         {
    11             long long curSum=array[ahead]+array[behind];
    12             if(curSum==sum)
    13             {
    14                 res.push_back(array[behind]);
    15                 res.push_back(array[ahead]);
    16                 break;
    17             }
    18             else if(curSum>sum)
    19             {
    20                 ahead--;
    21             }
    22             else
    23             {
    24                 behind++;
    25             }
    26         }
    27         return res;
    28     }
    29 };

     6.和为s的连续正数序列

    输入一个正数S,打印出所有和为S的连续正数序列(至少含有两个数)
    分析:首先考虑用两个数small和big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。如果从small到big的序列和大于S,则去掉序列中较小的值,也就是增加small的值;如果小于S,增大big,让序列包含更多的数字,一直增加到small为(1+S)/2为止
     1 class Solution {
     2 public:
     3     vector<vector<int> > FindContinuousSequence(int sum)
     4     {
     5         vector<vector<int> > res;
     6         if(sum<3)
     7             return res;
     8         int small=1;
     9         int big=2;
    10         int middle=(1+sum)/2;
    11         int curSum=small+big;
    12         while(small<middle&&big<sum)
    13         {
    14             while(curSum > sum)
    15             {
    16                 curSum -= small;
    17                 small++;
    18             }
    19             if(curSum==sum)
    20             {
    21                 InsertRes(small,big,res);
    22             }
    23             big++;
    24             curSum+=big;
    25         }
    26         return res;
    27     }
    28     void InsertRes(int small,int big,vector<vector<int> > &res)
    29     {
    30         vector<int> temp;
    31         for(int i = small;i<=big;i++)
    32             temp.push_back(i);
    33         res.push_back(temp);
    34     }
    35 };

     7.反转单词序列

    输入一个英文句子,反转句子中单词的顺序,但单词内字符的顺序不变,标点符号和普通字母一样处理
    分析:第一步翻转句子中所有字符;第二步再翻转每个单词中字符的顺序
     1 class Solution {
     2 public:
     3     string ReverseSentence(string str) 
     4     {
     5         ReverseWord(str,0,str.size()-1);//先整体翻转
     6         int start=0,end=0;
     7         int i=0;
     8         int length=str.size();
     9         while(i<length)
    10         {
    11             while(i<length&&str[i]==' ')//空格跳过
    12             {
    13                 i++;
    14             }
    15             start=end=i;
    16             while(i<length&&str[i]!=' ')
    17             {
    18                 i++;
    19                 end++;
    20             }
    21             ReverseWord(str,start,end-1);
    22         }
    23         return str;
    24     }
    25     void ReverseWord(string &str, int start, int end)
    26     {
    27         while(start < end)
    28         {
    29             swap(str[start++], str[end--]);
    30         }
    31     }
    32 };

     8.左旋转字符串

    汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它
    分析:三次翻转,先翻转前n个部分,再翻转后一部分,再翻转整体
     1 class Solution {
     2 public:
     3     string LeftRotateString(string str, int n)
     4     {
     5       int length = str.size();
     6         if(length<=0)
     7             return str;
     8         if(length>0&& n>0&&n<length)
     9         {
    10             int firstStart = 0;
    11             int firstEnd = n-1;
    12             int secondStart = n;
    13             int secondEnd = length-1;
    14             Reverse(str,firstStart,firstEnd);
    15             Reverse(str,secondStart,secondEnd);
    16             Reverse(str,firstStart,secondEnd);
    17         }
    18     return str;
    19     }
    20   void Reverse(string &str, int start, int end)
    21     {
    22         while(start < end)
    23         {
    24             swap(str[start++], str[end--]);
    25         }
    26     }
    27 };

     9.扑克牌的顺子

    从扑克牌中随机抽取5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以为任意数字。
    分析:首先把数组排序,在统计数组中0的个数,最后统计排序之后的数组中相邻数字之间的空缺总数。如果空缺总数小于或者等于0的个数,那么这个数组就是连续的;反之则不连续
    注意:如果非零数字重复出现,则不连续
     1 class Solution {
     2 public:
     3     bool IsContinuous( vector<int> numbers ) {
     4         if(numbers.size()!=5) 
     5             return false;
     6         sort(numbers.begin(),numbers.end());
     7         int i=0;
     8         while(numbers[i]==0) 
     9             i++;
    10         if(numbers[4]-numbers[i]>4) 
    11             return false;
    12         for(int j=i;j<4;j++){
    13             if(numbers[j]==numbers[j+1]) 
    14                 return false;
    15         }
    16         return true;
    17     }
    18 };

     10.圆圈中最后剩下的数字

    0,1,...,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求这个圆圈里剩下的最后一个数字。
    分析:两种解法:
    解法一:经典解法,用环形链表模拟圆圈
     1 class Solution {
     2 public:
     3     int LastRemaining_Solution(int n, int m)
     4     {
     5         if(n<1||m<1)
     6             return -1;
     7         int i=0;
     8         list<int> numbers;
     9         for(i=0;i<n;++i)
    10             numbers.push_back(i);
    11         list<int>::iterator current=numbers.begin();//开始点
    12         while(numbers.size()>1)
    13         {
    14             for(int i=1;i<m;++i)
    15             {
    16                 current++;//指针后移
    17                 if(current==numbers.end())
    18                     current=numbers.begin();
    19             }
    20             list<int>::iterator next=++current;
    21             if(next==numbers.end())
    22                 next=numbers.begin();
    23             --current;
    24             numbers.erase(current);
    25             current=next;
    26         }
    27         return *(current);
    28     }
    29 };
    解法二:创新解法:找到数学规律
                            递推公式    f[1]=0; 
                                                f[i]=(f[i-1]+m)%i;  (i>1) 
     1 class Solution {
     2 public:
     3     int LastRemaining_Solution(unsigned int n, unsigned int m)
     4     {
     5         if(n==0)
     6             return -1;
     7         if(n==1)
     8             return 0;
     9         else
    10             return (LastRemaining_Solution(n-1,m)+m)%n;
    11     }
    12 };

     11.求1+2+3+...+n

    求1+2+3+...+n要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句
    分析:解题思路:
    1.需利用逻辑与的短路特性实现递归终止。
    2.当n==0时,sum&&((sum+=Sum_Solution(n-1)))只执行前面的判断,为false,然后直接返回0;
    3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。
    1 class Solution {
    2 public:
    3     int Sum_Solution(int n) 
    4     {
    5       int sum = n;
    6         sum&&((sum+=Sum_Solution(n-1)));
    7       return sum;
    8     }
    9 };

     12.不用加减乘除做加法

    写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
    分析:用位运算代替二进制加法
    第一步不考虑进位对每一位相加==》采用异或
    接着考虑第二步进位,可以想象为是两个数先做位与运算,然后再向左移动一位
    第三步把前两个步骤结果相加。第三步相加的过程依然是重复前两步,直到不产生进位为止
     1 class Solution {
     2 public:
     3     int Add(int num1, int num2)
     4     {
     5         int sum,carry;
     6         do{
     7             sum=num1^num2;
     8             carry=(num1&num2)<<1;
     9             num1=sum;
    10             num2=carry;
    11         }while(num2!=0);
    12         return num1;
    13     }
    14 };
  • 相关阅读:
    C++ 函数返回局部变量的std::move()的适用场景(转)
    Android 内存泄漏总结(转)
    android JNI调用(Android Studio 3.0.1)(转)
    Linux c —— opendir函数和readdir函数内涵及用法(转)
    理解Android编译命令(转)
    Linux内存管理(转)
    Android内存分析命令(转)
    关于跳板机穿透的文章 (未验证)
    windows下 git配置ssh
    转: Git远程操作详解 (阮一峰)
  • 原文地址:https://www.cnblogs.com/lxt1105/p/7412379.html
Copyright © 2011-2022 走看看