【刷题】剑指OFFER 67道完整C++版

1.二维数组的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if(array.empty()) return false;
        
        int rows=array.size(),clos=array[0].size();
        int i=rows-1,j=0;
        
        while(i>=0 && j< clos){
            if(array[i][j] == target) return true;
            if (array[i][j]>target) i--;
            else j++;
                
        }
        return false;
    }
};

2.替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。


class Solution {
public:
   void replaceSpace(char *str,int length) {
       if (str == NULL || length == 0)
     return;
     int spaceNum=0;
     int i;
     for( i=0;str[i]!='\0';i++)
       {
        if (str[i] == ' ')
        spaceNum++;
       }
     int k =i+2*spaceNum;
     if (k > length)
     return;
     str[k]='\0';
     int p1= i-1;
     int p2= k-1;
     while(p1>=0 && p2>p1)
     {
       if(str[p1] == ' ')
       { 
        str[p2--]='0';
        str[p2--]='2';
        str[p2--]='%';
       }
       else
       {
       str[p2--]=str[p1];
       }
       p1--;
     } 
   }
};

3.从头打印链表

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。


/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        std::stack<int> nodes;
        vector<int>     outnodes;
        while(head != NULL)
        {
            nodes.push(head->val);
            head=head->next;
        }
        while(!nodes.empty())
        {
            outnodes.push_back(nodes.top());
            nodes.pop();
        }
        return outnodes;
    }
};

4.重建二叉树

输入某二叉树的前序遍历中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if ( pre.empty()|| vin.empty()) return NULL;
        int root_val=pre[0];
        TreeNode *root=new TreeNode(root_val);
        int leftLength=0;
        int rightLength=0;
        for (int i=0;i<vin.size();i++)
        {
            if (vin[i]==root_val)
            {
                leftLength=i;
                rightLength=vin.size()-i-1;
                break;
            }
        }
        vector<int> in_left,in_right,pre_left,pre_right;
        for(int j=0;j<leftLength;j++)
        {
            in_left.push_back(vin[j]);
            pre_left.push_back(pre[j+1]);
        }
        for(int j=0;j<rightLength;j++)
        {
            in_right.push_back(vin[leftLength+1+j]);
            pre_right.push_back(pre[1+leftLength+j]);
        }
        root->left=reConstructBinaryTree(pre_left,in_left);
        root->right=reConstructBinaryTree(pre_right,in_right);
        return root;
    }
};

5.用两个栈实现队列

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

class Solution
{
public:
    void push(int node) 
    {    
        stack1.push(node);
    }

    int pop()
    {
        int result;
              
        if(stack2.empty() && stack1.empty())
        {  
            printf("the quene is empty"); 
            return result;
        }
        else
        {
            if (stack2.empty())
            {
              while(!stack1.empty())
              {
                  stack2.push(stack1.top());
                  stack1.pop();
                }         
            }
         result=stack2.top();
         stack2.pop(); 
         return result; 
        }
     }
  
private:
    stack<int> stack1;
    stack<int> stack2;
};

6.旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int length=rotateArray.size();
        if (length==0) return 0;
        else if(length==1) return rotateArray[0];
        else {
          int left=0,right=length-1;
          int mid=0;
          while(rotateArray[left]>=rotateArray[right]) //确保旋转了
          {
              if(right-left==1)
              {
                  mid=right;
                  break;
              }
              mid=left+(right-left)/2;
              if(rotateArray[left]==rotateArray[right] && rotateArray[left])
              {
                  return MinOrder(rotateArray,left,right);
              }
              if(rotateArray[left]>rotateArray[mid])
              {
                  right=mid;
              }
              else
              {
                  left=mid;
              }
          }
            return rotateArray[mid];
        }
        
    }
private :
//顺序寻找最小值
    int MinOrder(vector<int> &num,int left ,int right){
        int result=num[left];
        for(int i=left+1;i<right;i++)
        {
            if(num[i]<result)
            {
                result=num[i];
            }
        }
        return result;
    }
};

7.斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39

class Solution {
public:
    int Fibonacci(int n) {
        int r1=1,r2=1;
        if(n <= 0)  return 0;
        if (n == 1 || n == 2) return 1;
        while(n-- > 2){
            r1+=r2;
            r2=r1-r2;
        }
        return r1;
    }
};

8.跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)

class Solution {
public:
    int jumpFloor(int number) {
       if (number<0)  return -1;
       if (number ==1 || number ==2) return number;
       else return jumpFloor(number-1)+jumpFloor(number-2); 
    }
};

9.变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

class Solution {
public:
    int jumpFloorII(int number) {
        int a=1,b=2;
        if (number < 0) return 0;
        if (number == 1 || number == 2)
            return number;
        else{
            for(int i=1;i<number;i++)
            {
                b=2*a;
                a=b;
            }
            return b;
        }
    }
};

10.矩形覆盖

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

class Solution {
public:
    int rectCover(int number) {
       int a=1;
       int b=2;
       int r=0;
       if (number<0)
           return 0;
       if(number==1 || number ==2)
           return number;
       else{
           for(int i=3;i<=number;i++)
           {    
               r=a+b;
               a=b;
               b=r;
           }
        return r;
       }
    }
};

11.二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示

