【剑指offer系列】系列二

系列二:10~19题

【系列一】:传送门       【剑指offer系列】:所有系列传送门          【系列三】:传送门

目录

系列二:10~19题

1、矩形覆盖【动态规划】

题目描述

1.1、分析

1.2、代码

 2、二进制中1的个数【进制转化】【补码反码原码】

题目描述

2.1、分析

2.2、代码

3、数值的整数次方【数学】

题目描述

3.1、分析

3.2、代码

4、调整数组顺序使奇数位于偶数前面【数组】

题目描述

4.1、分析

4.2、代码

5、链表中倒数第k个节点【链表】

题目描述

5.1、分析

5.2、代码

6、反转链表【链表】

题目描述

6.1、分析

6.2、代码

7、合并两个排序的链表【链表】

题目描述

7.1、分析

7.2、代码

8、树的子结构【二叉树】

题目描述

8.1、分析

8.2、代码

9、二叉树的镜像

题目描述

9.1、分析

9.2、代码

10、顺时针打印矩阵【数组】

题目描述

10.1、分析

10.2、代码

1、矩形覆盖【动态规划】

题目:矩形覆盖

题目描述

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

1.1、分析

1.2、代码

class Solution {
public:
    int rectCover(int number) {
        vector<int> f(number+1);
        f[1]=1,f[2]=2; //当长度为2时,可以两个横着放,也可以两个竖着放,所以有两种方法
        for(int i=3;i<=number;i++){
            f[i]=f[i-1]+f[i-2];
        }
        return f[number];
    }
};

 2、二进制中1的个数【进制转化】【补码反码原码】

题目:原题链接

题目描述

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

2.1、分析

      对于判断数值n的第i位是否为1方法是n>>i&1,所以我们只需要依次判断n的每一位是否为1,值得注意的是这里的n可能是负数,由于负数是用其补码表示的,我们1可以先对数据做一个转换就是把n转换成unsigned int类型,这个操作就是把原二进制表示进行了转换成表达式一样,但所表达的数值大小不一样了。

2.2、代码

class Solution {
public:
     int  NumberOf1(int n) {
         int res=0;
         unsigned int _n=n;  //处理负数情况
         while(_n){
             if(_n&1) res++;
             _n>>=1;
         }
         return res;
     }
};

3、数值的整数次方【数学】

题目:原题链接

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

保证base和exponent不同时为0

3.1、分析

     我们只需要把base循环乘exponent次即可,值得注意的是当exponent为负数的时候,需要对结果求一个倒数即可。

3.2、代码

class Solution {
public:
    double Power(double base, int exponent) {
        int t=abs(exponent);
        double res=1.0;
        for(int i=0;i<t;i++){
            res*=base;
        }
        return exponent<0?1.0/res:res;
    }
};

4、调整数组顺序使奇数位于偶数前面【数组】

题目:原题链接

题目描述

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

4.1、分析

       由于题目需要保持奇数和奇数,偶数和偶数之间的相对位置不变,所以不能用两个指针分别从首尾向中间遍历,我们在这里用插入排序的思想,当当前数是奇数时,我们需要在这数之前找一个偶数与之交换。

4.2、代码

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        for(int i=0;i<array.size();i++){
            if(array[i]%2==1){
                for(int j=i-1;j>=0;j--){
                    if(array[j]%2) break; //表示前面都是奇数
                    swap(array[j],array[j+1]);  //相连交换,保证偶数顺序不变
                }
            }
        }
    }
};

5、链表中倒数第k个节点【链表】

题目:原题链接

题目描述

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

5.1、分析

        在这里我们可以用双指针,先用一个指针向后移动k步,再把另一个指针从头开始,两个指针同时向后走,因为两个指针距离就是k,所以当第一个指针1走到最后时,第二个指针所指的数就是倒数第k个数。

5.2、代码

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* head, unsigned int k) {
        auto p=head;
        while(p&&k){
            p=p->next;
            k--;
        }
        if(k) return NULL;  //k可能大于链表长度
        auto q=head;
        while(p){
            p=p->next;
            q=q->next;
        }
        return q;
    }
};

