《剑指Offer——名企面试官精讲典型编程题》代码及其注释

面试题3:数组中重复的数字

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int length=nums.size();//将需要重复计算的数字使用寄存器保存起来
        if(nums.empty()||length<=0)return -1;//判断vector是否为空
        for(int i=0;i<nums.size();++i){//判断数组中是否所有数字都落在0~n-1范围内
            int tmp=nums[i];
            if(tmp<0||tmp>length-1)
                return false;
        }
        for(int i=0;i<length;++i){从头到尾扫描数组
            while(nums[i]!=i){数组下标和对应数相等则继续
                if(nums[i]==nums[nums[i]]){//数组下标对应数和数组下标对应数的下表对应数如果相等则说明该数重复
                    return nums[i];
                }
            int tmp=nums[i];//否则交换两数位置
            nums[i]=nums[tmp];
            nums[tmp]=tmp;
            }
        }
        return -1;
    }
};

面试题4:二维数组中的查找

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.empty())return false;//二维数组为空则返回false
        int rowsize = matrix.size();//行数
        int colsize = matrix[0].size();//列数
        int row = rowsize - 1, col = 0;
        while (row >= 0 && col < colsize) {
            int tmp=matrix[row][col];
            if (tmp == target){//值相等返回true
                return true;
            }
            if (tmp < target) {//值小于则列数增加
                ++col;
            }
            else
                --row;//否则则行数减少
        }
        return false;
    }
};

面试题5:替换空格

class Solution {
public:
    string replaceSpace(string s) {
        int spaceNum=0;
        int oldLength=s.length();
        for(int i=0;i<oldLength;i++){
            switch(s[i]){
                case ' ':spaceNum+=2;//每遇到一个空格就增加两个char长度
            }
        }
        int newLength=oldLength+spaceNum;
        s.resize(newLength);
        while(oldLength>=0&&newLength>oldLength){
            switch(s[oldLength]){
                case ' ':{
                    s[newLength--]='0';
                    s[newLength--]='2';
                    s[newLength--]='%';
                }break;
                default:
                    s[newLength--]=s[oldLength];
            }
            --oldLength;
        }
        return s;
    }
};

面试题6:从尾到头打印链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        stack<ListNode*> nodes;//栈(后进先出)
        vector<int>vec;
        ListNode* pNode=head;
        while(pNode!=nullptr){
            nodes.push(pNode);
            pNode=pNode->next;
        }
        while(!nodes.empty()){
            pNode=nodes.top();
            vec.push_back(pNode->val);
            nodes.pop();
        }
        return vec;
    }
};

面试题7:重建二叉树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
     TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.empty())return NULL; 
        return build(preorder, inorder, 0, 0, inorder.size());
    }
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int root, int start, int end){// 中序的start和end
        TreeNode *tree = new TreeNode(preorder[root]);
        int i = start;
        while(i < end && preorder[root] != inorder[i]) i++;
        tree->left = (i-start)?build(preorder, inorder, root + 1, start, i):NULL;//当节点没有左子树时,i等于start
        tree->right = (end-i-1)?build(preorder, inorder, root + 1 + i - start, i + 1, end):NULL;//当节点没有右子树时,end等于i+1
        return tree;
    }
};

面试题9:用两个栈实现队列

class CQueue {
    stack<int> pushS;
    stack<int> deleS;
public:
    CQueue() {
    }
    
    void appendTail(int value) {
        pushS.push(value);
    }
    