class Solution {
public:
     int  NumberOf1(int n) {
         int count=0;
         while(n)
         {
             n=n&(n-1);
             count++;
         }
         return count;
         
     }
};

12.数值的整数次方

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0

class Solution {
public:
    double Power(double base, int exponent) {
        if( exponent == 0 && base != 0)
              return 1;
        if(exponent == 0)
            return 0;
        if(exponent>0)
            return pow(base,exponent);
        else
            return 1/(pow(base,-exponent));
    }
private:
    double pow(double i,int j){
        double result=1;
        for(j;j>0;j--)
            result=result*i;
        return result;
    }
};

13.调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector <int> arr;
        for(int i=0;i<array.size();i++)
        {
            if(array[i]%2==0){
                arr.push_back(array[i]);
                array.erase(array.begin()+i);
                i--;    
            }
        }
        for (int i=0;i<arr.size();i++){
            array.push_back(arr[i]);
        }
    }
};

14.链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        
        if(pListHead == NULL || k==0)
            return NULL;
        ListNode* head=pListHead;
       // ListNode* tail=pListHead;
        for(int i=0;i<k-1;i++)
        {
            if(head->next!=NULL)
                head=head->next;
            else
                return NULL;
        }
        while(head->next!=NULL)
        {
            head=head->next;
            pListHead=pListHead->next;
        }
        return pListHead;
    }
};

15.反转链表

输入一个链表,反转链表后,输出新链表的表头

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode* new_head=NULL;  
        while(pHead)
        {
            ListNode* next =pHead->next;  
            pHead->next=new_head;   
            new_head=pHead;
            pHead=next;
        }
        return new_head;
    }
};

16.合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1== NULL )
            return pHead2;
        if(pHead2==NULL)
            return pHead1; 
        ListNode temp_head(0);     //临时头结点
        ListNode* pre =&temp_head;    //使用pre指针指向零食头结点
        while(pHead1&&pHead2){          // 都不为空时进行比较  把较小的节点连接给pre
            if(pHead1->val <= pHead2->val){
                pre->next=pHead1;
                pHead1=pHead1->next;
            }
            else{
                pre->next=pHead2;
                pHead2=pHead2->next;
            }
            pre=pre->next;    //pre指向新的节点
        }
            if(pHead1)
                pre->next=pHead1;
            if(pHead2)
                pre->next=pHead2;        
            return  temp_head.next;        
    }
};

17.树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(!pRoot1||!pRoot2)  return false;
        if(isPart(pRoot1,pRoot2)) return true;
        return HasSubtree(pRoot1->left,pRoot2)|| HasSubtree(pRoot1->right,pRoot2);
        //递归枚举  先枚举左边 若没有,再枚举右边
    }
    //判断 第二棵数的点是不是在第一课树上都有对应的点
     bool isPart(TreeNode *p1,TreeNode *p2) {
         if(!p2) return true; // 第二课树空了,说明之前的都匹配上了
         if(!p1||p1->val != p2->val) return false;
         return isPart(p1->left,p2->left) && isPart(p1->right,p2->right);
           }

};

18.二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像


/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if (pRoot==NULL || (pRoot->left== NULL && pRoot->right==NULL)){
            return;
        }
        TreeNode *temp=pRoot->left;
        pRoot->left=pRoot->right;
        pRoot->right=temp;
        if(pRoot->left){
            Mirror(pRoot->left);
        }
        if(pRoot->right){
            Mirror(pRoot->right);
        }
        return;
    }
};

19.顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        
        int row=matrix.size(),column=matrix[0].size();
        vector<int> res;
        if(!row) return res;
        
        vector<vector<bool>> st(row,vector<bool>(column,false));
        int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
        int x=0,y=0,d=1;
        //定义好了四个方向的 横纵坐标的变化  上 右 下 左
        //已经初始的遍历方向 是 右边 
        //即dx dy 的第二个元素的组合 :x不变,y加1
        
        for(int i=0;i<row*column;i++){
            res.push_back(matrix[x][y]);
            st[x][y]=true;
            int a=x+dx[d],b=y+dy[d]; //下次遍历的对象坐标
            //遍历方向是否改变
            //判断是否不能走 (出界了没法走或者已经走过了)
            if(a<0||a>=row||b<0||b>=column||st[a][b]){
                d=(d+1)%4; 
                a=x+dx[d],b=y+dy[d];
            }
            x=a,y=b;//更新迭代变量            
        }
    
        return res;
    }
};

20.包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

class Solution {
public:
    
    stack<int> stk,stkMin;
    
    void push(int value) {
        stk.push(value);
        if (stkMin.empty()||stkMin.top() >= value)
            stkMin.push(value);
    }
    void pop() {
        if(stk.top()==stkMin.top())
            stkMin.pop();
        stk.pop();
    }
    int top() {
        return stk.top();
    }
    int min() {
        return stkMin.top();
        
    }
};

21.栈的压入 弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size()!= popV.size()) return false;
        
        stack<int> stk;
        int i=0; 
        for (auto x: pushV){
            
            stk.push(x);
            //判断栈顶 是否和 下一个要弹出的数是否匹配
            //下一个弹出的数用迭代变量 i 来控制
            while(stk.size() && stk.top() == popV[i])
            {
                stk.pop();
                i++; 
            }
        }
        return stk.empty();
    }
};