6、反转链表【链表】

题目:原题链接

题目描述

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

6.1、分析

在这里用三个指针来遍历,a与b用于交换两两相连的节点。

6.2、代码

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(!pHead) return NULL;
        auto a=pHead,b=pHead->next;
        while(b){
            auto c=b->next;
            b->next=a;
            a=b;
            b=c;
        }
        pHead->next=NULL;
        return a;
    }
};

7、合并两个排序的链表【链表】

题目:原题链接

题目描述

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

7.1、分析

       我们分别遍历两个链表,把值更小的节点连接到最终的答案上,最后当有一个链表没遍历完时,直接把剩下的连接上即可。

7.2、代码

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        auto pre=new ListNode(-1);
        auto res=pre;
        while(pHead1&&pHead2){
            if(pHead1->val<pHead2->val){
                pre->next=pHead1;
                pHead1=pHead1->next;
            }
            else{
                pre->next=pHead2;
                pHead2=pHead2->next;
            }
            pre=pre->next;
        }
        //当其中有一个链表没遍历完时
        if(pHead1) pre->next=pHead1;
        if(pHead2) pre->next=pHead2;
        return res->next;
    }
};

8、树的子结构【二叉树】

题目:原题链接

题目描述

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

8.1、分析

     我们先把一个二叉树序列化,假设序列化结果为:A:1,2,4,#,#,3,#,#,5,#,#,;B:2,4,#,#,3,#,#,我们再把B序列后面的#和,删掉,因为B树不一定是A树的一个子树,所以现在问题就成了在A序列中找一个长度等于B序列长度的一个相等连续子序列,若有则返回true,没有则返回false。

8.2、代码

/*
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(!pRoot2) return false;
        string s1,s2;
        dfs(pRoot1,s1);
        dfs(pRoot2,s2);
        while(s2.back()==','||s2.back()=='#') s2.pop_back();
        for(int i=0;i<s1.size();i++){
            string t=s1.substr(i,s2.size());
            if(t==s2) return true;
        }
        return false;
    }
    //序列化二叉树
    void dfs(TreeNode* root,string &s){
        if(!root){
            s+="#,";
            return;
        }
        s+=to_string(root->val)+',';
        dfs(root->left,s);
        dfs(root->right,s);
    }
};

9、二叉树的镜像

题目:原题链接

题目描述

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

二叉树的镜像定义:源二叉树 
    	    8
    	   /  \
    	  6   10
    	 / \  / \
    	5  7 9 11
    	镜像二叉树
    	    8
    	   /  \
    	  10   6
    	 / \  / \
    	11 9 7  5

9.1、分析

       我们可以递归来做这个题,当节点为空时,直接返回,否则就交换左右节点,再递归处理左子树和右子树。

9.2、代码

/*
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) return;
        swap(pRoot->left,pRoot->right);
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
};

10、顺时针打印矩阵【数组】

题目:原题链接

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.

10.1、分析

     我们在这里定义两个方向数组,即每次遍历的方向,然后按照方向依次遍历,当前面没有数字且没出界时,就继续按照当前方向继续遍历,当不满足条件时则需要换方向了。

10.2、代码

class Solution {
public:
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; //方向数组
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int> res;
        if(matrix.empty()||matrix[0].empty()) return res;
        int n=matrix.size(),m=matrix[0].size();
        vector<vector<bool>> st(n,vector<bool>(m));
        int x=0,y=0,d=1;
        for(int i=0;i<m*n;i++){
            res.push_back(matrix[x][y]);
            st[x][y]=true; //用于记录当前格子已经填了数字
            int nexx=x+dx[d],nexy=y+dy[d];
            if(nexx>=0&&nexx<n&&nexy>=0&&nexy<m&&!st[nexx][nexy]){}
            else{ //不满足则换方向
                d=(d+1)%4;
                nexx=x+dx[d];
                nexy=y+dy[d];
            }
            x=nexx,y=nexy;
        }
        return res;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烊萌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值