《算法和数据结构》初出茅庐篇

🧧2022 "算法" 赛道新星计划导师🧧

下滑到文末报名

前言

  你为什么要刷题!如果你只是因为想找到一份好的工作而来刷题,那我劝你放弃这个念头!因为,你坚持不了!任何带有功利性的行为,在没有得到应有的回报之前,都不能很好的坚持下来。刷题应该成为一种习惯,有了习惯就会有自驱力,养成习惯以后你就会爱上它,本文就来教你如何爱上刷题。


点击地址观看完整版

一、刷提前的准备

1、编程语言

  首先是编程语言。推荐学习 C语言, 因为C语言陪我度过了我的青春,让我的青春不留遗憾!当然,我是认真的,原因就是情怀。有情怀,才能有动力继续做下去。每当敲出这个符号->的时候,你是否有一丝悸动,这是C程序员的峥嵘岁月!

2、编程环境

  其次是编程环境。可以直接用 LeetCode 官方提供的环境,这样就躲过了安装环境的复杂步骤。很多人可能是被环境的配置所劝退的,这就太可惜了。
  所谓,工欲善其事,必先利其器,环境整好了,也就成功了一半!
  如下图所示:
  蓝色框 代表选择的语言;
  红色框 代表编码框;
  橙色框 代表测试用例框;
  黄色框 代表本地调试按钮;
  绿色框 代表提交按钮;
在这里插入图片描述

3、测试用例

  第三点,就是一定要学会写测试用例。当你答案错误的时候,千万不要着急,也不要急着找大神来看代码为什么错了,很多人一旦错误就把代码贴到群里让大家一起看,这样只能把别人的能力不断变强,自己则会越来越弱。
  根据题目的条件,造一些合适的数据,如果执行结果和官方的不一致,再去查代码错误的原因,就会更加有方向。

二、推荐的书

1、LeetCode零基础指南

  我推荐一开始不要看书。对于程序员来说,实践才是最重要的,你在敲代码的时候一定是在电脑边,所以可以网上找一些算法资料会比看书更有用。比如下面这个专栏就是为力扣零基础的玩家量身订做的:

🍠《LeetCode零基础指南》🍠

  这个专栏主要讲解了一些 LeetCode 刷题时的一些难点和要点,主要分为以下几个章节,并且会持续补充一些有关于方法论的文章。文章有试读,可以简单先看一看试读文章。

(第一讲)导读
(第二讲)函数
(第三讲)循环
(第四讲)数组
(第五讲)指针
(第六讲)排序
(第七讲)贪心
(第八讲)矩阵
(第九讲)二级指针
(第十讲)简单递归

2、算法导论

  很多人问这本书,这本书太过深奥,初学者我不太推荐去看。

三、零基础如何刷LeetCode

1、水题

  一开始千万不要急着学算法,就按照自己十几年的经验,找 100 道水题水一水,什么是水题,就是 通过的人多,并且 标签为简单 的题。这种题一定够水。类似问题一起刷,多练习,多思考,多总结,多复盘。马山就能凑够 100 道了。

2、多维思考

  一个题水过了以后,千万不要骄傲,想想其它办法,用多种方法把它过掉它。锻炼你的多路思维。比如一个问题你可能是通过暴力枚举的方法过掉的,它的时间复杂度可能是 O ( n 2 ) O(n^2) O(n2),可以试着用 二分查找 转换成 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),或者用 双指针 或者 哈希表 转换成 O ( n ) O(n) O(n) 等等。


  以上算法都可以在我的博客上找到:

暴力枚举
双指针
二分查找
哈希表

3、举一反三

  把一些看似不同的题整理在一起,并且用同一套代码来过掉它,这样以后遇到一个完全不同的题,你能够第一时间联想到之前做过的某道题,这就是 举一反三,触类旁通 的道理。
  例如,很多题目都可以用 动态规划 来做:

4、参加九日集训

  「 九日集训 」是博主推出的一个能够白嫖付费专栏「 LeetCode零基础指南 」的活动。通过 「 专栏中的联系方式 」 或者 「 本文末尾的联系方式 」 联系博主,进行报名即可参加。九日一个循环,第三期计划 「 2021.12.10 」 开启。

  玩法很简单,每天会开启一篇试读文章,要求有三点:
  1)阅读完文章后,课后习题 「 全部刷完 」(都能在文中找到解法,需要自己敲一遍代码);
  2)写 「 学习报告 」 并发布社区 九日集训(每日打卡) 频道
  3)在 「 打卡帖 」 提交 「 学习报告 」 链接;

  完成以上三点后方可晋级到下一天,所有坚持到 9天 的同学,会成为 「 英雄算法联盟合伙人 」 群成员(目前已有将近 100 位成员),只限500个名额,优胜劣汰,和精英在一起,无论是沟通,学习,都能有更好的发展,你接触到的人脉也都是不一样的,等找工作的时候,我也会为大家打通 hr 和猎头,让你前程无忧~
  详细规则参见:九日集训规则详解
  目前第一轮「 九日集训 」已经进行到第七天,即将开启第二轮。

四、如何学习算法

1、系统整理

  如果已经刷满100道水题以后,建议系统的学一下算法,然后刷满对应的题数:

算法题数
零基础算法50题
数学题10题
排序100题
贪心20题
位运算40题
链表100题
20题
队列20题
数组 / 线性枚举50题
字符串40题
前缀和10题
双指针20题
二分枚举30题
离散化10题
哈希表20题
深度优先搜索50题
二叉树20题
二叉搜索树20题
字典树10题
广度优先搜索50题
拓扑排序10题
最短路10题
最小生成树5题
并查集5题
10题
树状数组10题
线段树20题
KMP10题
动态规划专题100题
数据结构设计专题30题

2、解题划级

  这是我给 「 英雄算法联盟合伙人 」 指定的寒假训练计划,如果你有时间,有精力,有热血,想要可以的话,可以通过文末的名片联系到我。一起努力刷题!

五、零基础算法十题入门

1、求1+2+…+n

1. 问题描述

  求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

2. 问题分析
  首先,题目要求不用乘法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。那如果我用了会怎么样?答案是并不会怎么样,因为平台不会去对它做语法分析,只是调用了你的函数,提供一些输入数据,如果输出数据和它给定的相同,就算通过。
  作为你接触算法的第一道题,其实这些条件都无所谓的,能过就行,他只检测输入输出,不检测你实际代码。
  对于新人来说,把问题过掉比问题本身更重要,题数的增加,是信心的增加,信心比什么都重要,有了信心,你才能继续往下走,只要你能往下推进,你才能继续学习,继续学习你迟早会学到相应的算法。好了,过了这题以后,把这道题放入你的重刷列表,等你对算法有一定理解以后再来用题目要求的方法来过了它。

3. 源码详解

int sumNums(int n){
    return n * (n+1) / 2;  // (1)
}

   ( 1 ) (1) (1) 公差为 1 的等差数列求和公式,完事;


2、递归乘法

1. 问题描述

  递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。

2. 问题分析
  第一题做的时候,我说过什么来着?别想太多,记住,你还是小白的时候,千万不要想太多,绕过平台规则自由的飞翔,让你的进度往前推进,有信心以后再回来巩固和提高。

3. 源码详解

int multiply(int A, int B){
    return A * B;      // (1)
}
  • ( 1 ) (1) (1) 管他什么递归乘法,直接上乘法运算符;

3、斐波那契数

1. 问题描述

  斐波那契数,通常用 F ( n ) F(n) F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 0 0 1 1 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F ( 0 ) = 0 , F ( 1 ) = 1 F ( n ) = F ( n − 1 ) + F ( n − 2 ) , ( 1 < n ≤ 30 ) F(0) = 0,F(1) = 1 \\ F(n) = F(n - 1) + F(n - 2), (1 \lt n \le 30) F(0)=0F(1)=1F(n)=F(n1)+F(n2),(1<n30) 给你 n n n ,请计算 F ( n ) F(n) F(n)

2. 问题分析
  这个问题考察的是数组的递推,把 F ( i ) F(i) F(i) 理解成 C语言的数组,用 f [ i ] f[i] f[i] 来表示第 i i i 个斐波那契数,然后就是一个循环就可以解决了。