22.从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        
        vector<int> res; 
        if(!root) return res;
        
        queue<TreeNode*> queueNode;
        queueNode.push(root);
        
        while(queueNode.size()){
             TreeNode* pNode=queueNode.front();
             queueNode.pop();
             res.push_back(pNode->val);
            if(pNode->left) queueNode.push(pNode->left);
            if(pNode->right) queueNode.push(pNode->right);
                  
        }
        return res;
    }
};

23.二叉搜索树的后续遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

class Solution {
public:
    vector<int> seq;
    
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.empty()) return false;
        seq=sequence;
        return dfs(0,seq.size()-1);

    }
    bool dfs(int l, int r){
        if(l>=r) return true;
        int root = seq[r];
        int k=l;
        while(k<r && seq[k]< root) k++;
        for (int i= k;i<r;i++){
            if (seq[i]<root) return false;
        }
        return dfs(l,k-1)&& dfs(k,r-1);
    }
};

24.二叉树中和为某一值的路径

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        
        if(!root || expectNumber <= 0) return res;
        dfs(root,expectNumber);
        return res;
    }
    void  dfs(TreeNode* p ,int sum)
    { 
        if(!p) return ;
        path.push_back(p->val);
        sum=sum-p->val;
        if(!p->left&&!p->right&&!sum) res.push_back(path);
        dfs(p->left,sum);
        dfs(p->right,sum);
        path.pop_back();
    }
};

25.复制链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* head)
    {
        if(!head) return  NULL;
        RandomListNode* p=head;
        while(p){
            RandomListNode* np=new RandomListNode(p->label);
            np->next=p->next;
            p->next=np;
            p=np->next; 
       }
        
        p=head;
        while(p){
            if(p->random)
                p->next->random=p->random->next; 
             p=p->next->next;    
        }
        
        RandomListNode* dummy=head->next;
        RandomListNode* cur=NULL;
        p=head;
        while(p->next){ 
            cur=p->next;
            p->next=cur->next;
            p=cur;
             
       }
        return dummy;
    }
};

26.二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    TreeNode* Convert(TreeNode* root)
    {
        if(!root) return NULL;
        auto sides=dfs(root);
        return sides.first;
    }
    
    pair<TreeNode*,TreeNode*> dfs(TreeNode* root){
        
        if(!root->left&& !root->right) return {root,root};
        if(root->left&&root->right){
            auto lsides=dfs(root->left),rsides=dfs(root->right);
            lsides.second->right=root;root->left=lsides.second;
            rsides.first->left=root;root->right=rsides.first;
            return{lsides.first,rsides.second};
        }
        if(root->left){
            auto lsides=dfs(root->left);
            lsides.second->right=root,root->left=lsides.second;
            return{lsides.first,root};
        }
        if(root->right){
            auto rsides=dfs(root->right);
            root->right=rsides.first,rsides.first->left=root;
            return{root,rsides.second};
        }
    }
    
};

27.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
class Solution {
public:
    vector<string> Permutation(string str) {
        
        vector<string> res;
        if(!str.size()) return res;
        Permutation(res,str,0);
        sort(res.begin(),res.end());
        return res;  
        
    }
    void Permutation(vector<string> & array,string str,int begin){
        if(begin==str.size()-1) array.push_back(str);
        for(int i=begin;i<str.size();i++)
        {
            if(i!=begin && str[i]==str[begin]) continue; //该数后面如果有重读的 直接进入下一轮循环
            swap(str[begin],str[i]);
            Permutation(array,str,begin+1);
            swap(str[begin],str[i]);//复原,交换下下一个不相同的数字
        }
    }
};

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

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int count=0 ,val=-1;
        for (auto x:numbers){
            if(!count) {
                val=x;
                count=1;
            }
            else{
                if(x == val) count++;
                else count--;
            }
        }
        if (CheckMoreThanHAlf(numbers,numbers.size(),val)) return val;
        else return 0;
    }
    

 bool CheckMoreThanHAlf(vector<int> numbers,int length,int result)    //检查result的值的个数是否大于整个数组的一半
        {
           int times=0;
           for(int i=0;i<length;++i)
             {
                if(numbers[i]==result)
                    times++;
             }
           bool isMoreThanHalf=true;
           if(times*2<=length)
               isMoreThanHalf=false;
           return isMoreThanHalf;
        }
};

29.最小的K的数

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        int len=input.size();
        if(k>len||k<0) return vector<int> ();
        
        priority_queue<int> heap;
        for (int i=0;i<len; i++)
        {
            heap.push(input[i]);
            if ( heap.size()>k ) heap.pop();
        }
        
        vector <int> res;
        while(heap.size())
           {
              res.push_back(heap.top());
              heap.pop();
           }
         reverse(res.begin(),res.end());
         return res;
    }
};

30.连续子数组最大和

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int res=INT_MIN,s=0;
        for(int i=0;i<array.size();i++){
            if (s < 0 )
                s=array[i];
            else
                s+=array[i];
            res=max(s,res);
        }
        return res;
    }
};

