C语言每日一练 —— 第21天:算法的应用

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言每日一练 —— 第21天:算法的应用相关的知识,希望对你有一定的参考价值。

文章目录

前言

为什么要学算法 ?
算法能给你带来什么能力提升?
算法在实际生中的应用有哪些?

  带着这些问题,我们开始我们今天的内容。直接来吧!

一、算法简介

  我曾经在某一个网站上看到一段非常触动我的话。我给大家念一念:

相信大家都听过
“面试造火箭,工作拧螺丝”
没错,
拧螺丝的人
只能一直留在岗位上拧螺丝。
而努力拧螺丝的人
说不定就有机会去造火箭。
而那些有造火箭能力的人
他们终究会造上火箭!

  是的,学习看似枯燥,然而,从中寻找出乐趣,并且偶尔给自己一些正反馈,带着激情,热情奔赴,迟早会有属于你的诗和远方。始终相信,星星之火,可以燎原。
  很多人学算法的初衷很简单,就是面试找工作进大厂,没错,不忘初心,方得始终。当然也有真正热爱算法之人,比如说我,以及正在看视频的你。然而真正踏入社会,进入工作,才是揭开你施展算法才华的序幕。

  生活当中处处是算法,那么就由我来简单介绍一下生活中的一些简单算法应用,然后再来讲讲大厂为什么这么注重算法,以及学好算法能够给你带来哪些方面的提升。

1、推荐算法

  你能看到我这个 视频,是因为你看了大量的算法、知识、计算机等等相关视频,所以算法将我推荐给了你,他认为你应该会喜欢看我的视频,当然,实际你喜不喜欢,还是要看你的反馈,比如说:是否投币、是否点赞、是否收藏、是否发布评论或者弹幕和我进行互动,根据你的反馈,我的视频可能会被推荐给更多和你一样志同道合之人。如果你觉得我说的有道理,也欢迎在弹幕里告诉我,让我增加一点点信心,以后出更加优质的视频。

2、最短路算法

  你在乘地铁的时候,如果有很多的换乘站,你就要对各种方案进行抉择选出一条最快的路或者选择一条换乘最少的路,这就是最短路问题。

3、最值算法

  比如说你乘完地铁,去吃自助餐,有牛排、大闸蟹、饮料 等等,你以哪种方式决定先吃什么?是不是你最喜欢吃的先吃?

那不就对应了在一个数组中找一个最大值吗?只不过把最大变成了最喜欢。

4、排序算法

  排序算法在现实生活中应该是非常常见的,比如幼儿园的小朋友排成一排,一般都是把最高的排在最后,也就是按照身高单调不降排序,这样才能保证每个同学都能看到老师在讲什么?

5、压缩算法

  你要上传一些东西到你的百度云网盘,但是内容量太大, 是不是要先压缩一下,压缩就是一种算法,他可以将原本空间占用很大的东西压缩的相对较小。

6、加密算法

  你有一些奇怪的内容,不想让别人看到,你就把它加密,并且保证只有你能够解密,这里的加密也是一种算法。

二、为什么要学算法

  为什么现在这么多大厂要求算法能力。

