栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

剑指offer刷题记(C++版本)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

剑指offer刷题记(C++版本)

也算是临时抱佛脚了吧,3月之前刷了lintcode100多道题吧,后来发文章什么的就放下了,最近秋招在即在牛客网上想着把剑指offer这本书刷完,尽量早刷完吧,最近也很忙。

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

  • 思路:从右上角来入手,如果右上角的数字大于目标,那么最右边一列都不满足,则去掉这一列,如果这个数小于目标,则最上面一行都不满足,删除这一行,如果刚好是目标就可以输出了。(这里的删除也不是真的去删掉,只需要挪动记录右上角的坐标数值就可以了)。

bool Find(int target, vector > array) {        if(array.empty())    return false;        int rows=array.size();        int cols=array[0].size();        int row=0;        int col=cols-1;        while(row=0)
        {            if(array[row][col]==target)  return true;            else if(array[row][col]>target)
            {
                col--;
            }            else  row++;
        }        return false;
    }
2.替换空格。
  • 题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

  • 思路:从后向前替换,先遍历一遍统计空格的数目,然后扩张字符串使得可以放下替换后的字符,然后从后向前依次复制,非空格字符直接复制,空格字符用题目要求的替换。

 //C风格的字符串最后一个字符是n,而且是记在字符串长度里的。
    void replaceSpace(char *str,int length) {        int i=0;        int cnt=0;        
        if(length==0||str==nullptr) return ;     //空字符串
        
        for(i=0;i=0&&index_new>index_old)   //没有到头或者两个指针追上了
        {            if(str[index_old]==' ')
            {                //依次替换三个字符,并且把index_old--
                str[index_new--]='0';
                str[index_new--]='2';
                str[index_new--]='%';    
                index_old--;
            }            else 
            {                //如果不是空格直接复制就可以了
                str[index_new--]=str[index_old--];
            }
        }

    }
3. 从尾到头打印链表。
  • 题目:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

  • 思路:使用栈。

vector printListFromTailToHead(ListNode* head) {        if(head==nullptr)            return vector();        stack tmp_stack;        vector res;
        ListNode *phead=head;   //如果要求不改变原链表,这里换一下
        while(phead!=nullptr)
        {
            tmp_stack.push(phead->val);
            phead=phead->next;
        }        while(!tmp_stack.empty())
        {
            res.push_back(tmp_stack.top());
            tmp_stack.pop();   //出栈
        }        return res;
    }
4.重建二叉树
  • 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

  • 思路: 必须注意到前序遍历的第一个节点是根节点,中序遍历的和根节点相同的那个节点(不含重复节点)是根节点且把树分成左右两个子树,左边子树是左子树,右边是右子树,左右两个子树也都是同样的规律,所以可以用递归来做,我递归写的不好,借鉴了别人的思路,自己改进了下。

class Solution {public:    TreeNode* reConstructBinaryTree(vector pre,vector vin) {        //如果size为0,则返回为空
        if(pre.size()==0||vin.size()==0)            return NULL;        
        vector pre_left,pre_right;    //前序遍历的左子树和右子树
        vector vin_left,vin_right;   //中序遍历的左子树和右子树
        //根据前序遍历的首节点为根节点这一个规律,找到中心节点,然后把原来的分成两个子二叉树,
        //准备使用递归来做。
        TreeNode *head=new TreeNode(pre[0]);   //调用构造函数先把第一个放进去。
        
        //找首节点在中序遍历中的位置。
        auto head_position=find(vin.begin(),vin.end(),head->val);        int position=head_position-vin.begin();   //这里找到是第几个,下面取构建。
        pre_left=vector(pre.begin()+1,pre.begin()+position+1);
        pre_right=vector(pre.begin()+position+1,pre.end());        //复制到前序遍历的左子树和右子树
        vin_left=vector(vin.begin(),vin.begin()+position);
        vin_right=vector(vin.begin()+position+1,vin.end());        //复制到中序遍历的左子树和右子树 
        