31.整数中1出现的次数(从1到n整数中1出现的次数)

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

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(!n) return 0;
        // 初始化,从最低位开始处理每一位
        // 当前位,当前位的 左右 以及 基数
        int nCurBit=n%10;  //待处理的位 
        int nLeft=n/10;   //待处理位前面的数
        int nRight=0;     //待处理后面的数
        int nBase=1;     // 基数
        int sum=0;       //出现1的次数
        
        //单独处理每一位
        //当前位 为最左右便 处理完毕 /
        while(n/nBase){
            //分情况讨论
            // 左边加右边  
            //该位的数等于0 ,那么该位 能出现1的次数 只能有左边的决定
            if(nCurBit == 0){
                sum+=nLeft*nBase;
            }
            //该位的数等于1 ,那么该位 能出现1的次数
    // 由左边的加右边 ,右边取值不能大于位右边的数,否则超出范围
            else if (nCurBit==1){
                sum+=nLeft*nBase+nRight+1;
            }
 //该位的数等于1 ,那么该位 能出现1的次数 有左边的决定加 
//右边决定 右边取值无约束 可以取到合法的最大值
            else{
                sum+=(nLeft+1)*nBase;  //第一次忘了 写加号 sum=
            }
            
            nCurBit=nLeft%10;
            nBase*=10;
            nLeft=n/(nBase*10);
            nRight=n%nBase;
        }
        return sum;
    }
};

32.把数组排成最小的数

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

class Solution {
public:
    static bool cmp(int a,int b){
        auto as=to_string(a),bs=to_string(b);
        return as+bs<bs+as ;
    }
    string PrintMinNumber(vector<int> numbers) {
        sort(numbers.begin(),numbers.end(),cmp);
        string res;
        for(auto s : numbers) res+=to_string(s);
        return res;
        
    }
};

33.丑数

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

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if (index<=0) return NULL;
        vector<int> res(1,1);
        int i=0,k=0,j=0;
        long long num=1;
        while(--index){
            num=min(res[i]*2,min(res[j]*3,res[k]*5));
            if (num==res[i]*2)  i++;
            if (num==res[j]*3)  j++;
            if (num==res[k]*5)  k++;
            res.push_back(num);
        }
        return res.back();
    
    }
};

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

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

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        // 特殊情况
        int res= -1;
        if(!str.size()) return res;
        
        // 哈希表 存储每个字符出现的次数
        unordered_map<char,int> count;
        for(auto c:str) count[c]++;
        
        //查找是否存在出现一次的字符,并记录位置
        // 找到会更改res的值
        int pos=0;
        for(auto c:str){
            
            if (count[c]== 1){
                res=c;
                break;
            }
            pos++; 
        }
        
        //如果找到 也就是res值被更改,返回对应位置即可
        return res!=-1? pos :res;
         
    }
};

35.数组的逆序对

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

class Solution {
public:
    long countRes ;
    int InversePairs(vector<int> data) {
        countRes = 0;
        if(!data.size()) return 0;
        MergeSort(data,0,data.size()-1);
        return countRes%1000000007 ;
    }
     
    void MergeSort(vector<int>& data,int start,int end)
{
        if(start < end){
            int mid = (start + end)/2;
            MergeSort(data,start,mid);
            MergeSort(data,mid+1,end);
            
            vector<int> tmp;
            Merge(data,start,mid,end,tmp);
        }
    }
    void Merge(vector<int>& data,int start,int mid,int end,vector<int> tmp)
{
        int i = start,j=mid+1;
        //处理成对出现的
        while(i<=mid && j<=end){
            if(data[i] > data[j]){
                tmp.push_back(data[i++]);
                countRes += end - j + 1;
            }
            else{
                tmp.push_back(data[j++]);
            }
        }
        //然后处理落单的
        while(i<=mid)
            tmp.push_back(data[i++]);
        while (j<=end)
            tmp.push_back(data[j++]);
 
        //更新data数组
        int k = 0;
        for (int i = start; i <= end && k<tmp.size(); i++)
            data[i] = tmp[k++];
         
    }
};

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

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

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        
        ListNode*p = pHead1,*q = pHead2; 
        // 不能写成  ListNode*p = pHead1,q = pHead2;
        while(p!=q){
            if(p) p=p->next; //没到头 继续走
            else p=pHead2;  //到头指向另外一个链表头结点
            if(q) q=q->next;
            else q=pHead1;
        }
        return p;
        
    }
};

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

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

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        if(data.empty()) return 0;
        
        int res,left_index,right_index;
        int left=0,right=data.size()-1;
        while(left<right){
            int mid=(left+right)>>1;
            if(data[mid]<k) left=mid+1; 
            //中点值小宇K,搜索范围在右边 否则左边
            else right=mid;
        }
        if(data[left]!=k) return 0;
        left_index=left;   
        // k的坐左标
        
        left=0,right=data.size()-1; 
        //再次遍历 ,迭代遍历初始化
        while(left<right){
            int mid=(left+right+1)>>1;
            if(data[mid]<=k) left=mid;
            else right=mid-1;
        }
        
        return right-left_index+1;
        
        
    }
};

38.二叉树的深度

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