    int deleteHead() {
        if(deleS.empty()){//看删除栈中是否有可删除的元素
            while(!pushS.empty()){//若没有则将插入栈中元素传到删除栈中
                deleS.push(pushS.top());pushS.pop();
            }
        }
        if(deleS.empty())return -1;//若此时删除栈中仍然没有元素则返回-1
        int val=deleS.top();
        deleS.pop();
        return val;返回删除栈有原栈顶元素
    }
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

面试题10-1:斐波那契数列

class Solution {
public:
    int fib(int n) {
        int min1=0,min2=1;
        if(n==0)return min1;
        else if(n==1)return min2;
        for(int i=2;i<=n;++i){
            int tmp=min1+min2;
            min1=min2;
            min2=tmp%1000000007;
        }
        return min2;
    }
};

面试题10-2:青蛙跳台阶问题

class Solution {
public:
    int numWays(int n) {
        int min1=1,min2=2;
        if(n==1||n==0)return min1;
        else if(n==2)return min2;
        for(int i=3;i<=n;++i){
            int tmp=(min1+min2)%1000000007;
            min1=min2;
            min2=tmp;
        }
        return min2;
    }
};

面试题11:旋转数组的最小数字

class Solution {
public:
    int minArray(vector<int>& numbers) {
        if(numbers.size()==1)return numbers[0];//数组长度为一时该值为最小值
        int beg=0,middle=0,end=numbers.size()-1;
        while(numbers[beg]>=numbers[end]){//当数组头和数组尾出现大于等于关系时才有可能是旋转数组
            if(end-beg==1){//当end-beg==1证明进入边界条件
                middle=end;
                break;
            }
            middle=(beg+end)>>1;//beg和end的中位数
            if(numbers[middle]<numbers[beg]){//中位数小于数组头证明旋转发生在数组的前半段
                end=middle;
            }else if(numbers[end]<numbers[middle]){//数组末尾小于中位数证明旋转发生在数组的后半段
                beg=middle;
            }else {//当头尾相等时可任意移动头或者尾,这里选择数组尾部
                end -= 1;
            }
        }
        return numbers[middle];
    }
};

 面试题12:矩阵中的路径

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        const int rows=board.size(),cols=board[0].size();//二维数组行列长度不修改,使用const
        int len=0;//标记string当前下标对应的char
        vector<bool> map(rows*cols,false);//map判断是否已经遍历过了
        for(int row=0;row<rows;++row){//遍历二维数组每一个元素用来作为输入的开头
            for(int col=0;col<cols;++col){
                if(exitChar(board,row,col,rows,cols,word,len,map))return true;
            }
        }
        return false;
    }
    bool exitChar(vector<vector<char>>& board,int x,int y,const int rows,const int cols, string& word,int len,vector<bool>&map){
        if (len==word.size())return true;//下标到达string结尾,证明该路径所有字符都在矩阵中找到了对应位置
        bool hasPath=false;//判断是否有对应路径
        if(x>=0&&x<rows&&y>=0&&y<cols&&board[x][y]==word[len]&&!map[x*cols+y]){//有对应路径,x,y合法,没有重复进入该格子
            ++len;//下标前移一格
            map[x*cols+y]=true;//地图标记为已走过
            hasPath=exitChar(board,x+1,y,rows,cols,word,len,map)||exitChar(board,x,y+1,rows,cols,word,len,map)||
            exitChar(board,x-1,y,rows,cols,word,len,map)||exitChar(board,x,y-1,rows,cols,word,len,map);//递归判断下一个路径是否能继续
            if(!hasPath){//如果不可继续
            map[x*cols+y]=false;//则修改地图标记
            --len;//下标值回退
        }
        }
        return hasPath;
    }
};

面试题13:机器人的运动范围

class Solution {
    bool check(int x,int y,int k){
        int sum=0;
        while(x>0){
            sum+=x%10;
            x/=10;
        }
        while(y>0){
            sum+=y%10;
            y/=10;     
        }
        if(sum<=k)return true;
        else return false;
    }
public:
    int movingCount(int m, int n, int k) {
        vector<bool>map(m*n,false);
        int cnt=exitMoving(map,0,0,m,n,k);
        return cnt;
    }
    int exitMoving(vector<bool>&map,int x,int y,int m,int n,int k){
        if(x>=0&&x<m&&y>=0&&y<n&&check(x,y,k)&&!map[x*n+y]){//有对应路径,x,y合法,没有重复进入该格子
            map[x*n+y]=true;//地图标记为已走过
            return 1+exitMoving(map,x+1,y,m,n,k)+exitMoving(map,x,y+1,m,n,k)+exitMoving(map,x-1,y,m,n,k)+exitMoving(map,x,y-1,m,n,k);//递归判断下一个路径是否能继续
        }
        return 0;
    }
};

 面试题14-1:剪绳子

