剑指 哦佛儿 力扣

本文介绍了数组中重复数字的查找、二维数组中特定值的判断、链表的逆序输出、重构二叉树及斐波那契数列等算法问题。涉及桶排序、集合、递归、二分查找等多种解法,并探讨了不同解法的时间复杂度和空间复杂度。此外,还展示了如何使用两个栈实现队列以及在矩阵中寻找特定路径的深度优先搜索(DFS)策略。
摘要由CSDN通过智能技术生成

本博客所有题目来源-------------力扣(剑指offer)【前10题 题解】


找出数组中重复的数字。


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

示例 1:

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

限制:

2 <= n <= 100000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解一:桶排序

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int a[100001];
        int j;
        memset(a,0,sizeof(a));
        for(auto x:nums){
            a[x]++;
        }
        for(auto x:nums){
            if(a[x]!=1){
                j=x;
                break;
            }
        }
        return j;
    }
};

题解二:set集合

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
       set<int> s;
       int j;
       for(auto x:nums){
         auto b=s.insert(x);
         if (b.second==false){
             j=x;
             break;
         }
       }
       return j;
    }
};

第三种 :萝卜与坑

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
      for(int i=0;i<nums.size();i++){
          while(nums[i]!=i){
              if(nums[i]==nums[nums[i]]){
                  return nums[i];
              }
            int temp =nums[i];
            nums[i]=nums[temp];
            nums[temp]=temp;
          }
      }
      return -1;
    }
};

这个是第三种解法的具体思路,挺不错的
据我目测这三种解法只有  1  --  3 种解法看起来用时短一点


在一个 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。

给定 target = 20,返回 false。

解法 : 利用数组特性 ,将数组倒转成为一个二叉搜索树 然后进行操作

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int> >& matrix, int target) {
        if(matrix.size()==0){
            return false;
        }
        int j=matrix[0].size()-1;
        int i=0;
        bool flag=false;
        while(j >= 0 && i<matrix.size()){
            if(matrix[i][j] > target){
                j--;
            }else if(matrix[i][j] < target){
                i++;
            }else{  
                flag=true;
                break;
            }
        }
        return flag;
    }
};

在这里插入图片描述
重点: 看图就行


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

 

示例 1:

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

解法一 :STL

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        stack<int> b;
        vector<int> a;
        ListNode *p=head;
        while(p!=NULL){
            b.push(p->val);
            p=p->next;
        }
        while(!b.empty()){
            a.push_back(b.top());
            b.pop();
        }
        return a;
    }
};

解法二 :递归

class Solution {
public:
    
    vector<int> reversePrint(ListNode* head) {
        if(head==NULL)
            return {};
        vector<int > a = reversePrint(head->next);
        a.push_back(head->val);
        return a;
    }
};

递归 并利用返回值,但是明显没有第一中时间复杂度和空间复杂度好


重构二叉树

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

 

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   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 {
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        this->preorder = preorder;
        for(int i = 0; i < inorder.size(); i++)
            dic[inorder[i]] = i;
        return recur(0, 0, inorder.size() - 1);
    }
private:
    vector<int> preorder;
    unordered_map<int, int> dic;
    TreeNode* recur(int root, int left, int right) { 
        if(left > right) return nullptr;                        // 递归终止
        TreeNode* node = new TreeNode(preorder[root]);          // 建立根节点
        int i = dic[preorder[root]];                            // 划分根节点、左子树、右子树
        node->left = recur(root + 1, left, i - 1);              // 开启左子树递归
        node->right = recur(root + i - left + 1, i + 1, right); // 开启右子树递归
        return node;                                            // 回溯返回根节点
    }
};

在这里插入图片描述
看图就可以认识 主要是下面的表

node->right = recur((root) + (i - left) + 1, i + 1, right);
//含义为 根节点索引 + 左子树长度 + 1

用两个栈来实现队列

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

 

示例 1:

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

输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:

1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用

这个是自己的蠢思路

class CQueue {
public:
    CQueue() {
        while (!a.empty()) {
            a.pop();
        }
        while (!b.empty()) {
            b.pop();
        }
    }
    
    void appendTail(int value) {
        a.push(value);
    }
    
    int deleteHead() {
        if(a.empty()){
            return -1;
        }
        while(!a.empty()){
            b.push(a.top());
            a.pop();
        }
        int delement = b.top();
        b.pop();
        while(!b.empty()){
            a.push(b.top());
            b.pop();
        }
        return delement;
    }
    stack<int> a;
    stack<int> b;
};

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

然后就

执行结果:
通过
显示详情
执行用时:
1084 ms, 在所有 C++ 提交中击败了10.88%的用户
内存消耗:
108.9 MB, 在所有 C++ 提交中击败了8.13%的用户

这是力扣官方的题解

class CQueue {
    stack<int> stack1,stack2;
public:
    CQueue() {
        while (!stack1.empty()) {
            stack1.pop();
        }
        while (!stack2.empty()) {
            stack2.pop();
        }
    }
    
    void appendTail(int value) {
        stack1.push(value);
    }
    