1、面试时

  其实在我毕业的那一年,也就是2010年的时候,感觉也没有太强调算法的能力。只要学校还行、学习成绩不错、笔试题不要被鄙视、稍微有点项目经历,基本就能轻松进入大厂。

  原因是因为那个时候,用户体量没有现在这么大,所以数据量也不会太大,自然对算法的要求就没有那么高了。你能把基础功能写出来,会一点简单的设计模式基本也就够用了。
  但是现在不行了,经济飞速发展,越来越多的人开始进入到了互联网行业中来,IT精英比比皆是,长江后浪推前浪。人才如泉涌般袭来,竞争也就越来越激烈。

  举个例子,如果你实现的功能和人数是呈平方关系的、或者立方关系的、甚至指数关系的,那么随着人数的增多,就会使这个功能消耗大量的CPU,而另一个人能够实现同样的功能,并且它的方法和人数呈线性关系。那么在同样数据量的情况下,他的方法肯定更优,我们也会优先录用他,这里的核心就是算法的时间复杂度。

  同样的,如果你实现的功能需要用到的空间和人数是呈平方甚至立方关系的,那么随着人数的增多,就会消耗大量的内存或者硬盘,而另一个人能够实现同样的功能,并且它的方法和人数呈线性关系,他的方法显然更优,我们就会优先录用他。这里的核心就是算法的空间复杂度。

  相信你一定听过 “空间换时间” 或者 “时间换空间” 这两个概念,只有学了算法,你才能掌握时间和空间的权衡,时间对应了计算机的CPU,空间对应了计算机的内存和硬盘。有关算法的时间复杂度和空间复杂度,后面我会详细进行介绍。

  由于算法是一个长期锻炼刷题才能养成的能力,不像八股文,你背下来,面试的时候遇到都能应对自如。算法不是,如果没有深入理解,很可能和面试官不在一个轨道上,所以会算法,至少能说明你是一个刻苦的人,能够为了自己的目标去努力,也肯定不会太笨。试想一下,你工作的时候,要交给你下属一个活,但是怎么说都听不懂,最后累的是你自己,这就是为什么很多大厂招聘的时候会把算法作为一个门槛。

2、工作中

  至于当你到了工作中,如果你没有学过算法,你觉得算法没用,你有工作经验,面试不管算法,哎,就是不学!就是不学!这时候,你发现遇到某个问题的时候,怎么想都没办法做到性能最优,这时候,一个同事缓缓走了过来,告诉你,这不是就是 平衡二叉树 嘛,然后他帮你把代码写了,性能比你快十倍!你觉得,作为你的上级,提拔你还是提拔他呢?

  很多人进入工作以后,觉得算法用不到,确实是用不到,但是会算法的那批人,工资就是比你高,如果你觉得无所谓,哎,我也无所谓了。
  所以,找到了工作并不意味着算法生涯的结束,恰恰是开始!那么,接下来我们看看算法能给我们带来什么。
  业务都会做,只有掌握了别人没法实现的东西,才是职场的核心竞争力。

三、算法能给我们带来什么能力的提升

  如果有人告诉你算法没用,那么只是他为了掩盖自己的平庸而找的借口而已。你可能会听到有写了几年业务的程序员学长说过 “除了面试,我就没用到过算法” 之类的话。
  能说出这样的话,首先肯定没有经过深思熟虑,脱口而出;其次,要么缺乏自信心,觉得自己一辈子都没办法学好算法了,所以就得过且过;要么就是故意误人子弟,不想让你超过他,青出于蓝胜于蓝。所以如果你已经看到这里,我希望你不要传播这样的思想,起码对这个学科保持基本的尊重。相信我,学好算法,一定会给你带来丰厚的回报。

  算法至少可以提升你的以下几个能力:
  1、抽象问题的能力
  2、解决问题的能力
  3、编码能力
  4、调试能力
  5、测试能力

1、抽象问题的能力

  大部分算法的问题不会直接告诉你用到的是什么样的算法,你需要通过你的思维进行抽象才能把它和自己学过的算法联系起来。抽象的过程就是锻炼思维的过程,而实际项目中问题会更加的宽泛,如何抽丝剥茧分解问题,并且和你做过的题联系起来也是一个逐渐实践,循序渐进的过程,切不可操之过急。

2、解决问题的能力

  任何问题都有解决办法,对于算法的题目,一般只有输入和输出,没有任何图形化界面。你不需要关心UI如何展示,只要你的输入按照预期进行输出,这样你就可以专注于解决问题本身,不需要考虑如何将它以什么形式展现出来,可以更加专注。

3、编写代码的能力

  编写代码的能力,我们一般也叫 coding 能力,在练习刷题的过程中,你可以不断提升你程序的时间效率和空间效率,不断优化你代码的质量,也可以看到别人提交的代码。为什么别人的代码跑的比你快,或许自己一个人怎么想都想不出来,但是一旦看到代码或者人家的思路,马上就茅塞顿开,豁然开朗。
  这是一个和世界顶尖程序员一起共事机会,可能在你的学校或者工作中用永远都没有这样的机会。这就是三人行必有吾师的道理,这是一个不断练习、总结、提升的过程。