class Solution {
public:
    int cuttingRope(int n) {
        if(n<2)return 0;//
        switch(n){
            case 2:return 1;
            case 3:return 2;           
        }
        int len=n/3,max=1;//尽可能多的剪出长度为3的绳子
        for(int i=0;i<len;++i){//len-1是为了处理绳子长度为n%3同余1的情况
            max*=3;
        }
        n%=3;//求剩余绳子长度
        if(n==1)return max*4;//若为1则乘以四
        else if(n)return max*3*(n);若不为0则乘上之前减去的len值1并乘上剩余绳子长度
        return max*3;若为0则直接乘上之前减去的len值1
    }
};

面试题14-2:剪绳子II

class Solution {
public:
    int cuttingRope(int n) {
        if(n<2)return 0;//
        switch(n){
            case 2:return 1;
            case 3:return 2;           
        }
        int len=n/3;//尽可能多的剪出长度为3的绳子
        long max=1;
        for(int i=0;i<len-1;++i){//len-1是为了处理绳子长度为n%3同余1的情况
            max=max*3%1000000007;
        }
        n%=3;//求剩余绳子长度
        if(n==1)return static_cast<int>(max*4%1000000007);//若为1则乘以四
        else if(n)return static_cast<int>(max*3*n%1000000007);//若不为0则乘上之前减去的len值1并乘上剩余绳子长度
        return static_cast<int>(max*3%1000000007);//若为0则直接乘上之前减去的len值1
    }
};

面试题18-1:删除链表的节点

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        if(!head)return head;//链表长度为0时直接输出原链表
        else if(head->val==val)return head->next;
        ListNode *dummy=new ListNode();//该指针用来保存链表头
        dummy->next=head;
        ListNode *cur=dummy;//当前指针
        while(cur&&cur->next)//cur不为nullptr且不为最后一个元素
        {
            if(val!=cur->next->val);
            else{
                ListNode *tmp=cur->next;
                cur->next=cur->next->next;//指针前移
                delete tmp;
                }
            cur=cur->next;
        }
        if(cur&&cur->val==val)delete cur;
        return dummy->next; 
    } 
};

面试题18-2:删除链表重复节点

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head||!head->next)return head;//链表长度为1或0时直接输出原链表
        ListNode *dummy=new ListNode();//该指针用来保存链表头
        dummy->next=head;
        ListNode *cur=dummy;//当前指针
        while(cur&&cur->next)//cur不为nullptr且不为最后一个元素
        {
            ListNode *nxt=cur->next;//下一个指针
            if(!nxt->next||nxt->val!=nxt->next->val)//当下一个指针是最后一个元素或者不重复
                cur=nxt;
            else{
                while(nxt->next && nxt->val==nxt->next->val){//当下一个指针后继不为空且对下一个指针值与下一个指针后继重复
                ListNode *tmp=nxt;
                nxt=nxt->next;//下一个指针前移
                delete tmp;
                }
                //此时nxt位于重复串的最后一位
                cur->next=nxt->next;
            }
        }
        return dummy->next;  
    }
};

面试题19:正则表达式匹配