    int deleteHead() {
        // 如果第二个栈为空
        if (stack2.empty()) {
            while (!stack1.empty()) {
                stack2.push(stack1.top());
                stack1.pop();
            }
        } 
        if (stack2.empty()) {
            return -1;
        } else {
            int deleteItem = stack2.top();
            stack2.pop();
            return deleteItem;
        }
    }
};

然后就

执行结果:通过
显示详情
执行用时:516 ms, 在所有 C++ 提交中击败了71.50%的用户
内存消耗:101.5 MB, 在所有 C++ 提交中击败了73.60%的用户

这个题是真的恶心

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

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

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

 

示例 1:

输入:n = 2
输出:1
示例 2:

输入:n = 5
输出:5

一开始,我一看题,很快啊,这么简单,上来就是一个递归,递归,递归,结果很快啊,直接超时了,我说这道题你不讲武德,最后还是用了正常的方法做了。

class Solution {
public:
    int fib(int n) {
        if(n<2){
            return n;
        }
        int first = 0;
        int second = 1;
        int thrid;
        for(int i=1;i<n;i++ ){
            thrid=first+second;
             if(thrid>=1000000007){
                thrid=thrid%1000000007;
            }
            first=second;
            second=thrid;
        }
        return thrid;
    }
};

**又是一个斐波那契数列,*题目

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

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

示例 1:

输入:n = 2
输出:2
示例 2:

输入:n = 7
输出:21
示例 3:

输入:n = 0
输出:1
提示:

0 <= n <= 100

解法基本上和上面的题一模一样,我机会是copy上面的代码,所以

class Solution {
public:
    int numWays(int n) {
        if(n<2){
            return 1;
        }
        int first = 0;
        int second = 1;
        int thrid;
        for(int i=1;i<=n;i++ ){
            thrid=first+second;
             if(thrid>=1000000007){
                thrid=thrid%1000000007;
            }
            first=second;
            second=thrid;
        }
        return thrid;
    }
};

这道题目感觉还有那么一丁点意思,感觉自己用的方法比较蠢,然后就去看了题解,果然有更好的方法,但是这个方法应该在处理数据比较多的情况下应该会好一点,毕竟是二分的 时间复杂度 O(log 2^N)

再提一下:快排的时间复杂度:O(nlog2^n)

先看看自己的蠢代码吧

class Solution {
public:
    int minArray(vector<int>& numbers) {
        /*if(numbers.size()==0){
            return ;
        }*/
        bool flag=false;
        int pre=numbers[0];
        for(int i=1;i<numbers.size();i++){
            if(pre>numbers[i]){
                pre=numbers[i];
                flag=true;
                break;
            }
            pre=numbers[i];
        }
        if(flag==false){
            return numbers[0];
        }
        return pre;
    }
};

解题思路 : 就是找到第一个违反递增规则的数,如果没有则输出第一个数。哎!这种做法好像就是有点蠢,没有什么物理意义。哎~~感觉剑指offer上的题目大多都是技巧性很高的题目,在普通的题目上用不普通的解法去做,提高效率,不错不错。愚蠢的我要加油

解法二:二分

class Solution {
public:
    int minArray(vector<int>& numbers) {
      int left = 0;
      int right =numbers.size()-1;
      int mid;
      while ( left <  right){
          int mid = (left+right)/2;
          if(numbers[mid]<numbers[right]){
              right=mid;
          }else if(numbers[mid]>numbers[right]){
              left=mid+1;
          }else{
              right--;
          }
      }
      return numbers[right];
    }
};

放张图自己理解吧 ,这个人太懒了!!!!

在这里插入图片描述


class Solution {
    int tx,ty;
    int map[201][201]={0};
public:
    bool exist(vector<vector<char>>& board, string word) {
        
        tx=board.size();
        ty=board[0].size();
        auto x=word.begin();
        for(int i=0;i<tx;i++)
            for (int j=0 ;j<ty;j++){
                if(board[i][j]==*x){
                    dfs(i,j);
                    break;
                }
            }
    }
    void dfs(int x,int y){
        int px,py;
        map[x][y]=1;
        for(int i=0;i<4;i++){
            px=x+next[i][0];
            py=y+next[i][1];
            if(map[px][py]!=0){
                dfs(px,py);
            }

        }

    }
private:
    int next[4][2]={{1,0},{0,-1},{-1,0},{0,1}};
};

终于在第10题让我发现了dfs,先看题

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[["a","b","c","e"],
["s","f","c","s"],
["a","d","e","e"]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

 

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例 2:

输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false

这道题明眼人打眼一看就是dfs题,但是加了个小小的判断,俗称“剪枝”的过程,其实就是加了一个判断条件。其他的没什么大不了的,看代码就行,时间长没写过dfs了,这次是看了一下模板才写的,果然艾宾浩斯遗忘曲线太恐怖了。

下周就要期末了,而我还在刷题,夸张夸张。不说了复习去了,鄙人在这用看这篇博客的博人的3天遇不到妹子换我期末过!!谢谢朋友们

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值