/*
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* p)
    {
        int res=0;
        if(!p) return res;
        
        queue<TreeNode*> q;
        q.push(p);
        
        while(q.size()){
            res++;
            int num = q.size();
            for(int i =0; i < num; i++){
                TreeNode* node=q.front();
                q.pop();
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);   
            }
        }
            
        return res;  
    }
};

39.平衡二叉树

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

class Solution {
public:
    bool ans = true;
    bool IsBalanced_Solution(TreeNode* p) {
        dfs(p);
        return ans;
    }
    int dfs (TreeNode* p){
        if (!p) return 0;
        int left = dfs(p->left),right=dfs(p->right);
        if (abs(left-right)>1) ans=false;
        return max(left,right)+1;
    };
};

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

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

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        
        if(!data.size()||data.size()<2) return;
        
        int allXOR =0;
        for(int i=0;i<data.size();i++)
            allXOR ^= data[i];
        int flag =allXOR & (-allXOR);
        //补码,符号位不变,从最低位开始,直到遇见第一个1,
        //这个1后面的都不变,前面的所有位依次取反
        //在和原数做位与,除过其他最右边的1,其他都是0
 
        *num1 = *num2 = 0;
        for(int i=0;i<data.size();i++){ 
            //根据flag位来判断 是要和哪一个异或运算
            if((data[i] & flag) == flag) 
                *num1 ^= data[i];
            else
                *num2 ^= data[i];
        }
    }

   };

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

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        
        if(sum < 3) return vector<vector<int>> ();
        
        vector<vector<int>> res;
        vector<int> path;
        
        int left=1,right=2,mid=(sum-1)/2; // 中间点的怎么求想清楚
        int curSum=left+right;
        while( left <= mid ){ //因为是最少连续2个数 所以左边顶多
           if(curSum==sum) { 
                for(int i=left;i<=right;i++) path.push_back(i);
                res.push_back(path);
                path.clear();
                curSum+=(++right); //继续找下一个 扩大区间 更新和
            }
            else if(curSum>sum){  //过大就缩小区间  动左边 更新和
                curSum-=left++;
            }    
            else{                 //过小就扩区间    动右边 更新和
                curSum+=(++right);
            }
        }
        return res;
    }
};

42.和为S的两个数组 [two sum]

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> res;
        if(array.size()<2) return res;
        
        int left=0,right=array.size()-1;
        long curSum=0;
        
        while(left<right){ //把left 写错了 写成了ledt
           curSum=array[left]+array[right];
           if(curSum==sum){
               res.push_back(array[left]);
               res.push_back(array[right]);
               break; // 从两边找第一次找到的必然是乘积最小的
           }
           else if(curSum>sum) right--;
           else left++;
        }
        return res;
    }
};

43.左旋转字符串

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

class Solution {
public:
    string LeftRotateString(string str, int n) {
        
        int len=str.size();
        if(!len) return "";
    // return ""  而不是 return ‘’
        reverse(str.begin(),str.end());  
   // end()函数返回一个迭代器,指向字符串的末尾(最后一个字符的下一个位置).
        reverse(str.begin(),str.begin()+(len-n));
        reverse(str.begin()+(len-n),str.end());
        return str;
   // reverse  左闭右开区间
    }
   
};

44.翻转单词顺序列

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

class Solution {
public:
    string ReverseSentence(string str) {
        if(str.size()==0) return "";
        
        reverse(str.begin(),str.end());
        int left=0,right=0;
        while(right<=str.size()){
            if(str[right]==' ' || (right==str.size() && str[right-1]!= ' '))
               { //翻转每个单词,最后一个单词如果是一个字符则没有必要翻转
               reverse(str.begin()+left,str.begin()+right);
               left=right+1;
            }
               right++;
        }
         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的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        if(numbers.empty()) return false;
        
        sort(numbers.begin(),numbers.end()); 
        //把数组排好序,如果满足条件的话,里面极差不能大于
        //且除过大小王也就0意外不能有重复数字
        int k=0;
        while(numbers[k]== 0) k++; //去大小王
        for(int i=k+1;i<numbers.size();i++){
            if(numbers[i]==numbers[i-1])  //是否有重复
                return false;
        }
        return numbers.back()-numbers[k] <=4;  
        //是否极差不大于4
    }
};

46.孩子们的游戏 【约瑟夫环】

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

如果没有小朋友,请返回-1

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
       if(!n && !m ) return -1;
 
        int last=0;
        for(int i=2;i<=n;i++)
        {
            last=(last+m)%i;
        }
        return last;
    }
};

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) {
        if(!n) return 0;
        int res=0;
        for(int i= 1;i<=n;i++)
            res=Add(res,i);
        return res;
    }
    
     int Add(int left, int right)
    {
        int temp;
        while(right != 0)
        {
            temp = left ^ right;               
            //  计算不带进位的情况
            right = (left & right) <<1;        
            //  计算带进位的情况
            left = temp;
            //  now left = 不带进位的情况, right = 带进位的情况
        }

        return left;
    }
};

48.不用加减乘除做加法

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

class Solution {
public:
    int Add(int num1, int num2)
    {
        if(num1==0 && num2!=0) return num2;
        if(num2==0 && num1!=0) return num1;
        if(num1==0 && num2==0) return 0;
        int temp;
        while(num2!=0){
            temp=num1^num2;
            num2=(num1&num2)<<1;
            num1=temp;
        }
        return num1;
    }
};

49.把字符串转换成整数

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
class Solution {
public:
    int StrToInt(string str) {
        if(!str.size()) return 0;
        int k=0,len=str.size();
        //去掉开头的空格
        while(k<len && str[k]== ' ') k++;
        bool is_minus=false;
        long long num=0;
        //判定正负
        if(str[k] == '+') k++;
        else if(str[k]== '-') k++,is_minus=true;
        //看正负号的数字,合法得到话加起来
        //也可以看做 跳过合法数字后 正负号后面是什么
        //是空格 合法
        //不是空格  不合法
        while(k<len && str[k]>='0' && str[k]<='9'){
            num=num*10+str[k]-'0';
            k++;
        }
        if(str[k]!= '\0') return 0;
        if(is_minus) num=num*-1;
        if(num > INT_MAX || num < INT_MIN) return 0;
        
        return (int)num;
        
    }
    
};

50.数组中重复的数字

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

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) {
        *duplication =-1;
        
        if(isValidate(numbers,length)== false) return false;
        //若输入合法,对数组排序 O(logn)
        sort(numbers,numbers+length);
        bool res=false;
        //遍历是否重复  O(n)
        for(int i=0; i<length-1;i++){
           if(numbers[i] == numbers[i+1]){
               res=true;
               *duplication=numbers[i];
               break;
           }
       } 
        return res;
    }
    
    bool isValidate(int *numbers,int length){
        if(!numbers) return false;
        for(int i=0;i<length;i++){
            if(numbers[i]<0||numbers[i]>=length) return false;
        }
        return true;
    }
};

51.构建乘积数组

给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]A[1]...A[i-1]A[i+1]...A[n-1]。不能使用除法。

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        
        int n = A.size();
        if(!n) return vector<int> ();  
        //不能直接返回NULL 为什么??
        
        vector<int> b(n);
        
        // 左边连乘
        for(int i=0,p=1;i<n;i++){
            b[i]=p;
            p*=A[i];
        }
        
        // 右边连乘
        for(int i=n-1,p=1;i>=0;i--){ 
            // i>=0  第一次 写成了 i >0 
            b[i]*=p;
            p*=A[i];
        }
        
        return b;

    }
};

52.正则表达式匹配

请实现一个函数用来匹配包括'.'和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配

class Solution {
public:
    vector<vector<int>> f;
    int s_len;
    int p_len;

    bool match(char* str, char* pattern) {
        string s=str,p=pattern;
        s_len = s.size();
        p_len = p.size();
        f = vector<vector<int>>(s_len+1,vector<int>(p_len+1,-1)); 
        //f[i][j]表示s从i开始与p从j开始的匹配情况。
        for(int i=0;i<s_len;i++){
       //初始化,f[x][p_len]=0, 任意s的子串都不与模式串p为空匹配。
            f[i][p_len]=0;
        }
        /*  初始化  f[s_len][y]=0, 任意p的子串都不与字符串s为空匹配。
        **这里是错误的**   例如 "" 与 "a*" 还是可以相匹配
        for(int j=0;j<p_len;j++){
            f[s_len][j]=0;
        }
        */
        f[s_len][p_len] = 1;  //只有两者均为空相匹配

        dp(0,0,s,p);
        return f[0][0]; 
    }


    bool dp(int i,int j,string &s,string &p){
        if(f[i][j]!=-1) return f[i][j];//已赋值

        bool first_match = (s[i]==p[j]||p[j]=='.');
        bool ans;
        if(p[j+1]=='*'){  
            ans = (first_match && i<s_len && dp(i+1,j,s,p)) || (j<p_len-1 && dp(i,j+2,s,p)) ; 
      //注意添加判断越界的情况
        }else{
            ans = first_match && i<s_len && j<p_len &&  dp(i+1,j+1,s,p); 
     //注意添加判断越界的情况
        }
            f[i][j]=ans;
        return f[i][j];

    }
};