        //调用递归
        head->left=reConstructBinaryTree(pre_left,vin_left);
        head->right=reConstructBinaryTree(pre_right,vin_right);        
        //返回树
        return head;

    }
};
5. 用两个栈实现一个队列。
  • 题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

  • 思路:用一个栈来入栈,元素入栈的时候都从这里来入栈,如果要删除的话,则利用另外一个空栈来另一个的所有元素取出并压入空栈,然后出栈,每次出栈检查那个栈是否是空的,如果不是空的,直接出栈,空的话则从入栈的栈里出来。
    语言描述起来有点麻烦,看图:

class Solution{public:    void push(int node) {
        stack1.push(node);
    }    int pop() {        //题目要求返回删除的那个元素,C++satck的pop是不返回值得,而只是删除,所以就要自己来做了!
        int tmp;        if(stack2.empty())
        {            while(!stack1.empty())
            {
                stack2.push(stack1.top());
                stack1.pop();
            }
            tmp=stack2.top();
            stack2.pop();
        }        else 
        {
            tmp=stack2.top();
            stack2.pop();
        }        return tmp;
    }private:    stack stack1;    stack stack2;
};

另外,可用两个队列来做一个栈。
思路: 队列的话因为队列都是先进先出,所以如果把一个队列的数字全部复制到另外一个队列的话顺序是没有变的,所以有必要借助两个队列么? 自然是有必要的,虽然顺序没有变,但是我们可以在转移元素的时候把最后一个删除掉,也就是说入栈的时候挑非空的队列入栈,出栈的时候把非空的队列复制到空的中,复制过程中把最后一个元素删掉。

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

  • 思路: 首先找到两段的分界点,分界点的找法:找到第一个递减的数字,要考虑边界条件。这样循环 肯定是可以做的,但是既然是局部有序的,实际上是可以用二分法来做这个事情的。

class Solution {public:    int minSearch(vector rotateArray)
    {        if(rotateArray.size()==0) return 0;        if(rotateArray.size()==1) return rotateArray[0];        for(auto beg=rotateArray.begin();beg!=rotateArray.end()-1;beg++)
        {            if(*(beg+1)<*beg)   return *(beg+1);
        }        return rotateArray[0];  //如果上面没有返回则说明是全等的
    }    
    int minNumberInRotateArray(vector rotateArray)
    {        if(rotateArray.size()==0)  
           return 0;        int index1=0;        int index2=rotateArray.size()-1;        int indexMid=0;        while(rotateArray[index1]>=rotateArray[index2])
        {            if(index2-index1==1)      
            {
                indexMid=index2;                break;
            }
            indexMid=(index1+index2)/2;            if(rotateArray[index1]==rotateArray[indexMid]&&
              rotateArray[index2]==rotateArray[indexMid])
            {                return minSearch(rotateArray);
            }            if(rotateArray[index1]<=rotateArray[indexMid])
            {
                index1=indexMid;
            }            else if(rotateArray[indexMid]<=rotateArray[index2])
            {
                index2=indexMid;
            }
        }        return rotateArray[indexMid];
    }
};
7. 斐波拉切数列。
  • 题目: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
    n<=39 。

  • 思路: 有递归和循环两种写法,但是递归消耗的资源是很大的,所以还是推荐使用循环的方式来写这个。