3. 源码详解

int f[31];                         // (1)
int fib(int n) {
    f[0] = 0;                      // (2)
    f[1] = 1;                      // (3)
    for(int i = 2; i <= n; ++i) {
        f[i] = f[i-1] + f[i-2];    // (4)
    }
    return f[n];                   // (5)
}
  • ( 1 ) (1) (1) 定义一个全局的辅助数组;
  • ( 2 ) − ( 3 ) (2)-(3) (2)(3) 初始化 F ( 0 ) = 0 , F ( 1 ) = 1 F(0) = 0,F(1) = 1 F(0)=0F(1)=1,分别存储到数组的第 0 个和第 1 个位置;
  • ( 4 ) (4) (4) 一层循环模拟递推公式;
  • ( 5 ) (5) (5) 返回第 n n n 个值 f [ n ] f[n] f[n],也就是斐波那契数列第 n n n 项;

4、n 的第 k 个因子

1. 问题描述

  给你两个正整数 n n n k k k,其中两者范围为 1 ≤ k ≤ n ≤ 1000 1 \le k \le n \le 1000 1kn1000。如果正整数 i i i 满足 n % i == 0,那么我们就说正整数 i i i 是整数 n n n 的因子。考虑整数 n n n 的所有因子,将它们 升序排列 。请你返回第 k k k 个因子。如果 n n n 的因子数少于 k k k ,请你返回 − 1 -1 1

2. 问题分析
  首先,对于 n n n 这个范围,它的因子数撑死 1000 1000 1000 个,实际上会少很多很多,有兴趣可以自己证明下,当然 夜深人静写算法(三)- 初等数论入门 也有关于因子相关的详细内容,不再累述。所以我们可以从 1 1 1 n n n 枚举,看哪些是 n n n 的因子,然后再用一个计数器计数,直到数到第 k k k 个就是我们需要求的答案了。
  如果全部枚举完,计数器都没有到 k k k,那么很显然,没有 k k k 个因子,直接返回 − 1 -1 1 即可。

3. 源码详解

int kthFactor(int n, int k){
    int i, cnt = 0;             // (1)
    for(i = 1; i <= n; ++i) {   // (2)
        if(n % i == 0) {        // (3)
            ++cnt;              // (4)
            if(cnt == k)
                return i;       // (5)
        }
    }
    return -1;                  // (6)
}
  • ( 1 ) (1) (1) 定义 c n t cnt cnt 为因子计数器;
  • ( 2 ) (2) (2) 1 1 1 n n n 枚举;
  • ( 3 ) (3) (3) 找到所有是 n n n 的因子的数 i i i
  • ( 4 ) (4) (4) 计数器加一;
  • ( 5 ) (5) (5) 如果计数器为 k k k 说明找到了第 k k k 个因子为 i i i,返回 i i i
  • ( 6 ) (6) (6) 如果全部枚举完,计数器都没有到 k k k,那么很显然,没有 k k k 个因子,直接返回 − 1 -1 1 即可;

5、统计平方和三元组的数目

1. 问题描述

  一个 平方和三元组 ( a , b , c ) (a,b,c) (a,b,c) 指的是满足 a 2 + b 2 = c 2 a^2 + b^2 = c^2 a2+b2=c2 的 整数 三元组 a a a b b b c c c。给你一个整数 n ( n ≤ 250 ) n(n \le 250) n(n250),请你返回满足 1 ≤ a , b , c ≤ n 1 \le a, b, c \le n 1a,b,cn 的 平方和三元组 的数目。

2. 问题分析
  首先,考虑最暴力的方法,就是三个数都枚举,然后判断等式是否成立,这样做的时间复杂度为 O ( n 3 ) O(n^3) O(n3)。但是我们可以明显的知道, c c c 一定是最大的,所以在枚举 c c c 的时候,可以从 a a a b b b 当中的大者开始,并且当 a 2 + b 2 < c 2 a^2 + b^2 \lt c^2 a2+b2<c2 时,就没必要再枚举 c c c,可以跳出循环,大大降低算法的时间复杂度。

3. 源码详解

int max(int a, int b) {
    return a > b ? a : b;                       // (1)
}