53.表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是

class Solution {
public:
    bool isNumeric(char* string)
    {
        if(string==NULL) return false;
        if(*string=='+'||*string=='-') ++string;
        if(*string=='\0') return false;  // 加减号开头  下面啥都没有 
        bool numberic = true;
        ScanDigitals(&string); //扫描 直到不是数字,分情况讨论 是小数点?,继续扫描,遇到了E
        if(*string!='\0'){  // 没到结尾
            if(*string == '.'){   //小数点后只能 是 数字 或者 字母e或者e  
                ++string;
                ScanDigitals(&string);
                if(*string == 'e'|| *string == 'E') 
                    numberic=IsExponential(&string);
            }
            else if(*string == 'e'|| *string == 'E'){ //直接遇到的是E 
                 numberic=IsExponential(&string);
            }
            else { //遇到其他非法字符
                //return false;
                numberic = false;
            }
        }
        return numberic && (*string =='\0');;
    }
    //变量数字量  指向下一个非数字  可能是 非法字符(+ -也算 加减只能开头或e后面) 可能是e  可能你字符串结尾‘\0’
    void ScanDigitals(char **str){
        while( **str != '\0' && (**str>='0' && **str <= '9'))
        {++(*str);}
    }
    bool IsExponential(char ** str){
        //if(**str != 'e' && **str != 'E') return false; 因为一定时e开头 所以注释掉
        ++(*str);//  看e后面的是否合法   只能是 符号 和 数字
        if(**str== '+'||**str=='-' ) ++(*str); //满足符号 ,看下一位
        if(**str=='\0') return false;//符号后面啥都没 不符合
        ScanDigitals(str);//看是不是都是数字
        return (**str == '\0') ? true : false;  //遍历完数字后 如果到达字符串尾 复合 e开头
       
    }

};