class Solution {public:    int Fibonacci(int n) {        if(n<0)            return 0;        int first=0;        int second=1;        int tmp=0;        if(n==0) return 0;        if(n==1) return 1;        //只要有前两个树就能生成后面的所有的,只记录倒数两个数就可以
        for(int i=1;i8.跳台阶问题。
  • 问题:  一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

  • 思路: 见代码注释。

class Solution {public:    int jumpFloor(int number) {        //虽然是个跳台阶的问题,但是实际上和刚才做的那个斐波那契数列是一样的。
        
        if(number<=0)  return 0;        if(number==1)  return 1;        if(number==2)  return 2;        int first=1;        int second=2;        int tmp=0;        for(int i=2;i9.变态跳台阶问题。
  • 题目: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法

  • 思路: 见代码注释。

class Solution {public:    int jumpFloorII(int number) {        //题目虽然变难了,但是实际上程序是更简单了,结合上一个题的分析
        
        int first=1;        for(int i=1;i10. 矩形覆盖。
  • 题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

  • 思路: 见代码注释。

class Solution {public:    int rectCover(int number) {        //这个问题啊,仔细一想和跳台阶完全是一样的,而且时简单的跳台阶问题
        
        if(number<=0)  return 0;        if(number==1)  return 1;        if(number==2)  return 2;        
        int first=1;        int second=2;        int tmp=0;        
        for(int i=2;i11.二进制中1的个数。
  • 题目: 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

  • 思路: 见代码注释。

class Solution {public:     int  NumberOf1(int n) {         //这个题就是要注意到n&n-1的话会把二进制中少个1,当然自己一开始是注意不到这个技巧的,只是这个题我做过而已。
         
         int res=0;         while(n!=0)
         {
             n=n&(n-1);
             res++;
         }         return res;
         
     }
};
12.数值的整数次方。
  • 题目: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

  • 思路: 主要是考虑指数的各种情况就可以。

class Solution {public:    //题倒不难,主要是要考虑到各种情况。
    //可以优化的地方还有连乘那里,可以考虑用
    double Power(double base, int exponent)
    {        if(-0.0000000113.调整数组顺序使奇数位于偶数前面。
  • 题目: 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

  • 思路:第一种方法,利用冒泡排序,从后往前交换书序。第二种利用STL中的stable_partition和lambda表达式。

class Solution {public:    
    //类似于冒泡排序,但是是从后往前
    
    
    //使用C++自带的stable_partition和lambda表达式
    void reOrderArray(vector &array) 
    {
       stable_partition(array.begin(),array.end(),[](int x){return (x%2==1);});
    }
};
14.链表倒数第k个节点。
  • 题目:输入一个链表,输出该链表中倒数第k个结点。

  • 思路: 双指针,end先后移k个,然后first和end同时后移,end到null之后,first指向的就是倒数第k个。细微的+1或者-1自己画一下就知道了。

class Solution {public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {        if(pListHead==NULL) return NULL;
        ListNode *First=new ListNode(0);
        First->next=pListHead;        
        for(int i=0;inext;
        }        //if(pListHead==NULL) return First->next;
        
        while(pListHead!=NULL)
        {
            First=First->next;
            pListHead=pListHead->next;
        }        return First->next;
    }
};
15.翻转链表。
  • 题目:输入一个链表,反转链表后,输出新链表的表头。

  • 思路:一个一个翻转,只需要用一个ListNode的动态空间。我比较喜欢用假表头,所以代码可能看起来不是很简介,但是比较好理解。

class Solution {public:    //具体的思路就是一个一个翻转,只是用一个ListNode的动态空间,
    ListNode* ReverseList(ListNode* pHead) {        if(pHead==NULL||pHead->next==NULL)            return pHead;
        ListNode *First=new ListNode(0);    //用来记录最前面的位置,因为要往前插入。
        First->next=pHead;      //先放在最前面
        while(pHead->next!=NULL)    //停止条件
        {
            ListNode *tmp=new ListNode(0);
            tmp->next=pHead->next;             //要翻转的链表拿出来,tmp指向它
            pHead->next=pHead->next->next;     //把pHead指向下一个
            tmp->next->next=First->next;       //把tmp指向的插入First后面
            First->next=tmp->next;              //把First移到最前面
            delete tmp;
        }        return First->next;

    }
};
16.合并两个排序链表。
  • 题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

  • 思路:双指针最经典的题了,在归并排序的过程中也是比用的。

class Solution {public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {        if(pHead1==NULL)  return pHead2;        if(pHead2==NULL)  return pHead1;        //处理特殊情况
        
        ListNode *First=new ListNode(0);
        ListNode *Last=new ListNode(0);
        Last->next=First;        
        //双指针来做,两个都不为空的时候循环
        while(pHead1!=NULL&&pHead2!=NULL)
        {            if(pHead1->val<=pHead2->val)
            {
                Last->next->next=pHead1;
                Last->next=pHead1;
                pHead1=pHead1->next;
            }            else{
                Last->next->next=pHead2;
                Last->next=pHead2;
                pHead2=pHead2->next;
            }
        }        if(pHead1==NULL)
            Last->next->next=pHead2;        if(pHead2==NULL)
            Last->next->next=pHead1;        
        return First->next; 
    }
};
17.树的子结构。
  • 题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)。

  • 思路:辅助函数递归的判断两个树是否相等,主函数递归得判断是够有子树。

class Solution {public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        bool result=false;        if(pRoot1!=NULL&&pRoot2!=NULL)
        {            if(pRoot1->val==pRoot2->val)  //如果两个相等,则进行匹配
                result=DoseTree1HaveTree2(pRoot1,pRoot2);            if(!result)
                result=HasSubtree(pRoot1->left,pRoot2);            if(!result)
                result=HasSubtree(pRoot1->right,pRoot2);            //如果不等的话则分别递归查找其左边和右边
        }        return result;   //如果没找到这里还是false
    }
    
    bool DoseTree1HaveTree2(TreeNode* pRoot1,TreeNode* pRoot2)
    {        if(pRoot2==NULL)    //如果已经比遍历到pRoot2位空,那么说明是子树
            return true;        if(pRoot1==NULL)     //如果pRoot1为空pRoot2不为空(如果为空已经返回),则说明不是子树
            return false;        if(pRoot1->val!=pRoot2->val)  //如果存在两个值不相等,那么肯定也不是子树
            return false;        return DoseTree1HaveTree2(pRoot1->left,pRoot2->left)&&
            DoseTree1HaveTree2(pRoot1->right,pRoot2->right);        //剩下的就是递归得判断左右是否相等了。
    }
};
18.二叉树的镜像。
  • 题目: 操作给定的二叉树,将其变换为源二叉树的镜像。

  • 思路:递归的交换左右子树。交换节点需要使用动态空间。

class Solution {public:
    void Mirror(TreeNode *pRoot) {        //终止条件就是当前的节点为空
        if(pRoot==NULL)   return;        
        //交换节点
        TreeNode *tmp=new TreeNode(0);
        tmp->left=pRoot->left;
        tmp->right=pRoot->right;
        pRoot->left=tmp->right;
        pRoot->right=tmp->left;
        delete tmp;        
        //判断左右分别是为空,不为空的话递归的来求镜像
        if(pRoot->left)
            Mirror(pRoot->left);        if(pRoot->right)
            Mirror(pRoot->right);
           
    }
};
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 printMatrix(vector > matrix) {    
    int startX = 0;    int startY = 0;    int endY = matrix.size()-1;    int endX;    if (endY>=0)
        endX = matrix[0].size()-1;    else
        endX = 0;    vector res;    if (endY<0)
    {        return res;
    }    while (startX <= endX && startY <= endY)
    {        //从左至右,遍历一行
        if (startX <= endX && startY <= endY)
        {            for (int i = startX; i <= endX; i++)
            {
                res.push_back(matrix[startY][i]);
            }
        }
        startY++;    //遍历完一行之后,这一行就相当于去掉了,行++
        //
        if (startX <= endX && startY <= endY)
        {            for (int i = startY; i <=endY; i++)
            {
                res.push_back(matrix[i][endX]);
            }
        }
        endX--;     //遍历完右边一列,这一列去掉,endX--
        if (startX <= endX && startY <= endY)
        {            for (int i = endX; i >= startX; i--)
                res.push_back(matrix[endY][i]);
            
        }
        endY--;     //遍历完这一次,把最后一行去掉

        if (startX <= endX && startY <= endY)
        {            for (int i = endY; i >= startY; i--)
                res.push_back(matrix[i][startX]);
        }
        startX++;
    }    return res;

    }
};



作者:和蔼的zhxing
链接:https://www.jianshu.com/p/71529158fcc6


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/232470.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号