int countTriples(int n){
    int a, b, c, sum, ans = 0;
    for(a = 1; a <= n; ++a) {
        for(b = 1; b <= n; ++b) {
            sum = a*a + b*b;                    // (2)
            for(c = max(a, b)+1; c <= n; ++c) { // (3)
                if(sum == c*c) {
                    ++ans;                      // (4)
                    break;
                }
                if(sum < c*c) {
                    break;                      // (5)
                }

            }
        }
    }
    return ans;
}
  • ( 1 ) (1) (1) 提供一个函数,返回两者的最大值;
  • ( 2 ) (2) (2) 两层循环枚举 a a a b b b,得到它们的平方和 s u m sum sum
  • ( 3 ) (3) (3) 然后枚举 c c c c c c 一定比 a a a b b b 中的大者还要大,所以从 max(a,b)+1开始枚举;
  • ( 4 ) (4) (4) 找到一组满足条件的解后,退出循环;
  • ( 5 ) (5) (5) 由于 c c c 是从小到大枚举的,一旦发现 a 2 + b 2 < c 2 a^2 + b^2 \lt c^2 a2+b2<c2,则 c c c 再大也不可能满足 a 2 + b 2 = c 2 a^2 + b^2 = c^2 a2+b2=c2 了,可以直接退出循环不再继续往下枚举了。

6、找出数组的最大公约数

1. 问题描述

  给你一个整数数组 nums ,数组元素不大于 1000,返回数组中最大数和最小数的 最大公约数 。两个数的 最大公约数 是能够被两个数整除的最大正整数。

2. 问题分析
  两个数的最大公约数的计算方法有很多,由于这个问题中,所有数字都不大于 1000,所以求最大公约数的方法,就可以从大到小枚举其中一个数的约数,然后判断是否是另一个数的约数,如果是,则直接返回就行。

3. 源码详解

int gcd(int a, int b) {
    int i;
    for(i = a; i >= 1; --i) {
        if(a % i == 0 && b % i == 0) {
            return i;                       // (1)
        }
    }
    return 1;
}

int findGCD(int* nums, int numsSize){
    int i;
    int min = nums[0], max = nums[0];
    for(i = 1; i < numsSize; ++i) {
        if(nums[i] < min) min = nums[i];   // (2)
        if(nums[i] > max) max = nums[i];   // (3)
    }
    return gcd(min, max);                  // (4)
}
  • ( 1 ) (1) (1) 从大到小枚举其中一个数的约数,然后判断是否是另一个数的约数,如果是,则直接返回就行;
  • ( 2 ) (2) (2) 选择一个最小的数;
  • ( 3 ) (3) (3) 选择一个最大的数;
  • ( 4 ) (4) (4) 根据题意,返回最小的数和最大的数的最大公约数;

7、最大三角形面积

1. 问题描述

  给定包含 n ( n ≤ 50 ) n(n \le 50) n(n50) 个点的集合,从其中取三个点组成三角形,返回能组成的最大三角形的面积。

2. 问题分析
  枚举三个点,然后以这三个点组成一个三角形,计算面积,去其中最大的即可。三点计算三角形面积,可以用各种高中学过的公式来做。我这里采用叉乘求解,叉乘的更多内容可参考:夜深人静写算法(四)- 计算几何入门

3. 源码详解

double area(int *a, int *b, int *c) {
    return fabs( (b[0]-a[0]) * (c[1]-a[1]) - (b[1]-a[1]) * (c[0]-a[0]) ) / 2;
}

double largestTriangleArea(int** points, int pointsSize, int* pointsColSize){
    int i, j, k;
    double a, maxa = 0;
    for(i = 0; i < pointsSize; ++i) {
        for(j = 0; j < pointsSize; ++j) {
            for(k = 0; k < pointsSize; ++k) {
                a = area(points[i], points[j], points[k]);  // (1)
                if(a > maxa) {
                    maxa = a;                               // (2)
                }
            }
        }
    }
    return maxa;                                            // (3)
}
  • ( 1 ) (1) (1) 枚举三个点组成三角形计算面积;
  • ( 2 ) (2) (2) 取面积最大的保存下来;
  • ( 3 ) (3) (3) 返回最大的那个面积;

