Live2D

【剑指offer】刷题记录3~10

注:点击题目可直接跳转至leetcode相应的题目代码提交处

03. 数组中重复的数字

题目

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

限制:

2 <= n <= 100000

思路

首先看限制n的范围,发现10万并不算大,所以可以使用哈希,利用下标来计算每个数出现的次数,因为是随意输出一个重复的数字,可以在统计时就看是不是重复了,如果重复了就输出;

代码

int a[100001];
int findRepeatNumber(vector<int>& nums) {
      int n=nums.size();
      for(int i=0;i<n;i++)
      {
          int tmp=nums[i];
          a[tmp]+=1;
          if(a[tmp]>1)
          {
              return tmp;
          }
      }
      return 0;
}

04. 二维数组中的查找

题目

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

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。

思路

经过评论区提示,可以从右上角看这个矩阵,这样就变成了一个二叉搜索树,然后依次判断即可,注意不要超出矩阵范围。点matrix[i][j]的两个子节点是matrix[i][j-1]matrix[i+1][j]

代码

bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0||matrix[0].size()==0)
        {
            return false;
        }
        int line=matrix.size();
        int cor=matrix[0].size();
        int i=0;
        int j=cor-1;
        while(i<line&&j>=0)
        {
            if(matrix[i][j]==target)
            {
                return true;
            }
            else if(target<matrix[i][j])
            {
                j--;
            }
            else 
            {
                i++;
            }
        }
        return false;
    }

注意刚开始一定要检查空矩阵的情况,且一定要放在最前面检查,否则力扣会报错:Char 9: runtime error: reference binding to null pointer of type 'std::vector<int, std::allocator<int>>' (stl_vector.h)


05. 替换空格

题目

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."

限制:

0 <= s 的长度 <= 10000

思路

调用C++中固有的关于string类型的函数,详情见:https://www.cnblogs.com/wwj321/p/12381569.html

代码

string replaceSpace(string s) {
      int n=s.size();
      string s1="\%20";
      for(int i=0;i<n;i++)
      {
          if(s[i]==' ')
          {
              s.erase(i,1); 
              s.insert(i,s1);
              n=n+2;
              i=i+2;
          }
      }
      return s;
}

06. 从尾到头打印链表

题目

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]

限制:

0 <= 链表长度 <= 10000

思路

从反过来可以直接想到用栈。

代码

vector<int> reversePrint(ListNode* head) {
        stack<int> S;
        ListNode *h=head;
        while(h!=NULL)
        {
            S.push(h->val);
            h=h->next;
        }
        int num=S.size();
        vector<int> vec(num);
        for(int i=0;i<num;i++)
        {
            vec[i]=S.top();
            S.pop();
        }
        return vec;
}

07. 重建二叉树

题目

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路

例如有树:

     A
    /  \
   B    C

前序遍历,为ABC,中序遍历为:BAC;可现根据前序遍历找到根节点,然后根据中序遍历分成左右树,再递归左右子树进行建树;

代码

/**
 * 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.size()==0)
        {
            return NULL;
        }
        TreeNode* root;
        root =new TreeNode;
        root->left=NULL;
        root->val=preorder[0];
        root->right=NULL;
        int num=preorder.size();     //inorder的size也一样
        vector<int> inleft;
        vector<int> preleft;
        vector<int> preright;
        vector<int> inright;
        for(int i=0;i<num;i++)
        {
            if(i!=0)
            {
                 preleft.push_back(preorder[i]);
            }
            inleft.push_back(inorder[i]);
            if(inorder[i]==preorder[0])
            {
                for(int j=i+1;j<num;j++)
                {
                    preright.push_back(preorder[j]);
                    inright.push_back(inorder[j]);
                }
                root->left=buildTree(preleft,inleft);
                root->right=buildTree(preright,inright);
                return root;
            }
        }
        return root;
    }
};

我这个是将左右的代码都重新分配到新的数组了。也可以一直用原 有的两个数组,重新写一个递归的函数,将数组下标加入到参数中,这样不用讲原数组移动到新的数组。

关于treeNode定义,可以这样写:

TreeNode* root=new TreeNode(preorder[0]);

09. 用两个栈实现队列

题目

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

思路

队列:先进先出;栈:先进后出

将数据存储在一个栈中,当增加数据时,直接加在栈后面;当删除数据时,将所有数据弹入到另一个栈暂存,然后将栈底的数据删掉,再将暂存的数据弹回来。(有点像汉诺塔)

代码

class CQueue {
public:
    stack<int> S1;
    stack<int> S2;
    CQueue() {

    }
    
    void appendTail(int value) {
        S1.push(value);
    }
    
    int deleteHead() {
        if(S1.empty())
        {
            return -1;
        }
        while(!S1.empty())
        {
            int a=S1.top();
            S1.pop();
            S2.push(a);
        }
        int k=S2.top();
        S2.pop();
        while(!S2.empty())
        {
            int b=S2.top();
            S2.pop();
            S1.push(b);
        }
        return k;
    }
};

10- I. 斐波那契数列

题目

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

思路

递归会比较耗时好空间,斐波那契数列可以直接循环计算,不用递归。

代码

class Solution {
public:
    int fib(int n) {
        if(n==0)
        {
            return 0;
        }
        if(n==1)
        {
            return 1;
        }
        int tmp;
        int a,b;
        a=0;
        b=1;
        for(int i=2;i<=n;i++)
        {
            tmp=a+b;
            if(tmp>1000000007)
            {
                tmp=tmp-1000000007;   //注:这里原本是取余数运算,因为是一次次加的,
                                      //所以可以改成减法,减法计算速度快很多,
                                      //可以击败100%,如果是取余慢很多
            }
            a=b;
            b=tmp;
        }
        return tmp;
    }
};

10- II. 青蛙跳台阶问题

题目

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

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

思路

最简单的动态规划类题目,可以看作是斐波那契数列;

代码

class Solution {
public:
    int numWays(int n) {
        if(n==0)
        {
            return 1;
        }
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        int a,b;
        a=1;
        b=2;
        int tmp;
        for(int i=3;i<=n;i++)
        {
            tmp=a+b;
            if(tmp>1000000007)
            {
                tmp=tmp-1000000007;
            }
            a=b;
            b=tmp;
        }
        return tmp;
    }
};

后续:【剑指offer】刷题记录11~20

posted @ 2021-03-11 19:06  WSquareJ  阅读(62)  评论(0编辑  收藏  举报