4、调试能力

  相信很多人在做算法题的时候,都遇到过怎么改都改不出和测试用例一样的输出,特别是遇到指针的问题。二叉树或者链表的指针指来指去,根本不知道现在程序是怎么运作的。我这里有三个方法:
  1)画图
  2)调试
  3)实在不行,就用 print 大法

1)画图

  画图是让你在没有把问题想清楚的情况下,把问题具象化,这是一种艺术能力。熟能生巧,好好培养,有朝一日,必有收获,它能够帮助你更好的理解问题的本质。

2)调试

  调试是程序员的基本素养,能够精确到每一个语句、每一行代码、每一个变量在当前状态下的值,这样你就可以判断出问题出现在哪一行代码,更好的定位问题。

3)print 大法

  如果不会调试,那么就用 print 大法吧。每个变量的值改变以后,都把它打印出来看看,从而确定问题出错在哪一行代码,这是一种最傻瓜式然而也是相对比较常用的方式。初学者推荐用这种方法。

5、测试能力

  相信很多人在做算法题的时候,都遇到过测试用例都过了,但是最后题还是过不了的情况,那是因为测试用例一般会是大量的、并且考虑到大部分的情况,只要有一组数据失败就算不通过。这是一个非常严谨的过程,就好比工作中,测试工程师会用各种测试用例来测试你的代码,如果测试不通过,程序必然会产生BUG,这是需要严格杜绝的。
  所以,通过实现一个个算法,能够让你养成一个更加严谨的测试习惯,多考虑边界条件,多想想哪些地方会出问题,对日后工作有百利而无一害。

四、解决问题的方法

  面对一个算法问题,有没有一些常用套路,有是有,只不过问题越普适,就会越抽象,就算我说了,可能也不是很容易迅速理解。大方向就是分为以下几步:
  1)读懂题意
  2)查看问题的数据范围
  3)抽象出数据结构
  4)对数据结构执行增删改查

1、读懂题意

  试想一下,如果题目都没有读懂,光看测试数据,怎么可能把算法写出来,所以至少需要把题目先读懂,明白它是在解决什么问题,问题的输入是什么,输出是什么。

2、数据范围

  看数据范围的目的,就是为了让你能够第一时间了解,这是一个暴力算法能够解决的问题,还是需要用到高级数据结构,例如 平衡树、线段树、树状数组 才能解决的问题。
  对于初学者而言,无论数据范围大或者小,都建议先按照暴力思想,也就是普通枚举的方法先把问题思考一下,有了第一步,再去想利用什么数据结构能够优化算法。

3、抽象出数据结构

  一个算法,大概率下都会伴随着数据结构,例如 枚举可以建立在数组、链表、树 或者 图 上,二分枚举是建立在有序数组上的,二叉树遍历是建立在二叉树上的,最短路是建立在图上的 等等。所以,需要思考的是,这个问题需要用哪种数据结构,才能够满足增删改查都能够在题目要求的时间范围内。

4、增删改查

  所有数据结构,无非就是增删改查四种操作,没有其它的了。如何利用给定数据结构的特性,去优化你的算法,就是解题的核心。

五、经典算法易错问题

  这里列出了一些学习算法的过程中经常会遇到的问题。

1、内存泄漏

  手动申请的内存,即堆内存,注意在适当的时候进行释放,否则程序在多次运行以后,就可能导致内存泄漏,产生泄漏以后,程序只要不关闭,耗尽操作系统的内存,程序会产生崩溃。所以,内存泄漏一定是要尽量避免的。

2、栈溢出

  一般出现在递归问题中,函数的递归调用,会把一些临时变量存储在栈内存中,栈内存都是实现分配好的,所以递归深度达到一定限度,势必会引起内存溢出,所以我们在递归的时候一定要避免深度过大,或者函数内部的变量占用内存过多。