8、数组异或操作

1. 问题描述

  给你两个整数, n n n s t a r t start start。数组nums定义为:nums[i] = start + 2*i(下标从 0 开始)且 n == nums.length。请返回 nums中所有元素按位异或 X O R XOR XOR 后得到的结果。

2. 问题分析
  分两步模拟,先把所有数都通过规则生成出来。然后再将所有数异或,因为异或满足左结合律,所以可以一边生成,一边异或,最后返回所有数异或的和。

3. 源码详解

int xorOperation(int n, int start){
    int i, ans = 0;
    for(i = 0; i < n; ++i) {
        ans ^= start + i*2;           // (1)
    }
    return ans;
}
  • ( 1 ) (1) (1) 根据规则一边生成,一边异或,最后返回所有数异或后的结果;

9、整数的各位积和之差

1. 问题描述

  给你一个整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \le n \le 10^5) n(1n105),请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。

2. 问题分析
  首先,可以利用迭代将每位数字取出来,然后用两个变量,在迭代的过程中,分别保存它们的 积 与 和,然后再相减即可。

3. 源码详解

int subtractProductAndSum(int n){
    int prod = 1, sum = 0, digit;
    while(n) {
        digit = n % 10;    // (1)
        prod *= digit;     // (2)
        sum += digit;      // (3)
        n /= 10;           // (4)
    }
    return prod - sum;     // (5)
}
  • ( 1 ) (1) (1) 取当前数字的最低位;
  • ( 2 ) (2) (2) 将各位数字的乘积存储在prod上;
  • ( 3 ) (3) (3) 将给为数字的和存户在sum上;
  • ( 4 ) (4) (4) 将数字除10;
  • ( 5 ) (5) (5) 返回 乘积 - 和;

10、统计位数为偶数的数字

1. 问题描述

  给你一个整数数组 nums,请你返回其中位数为 偶数 的数字的个数。

2. 问题分析
  对每个数字不断除10,然后统计多少位,如果位数为偶数则计数器加一,最后返回计数器。

3. 源码详解

int findNumbers(int* nums, int numsSize){
    int i, bit, cnt = 0;
    for(i = 0; i < numsSize; ++i) {
        bit = 0;
        while(nums[i]) {
            nums[i] /= 10;
            ++bit;            // (1)
        }
        if(bit % 2 == 0) 
            ++cnt;            // (2)
    }
    return cnt;               // (3)
}
  • ( 1 ) (1) (1) 对于每个数,不断除10,用bit统计当前数的位数;
  • ( 2 ) (2) (2) 如果位数为偶数的数,则计数器自增一;
  • ( 3 ) (3) (3) 返回计数器;

六、如何总结

  刷题为主,总结为辅,一句话题解,然后发解题报告作为记录。
  阶段性,里程碑式的将学过的算法组织成自己的技能树将不会的算法也添加到自己的技能树中,找一块完整的时间,将技能树中没有学会的知识点学会,点亮技能树!

七、算法专栏推荐

专栏定位适宜人群
「 光天化日学C语言 」「 入门 」没有任何语言基础
「 LeetCode零基础指南 」「 初级 」零基础快速上手力扣
「 C语言入门100例 」「 中级 」零基础持续C语言练习教程
「 算法零基础100讲 」「 高级 」零基础持续算法练习教程
「 画解数据结构 」「 高级 」「 推荐 」 数据结构动图教程
「 算法进阶50讲 」「 资深 」进阶持续算法练习教程
「 LeetCode算法题集汇总 」「 资深 」全面的力扣算法题练习集锦
「 夜深人静写算法 」「 资级 」竞赛高端算法集锦

八、配套赠送福利

语言入门《光天化日学C语言》(示例代码)
语言训练《C语言入门100例》试用版
数据结构《画解数据结构》源码
算法入门《算法入门》指引
算法进阶《夜深人静写算法》算法模板

👇🏻 添加 博主 获取更多资料👇🏻
  • 131
    点赞
  • 399
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

英雄哪里出来

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

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

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

打赏作者

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

抵扣说明:

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

余额充值