刷题-剑指Offer 03&04

博主分享了自己在刷《剑指Offer》中遇到的题目,包括数组中重复数字的朴素解法和优化思路,以及二维数组中查找数字的错误与修正。强调了沟通询问需求、边界测试和编程规范的重要性,并对自己的学习过程进行了反思,认为编程经验需要不断积累和总结。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一些话

身处这个专业,身边的大佬们编程能力总是让人望尘莫及。早就觉得洛谷和力扣应该刷起来了,却总以“我又不着急找工作”“我也没法参加ACM比赛”为由。不知不觉大学已经过半,我们专业大四又全是实习(感觉大学又少了一年),真真是垂死病中惊坐起啊。不得不刷起来了。
大一时程设就因为种种原因与大佬们落下了差距,现如今不能再咸鱼下去了。先从剑指offer开始,尝试融会贯通编程语言、数据结构与算法。为什么不想直接工作还刷剑指offer呢,当然是为了之后实习面试准备呀(bushi,是因为它题最少哈哈哈)!我的设想是先拿C++通刷一遍(不追求时间空间开销,几乎都用蛮力法),之后或许会换python并逐渐优化一下开销。
大佬们都搭了自己的网站,我还是先把刷题的一些小心得写到csdn里,暑假有空的话再摸索着搭个站。

Offer 03 数组中重复的数字

找出数组中重复的数字。

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

来源:力扣(LeetCode)

哈哈,真的是很朴素的一道题了,既然找出任意一个数就行,就直接上最朴素蛮力法。Brute force yyds!大概就是设置了一个数组ifRepeated[]来记录,数组中第i个元素为true则表示数字i重复出现了。就是注意一下要先将所有值初始化为false,然后每遇到一个数字就改成true,修改前若检查为true即发现了第一个重复的数字,返回即可。

my 辣鸡 code

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int length = nums.size();
        bool *ifRepeated = new bool[length];
        for(int i = 0; i < length; i++)
            ifRepeated[i] = false;
        for(int i = 0; i < length; i++) {
            int j = nums[i];
            if(ifRepeated[j] == true) return j;
            else ifRepeated[j] = true;
        }
            return -1;
    }
};

更好的方法

根据变治法我们可以知道,对输入做做处理就往往可以减少开销!所以当然要预排序啦,排序后相邻的两数相等就说明它是重复出现的数字了。刚学完变治法都没有第一时间想到它,实在惭愧惭愧。

Offer 04

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

来源:力扣(LeetCode)

不多解释了,直接上蛮力法。这岂不easy,一遍过。

执行错误的辣鸡code

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        int i = 0, j = 0;
        int m = matrix.size(), n = matrix[0].size();
        for(i = 0; i < m; i++) {
            for(j = 0; j < n; j++) {
                if(matrix[i][j] == target) return true;
                if(matrix[i][j] > target) continue;
            }
        }
        return false;
    }
};

然后就报错

Line 1033: Char 9: runtime error: reference binding to null pointer of type ‘std::vector<int, std::allocator>’ (stl_vector.h)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/…/lib/gcc/x86_64-linux-gnu/9/…/…/…/…/include/c++/9/bits/stl_vector.h:1043:9

最后执行的输入:
[]
0

and I was like, what这还能错?输入为空的时候为啥能有毛病呢,不就是m、n为零,不进入循环、最后返回false就好了吗?不得不说,因为始终没有培养出边界测试的习惯,这个问题真的是百思不得其解、我改了好多次也没想明白。过了好久才发现问题出在

int m = matrix.size(), n = matrix[0].size();

即使matrix为空,这个操作m当然能得到0的值,但仔细想想才发现matrix[0]都不存在,怎么能对一个不存在的对象调用size()呢!简直是太愚蠢了啊……然后才终于AC了

正确的蛮力法code

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        int i = 0, j = 0;
        int m = matrix.size();
        if(m == 0) return false;
        int n = matrix[0].size();//matrix非空,matrix[0]才存在!
        for(i = 0; i < m; i++) {
            for(j = 0; j < n; j++) {
                if(matrix[i][j] == target) return true;
                if(matrix[i][j] > target) continue;
            }
        }
        return false;
    }
};

一点点小感想

对于Offer 03,评论区的一位老哥指出,

这道题在原书上绝对不是简单级别啊!
它考察的是程序员的沟通能力,先问面试官要时间/空间需求!!!
只是时间优先就用字典,
还有空间要求,就用指针+原地排序数组,
如果面试官要求空间O(1)并且不能修改原数组,还得写成二分法!!!

正确极了,沟通能力太重要了,甚至在敏捷开发中都占有一席。

对于Offer 04,只能说边界测试这种东西往往只有老师在文档中提出要求我才会想起去加。但对边界的保护应该是一个合格码农应铭记于心的东西,同理还有对于动态内存开辟后一定要记得收回(想起sj老师的银行ATM惨案哈哈哈)。
还有,对于题目中行列的递增特性可以抽象地理解为二叉搜索树,这样想到的话其实从右上角开始搜索就会快很多了。上学期学的ds仿佛都还给老师了,唉……

后记

想起大一室友荣誉课的作业里,对于输入卡得严极了。那时看到他们痛苦debug时只觉还好自己的作业简单,到今日被同学提醒要用大驼峰小驼峰命名、二元运算符两端要留空格时才悔不当初。编程这个东西,还得多吃堑才能长智啊!

基于python实现的粒子群的VRP(车辆配送路径规划)问建模求解+源码+项目文档+算法解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问时比较容易实现,而在VRP问上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问时比较容易实现,而在VRP问上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值