54.字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。


class Solution
{
public:
    unordered_map<char,int> count;
    queue<char> q;
  //Insert one char from stringstream
    void Insert(char ch)
    {
        count[ch]++; 
        //输入字符出现次数加1
        //输入了新的字符,出现次数更新了,维护队
        //若新的字符第一次出现,入队
        //若新的字符不是第一次出现,不入队,因为之前出现过,必然使得出现次数大于1,出队
        if(count[ch]>1){
            while(q.size()&& count[q.front()]>1) q.pop();
        }
        else q.push(ch);
         
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        if(q.empty()) return '#';
        return q.front();
    }

};

55.链表中环的入口结点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(pHead==NULL) return NULL;
        ListNode *fast=pHead,*slow=pHead;
        while(fast && fast->next ){
            fast=fast->next->next;
            slow=slow->next;
            if(fast==slow) break;
        }
        //循环退出条件 正常遍历完退出  对应着无环的情况
        //相遇退出
        if(!fast || !fast->next ) return NULL; //返回无环的情况
        slow=pHead;
        while(fast!=slow){
            fast=fast->next;
            slow=slow->next;
        }
        return slow;

    }
};

56.删除链表中重复的节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(!pHead) return NULL;
        ListNode* dummy =new ListNode(-1);
        dummy->next=pHead;
        
        ListNode* p=dummy;
        while(p->next){
            ListNode* q = p->next;
           // while(q && (p->next->val == q->val)) q=q->next;
            while(q){
                int val=p->next->val;
                if(q->val == val ) q=q->next;
                else break;
                
            }
           
            if(p->next->next==q) p=p->next;
            // 当前节点的下下一个元素 和 是新的下一段的开头 说明后继 不是重读的
            else p->next=q;
        }
        return dummy->next;

    }
};

57.二叉树的下一个结点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* p)
    {
        if (p->right){ // 找右子树左边的那个
            p=p->right; 
            while(p->left){
                p=p->left;
            }
            return p;
        }
        while(p->next && p == p->next->right) p=p->next; 
        //进不去的条件是找到了上面的第一个做左孩子的子节点 
        return p->next;
    }
};

58.对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    
    bool isSymmetrical(TreeNode* p)
    {
         if(!p) return true;
         return dfs(p->left,p->right);
    }
    bool dfs(TreeNode * p,TreeNode * q ){
        if(!p || !q)  return  !p && !q ;  
        // 有一个为空,返回false,两个都空的话返回 true
        if( p->val != q->val) return false;
        return dfs(p->left,q->right) && dfs(p->right,q->left);
    }

};

59.按照之字形顺序打印二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    vector<vector<int> > Print(TreeNode* p) {
        vector<vector<int>> res;
        vector<int> path;
        if(!p) return res;
        
        queue<TreeNode*> q;
        q.push(p);
        bool flag=true;
        // 加标志位,奇数行 左->右  偶数行  右-> 左
       
        while(q.size()){
            
            //vector<int> path;
            int size= q.size();
            for(int i=0;i<size;i++){  
                //注意 这里  i<q.size()  不能这么写 因为队列是动态变化的
                auto front=q.front();
                path.push_back(front->val);
                q.pop();
                if(front->left) q.push(front->left);
                if(front->right) q.push(front->right);     
               }
            if(!flag) reverse(path.begin(),path.end());
            flag=!flag;
            res.push_back(path);
            path.clear();
        
        }
        return res;

    }
    
};

60.把二叉树打印成多行

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
        vector<vector<int> > Print(TreeNode* root) {
            vector<vector<int>> res;
            if(!root) return res;
            
            queue<TreeNode*> q;
            q.push(root);
            
            while(q.size()){
                int size=q.size();
                vector<int> path;
                for(int i=0;i<size;i++){
                    auto front=q.front();
                    path.push_back(front->val);
                    q.pop();
                    if(front->left) q.push(front->left);
                    if(front->right) q.push(front->right);
                 }
                 res.push_back(path);
                
            }
            return res;
        
        }
    
};

61.序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {    
        if(! root) return NULL;
        string str;
        dfs_s(root ,str); //注意函数的返回值
        char *res =new char[str.size()+1];
        int i;
        for(i=0;i<str.size();i++){
            res[i]=str[i];
        }
        res[i]='\0';
        return res;
    }
    
    void dfs_s(TreeNode *root,string &str){
        if(root==NULL){
            str+='#';
            return ;
        }
        str+=to_string(root->val);
        str+='!';
        dfs_s(root->left,str);
        dfs_s(root->right,str);
    }
    
    TreeNode* Deserialize(char *str) {
        if(str == NULL) return NULL;
        TreeNode* res=dfs_d(&str);
        return res;
    }
    
    TreeNode* dfs_d(char **str){
        if(**str == '#'){
            ++(*str);
            return NULL;
        }
        //把字符转换成节点
        int num=0;
        while(**str!='\0' && **str != '!'){
            num=num*10+((**str)-'0');
            ++(*str);
        }
        TreeNode *root=new TreeNode(num);
        // 转化完毕,查看是否还有未转换的节点。
        // 到达字符串末尾即转换完毕
        if(**str=='\0') return root;
        else (*str)++;
        root->left=dfs_d(str);
        root->right=dfs_d(str);
        return root;
    }
};