class Solution {
public:
    int n, m;
    bool matchUnit(string s,size_t sSize,string p,size_t pSize){
        if(n==sSize&&m==pSize)return true;//字符串和模式都结束,匹配
        if(sSize<n&&m==pSize)return false;//若模式结束但字符串还未结束则不匹配,但若字符串结束但模式还没结束还有可能模式剩余'*'的可能,此时可以匹配0个字符
        if(pSize+1<m&&p.at(pSize+1)=='*'){//对应字符的下一个字符若为'*'则可能匹配0个,1个或者n个对应字符
            if(sSize<n&&(s.at(sSize)==p.at(pSize)||p.at(pSize)=='.')){//对应字符串字符和模式对应字符相等或者模式对应字符为'.'
                return matchUnit(s,sSize+1,p,pSize+2)//匹配1个对应字符
                ||matchUnit(s,sSize+1,p,pSize)//匹配n个对应字符
                ||matchUnit(s,sSize,p,pSize+2);//匹配0个对应字符
            }
            else
                return matchUnit(s,sSize,p,pSize+2);//不匹配,只能匹配0个对应字符
        }
        else if(sSize<n&&(s.at(sSize)==p.at(pSize)||p.at(pSize)=='.'))//对应字符串字符和模式对应字符相等或者模式对应字符为'.'
            return matchUnit(s,sSize+1,p,pSize+1);//匹配1个对应字符
        return false;
    }
    bool isMatch(string s, string p) {//s为字符串,p为模式
        n=s.size();
        m=p.size();
        return matchUnit(s,0,p,0);
    }
};
class Solution {
public:
    int n, m;
    bool matchUnit(string s,size_t sSize,string p,size_t pSize){
        if(n==sSize&&m==pSize)return true;//字符串和模式都结束,匹配
        if(sSize<n&&m==pSize)return false;//若模式结束但字符串还未结束则不匹配,但若字符串结束但模式还没结束还有可能模式剩余'*'的可能,此时可以匹配0个字符
        bool flag=sSize<n&&(s.at(sSize)==p.at(pSize)||p.at(pSize)=='.');//对应字符串字符和模式对应字符相等或者模式对应字符为'.'
        if(pSize+1<m&&p.at(pSize+1)=='*')//对应字符的下一个字符若为'*'则可能匹配0个,1个或者n个对应字符
            return flag&&matchUnit(s,sSize+1,p,pSize)||matchUnit(s,sSize,p,pSize+2);//匹配n个对应字符或匹配0个对应字符
        else 
            return flag&&matchUnit(s,sSize+1,p,pSize+1);//匹配1个对应字符
    }
    bool isMatch(string s, string p) {//s为字符串,p为模式
        n=s.size();
        m=p.size();
        return matchUnit(s,0,p,0);
    }
};

这里不建议用递归,时间会爆炸的

class Solution {
public:
    bool isMatch(string s, string p) {//s为字符串,p为模式
        int n=s.size(), m=p.size();
        vector<vector<bool>> map(n+1,vector<bool>(m+1));
        map[0][0]=true;
        for(size_t pSize=1;pSize<=m;++pSize){
            if(p.at(pSize-1)=='*')
                map[0][pSize]=map[0][pSize-2];
        }
        for(size_t sSize=1;sSize<=n;++sSize){
            char strChar=s.at(sSize-1);
            for(size_t pSize=1;pSize<=m;++pSize){
                char pChar=p.at(pSize-1);
                if(pChar=='*'){//对应字符的下一个字符若为'*'则可能匹配0个,1个或者n个对应字符
                    pChar=p.at(pSize-2);    
                    if(strChar==pChar||pChar=='.')//对应字符串字符和模式对应字符相等或者模式对应字符为'.'
                        map[sSize][pSize]=map[sSize-1][pSize]||map[sSize][pSize-2]||map[sSize-1][pSize-2];//匹配n个对应字符或匹配0个对应字符或匹配1个对应字符
                    else
                        map[sSize][pSize]=map[sSize][pSize-2];//匹配0个对应字符
                }
                else if(strChar==pChar||pChar=='.')//对应字符串字符和模式对应字符相等或者模式对应字符为'.'
                    map[sSize][pSize]=map[sSize-1][pSize-1];//匹配1个对应字符
            }
        }
        return map[n][m];
    }
};

 

 

原文地址:https://www.cnblogs.com/GodZhuan/p/14307771.html