3、死循环

  递归问题中,如果递归出口没有写好,就有可能出现死递归或者死循环。while 或者 for 语句的循环结束条件如果没有写好,也有可能产生死循环,从而令程序永远执行不下去。

4、数组越界

  申请的数组是有长度大小的,用过访问的下标超过了数组本来有的长度,就会产生数组越界,数组越界是一种未定义的行为,有可能产生问题,也有可能不会产生问题,甚至可能在产生问题后一天以后才暴露出来,所以是一种非常危险的行为,一定要谨慎使用。

5、整型溢出

  整型溢出就是每个整数都是有范围的,如果经过某种运算,超过了给范围,就会和数学上的计算相违背,从而产生非预期的结果,比如等差数列的求和 n ∗ ( n + 1 ) / 2 n*(n+1)/2 n(n+1)/2,如果 n ∗ ( n + 1 ) / 2 n*(n+1)/2 n(n+1)/2 本身是没有溢出的,但是 n ∗ ( n + 1 ) n*(n+1) n(n+1) 溢出了,那么再除2的结果就和最后你期望的结果大相径庭了。
  以及在二分查找的过程中,不要直接用 l e f t left left r i g h t right right 相加除 2,而采用 l e f t + ( r i g h t − l e f t ) / 2 left + (right - left)/2 left+(rightleft)/2 的形式,都是为了避免溢出考虑的。

6、初始化问题

  初始化问题主要是定义一个变量没有给它赋值,后期在进行累加时,就会产生错误,比如你期望它的初始值是0,但是如果没有赋值,可能是一个随机值。这样就会和结果不符。

7、边界问题

  对于一个区间 [ l , r ] [l, r] [l,r],它的长度到底是 r − l r-l rl 还是 r − l + 1 r-l+1 rl+1,这也是一个老生常谈的问题,当然还有二分查找的边界,左区间 是加一还是不加,右区间是减一还是不减。

8、递归出口

  递归出口一般是递归的结束条件,也就是 深度优先搜索树 的最后一层,如果没有考虑周到,递归的作用可能被辜负。

9、变量表义不明晰

  某个变量本来想表示成这个物品是否合法,例如取名为 bCheck,然后后面实现的时候可能会产生疑惑,它到底是表示合法还是不合法?这就是变量名取名不清晰导致的,这个例子中比较好的办法就是改成 bValid。

10、运算符优先级问题

  比较经典的就是 异或 和 等于 的优先级,你觉得哪个高?如果对优先级没有信心,那就老老实实加括号吧,起码加了括号,你能确保优先级一定是对的,不用去管运算符的优先级和结合性。

11、等幂性

  一个函数如果能被多次重入,则说明它是等幂的,如果每次执行结果都不一样,或者说会改变一些函数内部的值,就要考虑它的算法实现了。

  诸如这类的内容量太大了,一句话简单也说不清楚,我打算后面专门开一个视频来讲解,并且把我自己遇到的各种错误进行一个总结。如果想听的小伙伴可以打个1,尽量快速安排上。

六、后记

  如果想听更多算法相关内容,欢迎大家在评论区留言,那么后面的内容我会逐步和大家聊聊:

  如何学好算法?
  如何学好数据结构?
  如何理解时间复杂度和空间复杂度?
  如何系统的刷题?
  如何通过题目信息判断这是一个什么样的算法题?
  如何将学过的算法运用到实际的做题当中?
  如何避免看过解题报告以后会了,下次遇到同样题目还是不会的窘境?
  如何通过报错信息判断到底是什么类型的错误?
  如何对测试用例进行调试?
  如何快速高效的把一个题过掉?
  如何爱上数学,爱上算法,爱上计算机,爱上编程?

以上是关于C语言每日一练 —— 第21天:算法的应用的主要内容,如果未能解决你的问题,请参考以下文章

C语言每日一练——第126天:佩奇借书问题

C语言每日一练——第147天:兔子产子问题

算法题每日一练---第51天:找不同

C语言每日一练 —— 第22天:零基础学习动态规划

C语言每日一练——第64天:自动发牌程序

C语言每日一练——第61天:掷骰子游戏