62.二叉搜索树的第K个结点

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4

class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {   
        if(pRoot == NULL) return NULL;
        return dfs(pRoot,k) ;
    }
    
    TreeNode* dfs(TreeNode* pRoot, int& k)
    {
        TreeNode* target = NULL;
        if(pRoot->left !=NULL) 
            target = dfs(pRoot->left,k);
         k--;
         if(k==0){
            target = pRoot;
            return target;
         }
        if(target == NULL && pRoot->right !=NULL && k>0) 
            target = dfs(pRoot->right,k);
        return target;
    }
};

63.数据流的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

class Solution {
public:
    priority_queue<int> max_heap;
    priority_queue<int,vector<int>,greater<int>> min_heap;
    void Insert(int num)
    {
        max_heap.push(num);
        if(min_heap.size() && max_heap.top()> min_heap.top()){
            auto maxv=max_heap.top(),minv=min_heap.top();
            max_heap.pop(),min_heap.pop();
            max_heap.push(minv),min_heap.push(maxv);
        }
        if(max_heap.size()>min_heap.size()+1){
            min_heap.push(max_heap.top());
            max_heap.pop();
        }
    }

    double GetMedian()
    { 
        if(max_heap.size()+ min_heap.size() & 1)
            return max_heap.top();
        //如果是奇数 最低位必定为1 
        return(max_heap.top()+ min_heap.top()) / 2.0;
    }

};

64.滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

class Solution {
public:
    vector<int> maxInWindows(vector<int>& nums, int k) {
        if(k<1) return vector<int> ();
        vector<int>res;
        deque<int>q;
        for(int i = 0; i < nums.size(); i++){
            if(!q.empty() && i-q.front() >= k)//判断队头是否需要出队
                q.pop_front();
            while(!q.empty()&&nums[q.back()]<nums[i])//维护队列单调性
                q.pop_back();
            q.push_back(i);
            if(i >= k-1){
                res.push_back(nums[q.front()]);//取队头作为窗口最大元素
            }
        }
        return res;
    }
};

65.矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        if(matrix== NULL|| rows<1 || cols<1 || str ==NULL) return false;
        //vector<int> *flag(0,rows*cols);
        bool *flag=new bool[rows*cols];
        memset(flag,false,rows*cols);
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if( dfs(matrix,rows,cols,i,j,str,0,flag))
                    return true;
            }
        }
        delete[] flag;
        return false;
    }
     
    bool dfs(char* matrix,int rows,int cols,int i, int j ,char* str,int k ,bool* flag){
        int index=i*cols+j;
        if(i<0 || i>rows || j<0 || j>cols || matrix[index]!= str[k] || flag[index] == 1)
            return false;
        //抵达字符串末尾 说明已经找到
        if(str[k+1]=='\0') return true;
        flag[index]=1;
        if(  dfs(matrix,rows,cols,i-1,j,  str,k+1,flag)  //左
          || dfs(matrix,rows,cols,i+1,j,  str,k+1,flag)  //右
          || dfs(matrix,rows,cols,i  ,j+1,str,k+1,flag)  //上
          || dfs(matrix,rows,cols,i  ,j-1,str,k+1,flag)  //下
          ) return true;
        flag[index]=0;
        return false;
    }
 
 
};

66.机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        if(rows<1 || cols <1 || threshold<0) return 0;
        queue<pair<int,int>> q;
        vector<vector<bool>> st(rows,vector<bool>(cols,false));
        int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
        
        int res=0;
        q.push({0,0});
        
        while(q.size()){
            auto t= q.front();
            q.pop();
            if(st[t.first][t.second]|| get_sum(t)>threshold) continue;
            res++;
            st[t.first][t.second]= true;
            for(int i=0;i<4;i++){
                int x=t.first+dx[i],y=t.second+dy[i];
                if(x>=0 &&  x<rows && y>=0 && y<cols) q.push({x,y});
            } 
        }
        return res;
    }
    
    int get_sum(pair<int,int> p){
        int s=0;
        while(p.first){
            s+=p.first%10;
            p.first/=10;
        }
        while(p.second){
            s+=p.second%10;
            p.second/=10;
        }
        return s;
    }
};

67.剪绳子

给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
class Solution {
public:
    int cutRope(int length)
    {
    if (length == 0) return 0;
    if (length == 1) return 1;
    if (length == 2) return 1;
    if (length == 3) return 2;
    
    int dp[length+1];
    memset(dp,0,sizeof(dp));
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 3;
    for (int i=4;i<=length;i++){
        int max = 0;
        for(int j=1; j<=i/2;j++){
            if (dp[i-j] * dp[j] > max){
                max = dp[i-j] *dp[j];
            }
        }
        dp[i] = max;
    }
    return dp[length];
    }

};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容