剑指Offer 03-07题解

剑指Offer 03-07题解

目前我做过的剑指Offer…

剑指Offer 03. 数组中重复的数字

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

来源:力扣(LeetCode)

方法一: 哈希表

采用哈希表储存数字出现的的次数,当其大于1时,则表示该数字重复出现了。

代码 : 时间O(n) 空间O(n)

class Solution {
public:
    int findRepeatNumber(vector<int>& nums)
    {   
        unordered_map<int, int> nums_map;
        for (auto& num : nums)      //遍历数组nims
        {
            if (++nums_map[num] > 1)  //出现次数大于1
                return num;
        }
        return -1; //无重复返回-1
    }
};
方法二: 原地哈希

由于题目所给数组 nums 中的n个数字的范围是在 0~n-1 之间
所以 nums 数组中的每一个数字都可以有与其对应相等的数组下标。 即: nums[i]=i
移动数字到其对应下标的位置,若该位置已被占用且满足:nums[i]=i ,则表示该数字重复出现

代码 : 时间O(n) 空间O(1)

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) 
    {
        for(int i=0;i<nums.size();++i)
            {
                while(nums[i]!=i) //判断i是否在其对应下标的位置
                {
                    if(nums[i]==nums[nums[i]])  //看i对应下标的位置是否被占用
                        return nums[i];         //是 则i重复
                    swap(nums[i],nums[nums[i]]);//将i换到与他对应的下标位置
                }
            }
        return -1;
	}
};
方法三: 快排

将数组 nums 进行快速排序,然后对排序好的数组遍历当满足:nums[i]==nums[i+1] 表示 nums[i] 重复

代码: 时间(nlogn) 空间(1)

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end()); 
        for(int i=0;i<nums.size()-1;++i)
            if(nums[i]==nums[i+1])      //有序数组中 前后相等表示重复
                return nums[i];
        return -1;
	}
};

剑指Offer 04. 二维数组中的查找

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

来源:力扣(LeetCode)

方法一: 暴力

两重for循环 暴力解决 时间(nm)空间(1)

方法一: 二叉搜索树

因为数组 从左至右递增 从上至下递增 所以将其旋转四十五度就相当于同一个二叉搜索树
从根节点(matrix[0][m-1]) 开始左边更小右边更大

代码:时间(n+m) 空间(1)

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target)
    {
        int n=matrix.size()-1;  // 行数
        if(n<0)                 // 判断行数是否为空 (为空 则:数组为空)
        return false;
        int j=matrix[0].size()-1; // 列数
        if(j<0)                   // 判断列数是否为空
        return false;
        int i=0;
        while(matrix[i][j]!=target)  // 对比 
        {
            matrix[i][j]>target?--j:++i;  // 大减(左移) 小加(下移)
            if(j<0||i>n)           // 判断是否遍历过界
            return false;
        }
        return true;
    }
};

暂时没有更优方法…待补…


剑指Offer 05. 替换空格

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

来源:力扣(LeetCode)

方法一: 遍历写入新字符串

创建一个一空字符串ss,遍历字符串s,当为字符时写入ss 为 “ ” 时写入 “%20”

时间 (n) 空间(1)

class Solution {
public:
    string replaceSpace(string s) {
	   string ss;
        for(auto &c:s)
        {
            if(c==' ')
                ss+="%20";
            else
                ss+=c;
        }
        return ss;
    }
};

剑指Offer 06. 从尾到头打印链表

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

来源:力扣(LeetCode)

方法一: 数组反转

直接将节点信息push入数组中,再将数组中的数据反转

代码: 时间(n) 空间(1)

/**
 * 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) {
       vector<int> nums;
       int i=0;
       while(head!=nullptr)
       {
           nums.push_back(head->val);
           ++i;			// 记录数组元素个数
           head=head->next;
       }
       int num;  //交换时用到
       int j=0;
       for( j,--i;i>j;--i,++j)	// 头尾元素互换(即 反转数组)
       {
           num=nums[i];
           nums[i]=nums[j];
           nums[j]=num;
       }
       return nums;
    }
};
方法二: 递归

通过函数遍历到链表尾部 ,在层层返回函数对数组插入值 (注意栈溢问题)

代码: 时间(n)空间(n)

class Solution {
public:
    void digui(vector<int> &nums,ListNode *head)
    {
        if(head==nullptr)		//判断是否到链表尾部
            return ;
        digui(nums,head->next);   
        nums.push_back(head->val); 
    }
    vector<int> reversePrint(ListNode* head) {
        vector<int> nums;
        digui(nums,head);
        return nums;
    }
};

剑指Offer 07. 重建二叉树

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

来源:力扣(LeetCode)

方法一: 分治递归

前序遍历顺序为 root ->left ->right
中序遍历顺序为 left ->root ->right
前序遍历的每一个结果 对应在中序遍历里 root 的位置将其分为左子树和右子树
在对左右子树[中序遍历的左右子区间]分别进行上述操作即可重建二叉树
定位方式:<注意仅限此题,若有重复元素定位会出错>
利用哈希表定位前序遍历中每个元素在中序遍历中的位置

代码: 时间(n) 空间(n)

/**
 * 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) {
        this->preorder=preorder;     //递归中要使用preorder ,so 将其内容传到整个类
        for(int i=0;i<inorder.size();++i)
            map[inorder[i]]=i;		//利用哈希表定位perorder每个元素在inorder中的位置
        return recur(0,0,inorder.size()-1);

    }
    TreeNode* recur(int root,int left,int right)
    {   //root 根节点在perorder的位置 
        //left 子区间左边界
        //right子区间右边界
        if(left>right) return nullptr;	//判空(左/右子区间为空时 结束)
        TreeNode *node=new TreeNode(preorder[root]);
        int i=map[preorder[root]];    	//使用哈希定位的位置
        node->left=recur(root+1,left,i-1);	//左区间
        node->right=recur(root+i+1-left,i+1,right);	//右区间
        return node;
    }
    unordered_map<int ,int> map;  //哈希定位用
    vector<int> preorder;		
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值