A. 学编程需要哪些数学知识
1.学习方法:本人认为这比什么都重要如果这个没掌握的话,可能直接影响你的成败。众所周知。。计算机知识 尤其是编程涉及到的知识可以说浩如烟海---那么面对这么多的知识该怎么去学呢?
---重点:1重实践,不要去想,把一个知识点完全彻底的掌握,那将是非常恐怖的,有编程经验的朋友都知道,编程里每个知识点深纠起来的话是非常困难的,更不要说是新手了。。那么知识点该掌握到什么程度呢? 个人认为:1-知道它是做什么 2-知道怎么使用。 这就足够了。。。。不要去管他的原理是什么,能把东西做出来才是王道。。。
---重点:2多写, 这个在编程界可以说是真理了,真正写程序的人都知道,一段程序你理解了并不代表你就会写了,那么怎么样才能提高“写”的能力呢? 本人认为要注意一下几点 1- 练习多做是必然的。 2- 做练习时不要因为觉得代码简单就只看不敲,哪怕多敲一遍HelloWorld 都是有好处的。 3- 相似的代码不要复制,我见过很多朋友,遇到两段程序类似,就懒的敲直接粘贴过去修改。。。请记住这是软件开发人员的做法,而你不是,目前你还只是一个学习者而已。所以 原则就是 能敲的就不要复制。
---重点:3把精力用在理解上而不要用在背上 写程序的朋友都知道,函数---关键字---常用类什么的,都非常的熟悉,为什么我们背过吗?没有 写的多了自然就记的牢了, 所以建议新手不要去死背什么概念,或语法 一定要理解它的作用。。。
---重点:4 笔记,我认为这点很重要,我自学时全是看书,和视频教程,然后总结对自己有用的东西。记在本上,而将来如果印象不深刻了由于是自己用自己理解的方式写的,简单翻一翻就能回忆起来,而如果,你忘了再去翻视频 或 翻书的话。。那么即使你曾经学过,也可能一时想不起来。。。
1.关于数学。。。这个问题,我觉得是目前争论最多的话题,我见过N多人说 学编程要学XX数学---什么微积分---什么离散---吓的新手连想都不敢想,我只想对这些人说一句,如果你懂,请你们帮助新手,如果你们不懂 请你们闭嘴 谢谢不要 误人子弟。。。那么下面我来 具体回答一下数学方面的问题。。。
1- 编程用数学吗? 用! 回答是肯定的,但要看你是做哪方面的程序。 懂编程的都知道,现在编程基本分B/C构架,即:客户端/浏览器端 与 C/S构架 即:客户端/服务器端 前者基本上就是JAVA PHP ASP.NET 等等。。。其中有多少地方用到了数学,如果还坚持没数学学不了编程的朋友请站出来回答下我的问题。。。
至于C/S 如果不是做系统级的程序员 或 大型3D图象处理 或者是音频处理的软件我请问又有多少地方用到了数学?如果你觉得x/y=z 这也算高等数学的话,我无话可说。。。。总结--除了3D等图象处理编程 或 音频处理编程 或系统级编程以外 其他编程对数学要求并不很高。。。。
2.关于英语, 我认为这个是个不可回避的话题,学编程一点英语不懂我觉得不太现实,毕竟有很多文档也是用英文写的,而且程序员都知道,编程时经常要用简单的英文,哪怕是定义个变量名,也要用英文起名, 没见过哪个程序员定义的变量叫什么aaa或bbb的。 那么新手该怎么面对英语呢, 我觉得很容易,按照书上或教程上去做就足够了,1 编写程序时 按规范要求去做,首先变量名,用见名知意思的英文单词, 写注释时 也用英文短句。。。 抛异常时 也用英文来标注等等。。。。慢慢积累,时间久了你就会发现其实计算机里的英语 就只有那么几句而已。。。
3.关于学校 这个我也想提一下,有很多想以程序员为工作的朋友可能都考虑过找个培训班---但我的建议是。不要去---起码一般的不要去,为什么?效果不好,就这么简单,我亲自到XXX着名编程培训学校试听过。。。结果很遗憾 一周才那么几天课,我3天阅读的知识点比他们1个兴趣 教的还多。。。而且上机和理论还是分开,新学的知识不能立刻上机实践等等。。我觉得都是很严重的弊病。。。跟严重的那些所谓的学校给学生们造成了一种假象。。。只要在学校里考试合格了,出去就能做程序员,甚至软件工程师了。。。最后他们将发现,原来他们在学校里学的 只是基础中的基础而已 - -
4. 自学的资料,我个人认为,自学第一重要的是 视频教程,懂的人都知道,编程学习时重点并不完全是知识点,而是如何运用那些知识点,这也是项目经验今天被人们这么看中的主要原因。。。所以视频教程绝对是不二的选择,现在网上的视频教程非常之多 各种各样的都有 具体怎么找相信不用我教了 google 电驴 迅雷--我就是靠他们活过来的 。。。而且视频教程还有一点是学校比不了的,那就是 你可以随时看 重复看,一个知识点没明白 你可以反复的听10遍 20遍都没问题, 学校恐怕就不行了吧。 另一个优点是可以在你状态好时看, 大家都有状态不好的时候,累了-困了 很正常,可在学校,谁管你? 老师讲完了 听不懂你自己的问题,而视频呢,好办 累了 先休息一会 有精神了 想怎么看就怎么看。。。我觉得 找到好的视频教程。。比任何老师都重要。。至于出现问题不懂怎么办? 相信能来到着找到我这篇文章的朋友 都有办法解决的。。
5.书 --- 我非常喜欢看视频教程,但我坚决反对只看视频不看书,为什么?很简单视频传授的是 写程序的经验 而书则是细腻的为你讲解其中的原理。。所以我的建议是 先把一个知识的视频看一遍,然后再把书翻一遍 然后自己再写2遍 量变必然引起质变 我相信这是放之四海 而皆准的道理(指编程行业)
6.时间+态度 我认为这也很重要,很多人经常这样问我,我1个月能学会编程么? 我半年能成为编程高手么? 我觉得有这样心理的人比适合学编程。。。 学编程最忌心浮,一个知识点还没弄明白 就想写个项目出来 这是不可能的,这样最后只能导致你自己丧失信心,编程要一步一步的来,相信我哪怕用一天时间才掌握了一个知识点,起码比你用一天的时间 看完整本书强。。因为前者起码你还是有点收获的(指新手,老手两天一本书很正常有经验了吗 - -) 这里我可以给大家一我的学习时间大家可做为参考。。。我是从0基础开始一直到现在掌握j2ee基本所有的基础开发技能 用时一年半,本人觉得不算慢 每天最少看书+练习5小时 每天不停这个是我的进度。
此文献给想学编程却又碍于各个方面左右不定的人,和正在学的初学者!
参考文献:http://hi..com/vigorlin/profile!
B. 作为一个程序员,有哪些常用的算法
常用的算法有:递推法、贪心法、列举法、递归法、分治法和模拟法
原则:1. 扎实的基础。数据结构、离散数学、编译原理,这些是所有计算机科学的基础,如果不掌握他们,很难写出高水平的程序。据我的观察,学计算机专业的人比学其他专业的人更能写出高质量的软件。程序人人都会写,但当你发现写到一定程度很难再提高的时候,就应该想想是不是要回过头来学学这些最基本的理论。不要一开始就去学OOP,即使你再精通OOP,遇到一些基本算法的时候可能也会束手无策。
2. 丰富的想象力。不要拘泥于固定的思维方式,遇到问题的时候要多想几种解决问题的方案,试试别人从没想过的方法。丰富的想象力是建立在丰富的知识的基础上,除计算机以外,多涉猎其他的学科,比如天文、物理、数学等等。另外,多看科幻电影也是一个很好的途径。
3. 最简单的是最好的。这也许是所有科学都遵循的一条准则,如此复杂的质能互换原理在爱因斯坦眼里不过是一个简单得不能再简单的公式:E=mc2。简单的方法更容易被人理解,更容易实现,也更容易维护。遇到问题时要优先考虑最简单的方案,只有简单方案不能满足要求时再考虑复杂的方案。
4. 不钻牛角尖。当你遇到障碍的时候,不妨暂时远离电脑,看看窗外的风景,听听轻音乐,和朋友聊聊天。当我遇到难题的时候会去玩游戏,而且是那种极暴力的打斗类游戏,当负责游戏的那部分大脑细胞极度亢奋的时候,负责编程的那部分大脑细胞就得到了充分的休息。当重新开始工作的时候,我会发现那些难题现在竟然可以迎刃而解。
5. 对答案的渴求。人类自然科学的发展史就是一个渴求得到答案的过程,即使只能知道答案的一小部分也值得我们去付出。只要你坚定信念,一定要找到问题的答案,你才会付出精力去探索,即使最后没有得到答案,在过程中你也会学到很多东西。
6. 多与别人交流。三人行必有我师,也许在一次和别人不经意的谈话中,就可以迸出灵感的火花。多上上网,看看别人对同一问题的看法,会给你很大的启发。
7. 良好的编程风格。注意养成良好的习惯,代码的缩进编排,变量的命名规则要始终保持一致。大家都知道如何排除代码中错误,却往往忽视了对注释的排错。注释是程序的一个重要组成部分,它可以使你的代码更容易理解,而如果代码已经清楚地表达了你的思想,就不必再加注释了,如果注释和代码不一致,那就更加糟糕。
8. 韧性和毅力。这也许是"高手"和一般程序员最大的区别。A good programming is 99 weat and 1 ffee。高手们并不是天才,他们是在无数个日日夜夜中磨练出来的。成功能给我们带来无比的喜悦,但过程却是无比的枯燥乏味。你不妨做个测试,找个10000以内的素数表,把它们全都抄下来,然后再检查三遍,如果能够不间断地完成这一工作,你就可以满足这一条。
希望对你有帮助
C. 程序员必须掌握哪些算法
一.基本算法:
枚举. (poj1753,poj2965)
贪心(poj1328,poj2109,poj2586)
递归和分治法.
递推.
构造法.(poj3295)
模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996)
二.图算法:
图的深度优先遍历和广度优先遍历.
最短路径算法(dijkstra,bellman-ford,floyd,heap+dijkstra)
(poj1860,poj3259,poj1062,poj2253,poj1125,poj2240)
最小生成树算法(prim,kruskal)
(poj1789,poj2485,poj1258,poj3026)
拓扑排序 (poj1094)
二分图的最大匹配 (匈牙利算法) (poj3041,poj3020)
最大流的增广路算法(KM算法). (poj1459,poj3436)
三.数据结构.
串 (poj1035,poj3080,poj1936)
排序(快排、归并排(与逆序数有关)、堆排) (poj2388,poj2299)
简单并查集的应用.
哈希表和二分查找等高效查找法(数的Hash,串的Hash)
(poj3349,poj3274,POJ2151,poj1840,poj2002,poj2503)
哈夫曼树(poj3253)
堆
trie树(静态建树、动态建树) (poj2513)
四.简单搜索
深度优先搜索 (poj2488,poj3083,poj3009,poj1321,poj2251)
广度优先搜索(poj3278,poj1426,poj3126,poj3087.poj3414)
简单搜索技巧和剪枝(poj2531,poj1416,poj2676,1129)
五.动态规划
背包问题. (poj1837,poj1276)
型如下表的简单DP(可参考lrj的书 page149):
E[j]=opt{D+w(i,j)} (poj3267,poj1836,poj1260,poj2533)
E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1]+zij} (最长公共子序列) (poj3176,poj1080,poj1159)
C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最优二分检索树问题)
六.数学
组合数学:
1.加法原理和乘法原理.
2.排列组合.
3.递推关系.
(POJ3252,poj1850,poj1019,poj1942)
数论.
1.素数与整除问题
2.进制位.
3.同余模运算.
(poj2635, poj3292,poj1845,poj2115)
计算方法.
1.二分法求解单调函数相关知识.(poj3273,poj3258,poj1905,poj3122)
七.计算几何学.
几何公式.
叉积和点积的运用(如线段相交的判定,点到线段的距离等). (poj2031,poj1039)
多边型的简单算法(求面积)和相关判定(点在多边型内,多边型是否相交)
(poj1408,poj1584)
凸包. (poj2187,poj1113)
中级(校赛压轴及省赛中等难度):
一.基本算法:
C++的标准模版库的应用. (poj3096,poj3007)
较为复杂的模拟题的训练(poj3393,poj1472,poj3371,poj1027,poj2706)
二.图算法:
差分约束系统的建立和求解. (poj1201,poj2983)
最小费用最大流(poj2516,poj2516,poj2195)
双连通分量(poj2942)
强连通分支及其缩点.(poj2186)
图的割边和割点(poj3352)
最小割模型、网络流规约(poj3308)
三.数据结构.
线段树. (poj2528,poj2828,poj2777,poj2886,poj2750)
静态二叉检索树. (poj2482,poj2352)
树状树组(poj1195,poj3321)
RMQ. (poj3264,poj3368)
并查集的高级应用. (poj1703,2492)
KMP算法. (poj1961,poj2406)
四.搜索
最优化剪枝和可行性剪枝
搜索的技巧和优化 (poj3411,poj1724)
记忆化搜索(poj3373,poj1691)
五.动态规划
较为复杂的动态规划(如动态规划解特别的旅行商TSP问题等)
(poj1191,poj1054,poj3280,poj2029,poj2948,poj1925,poj3034)
记录状态的动态规划. (POJ3254,poj2411,poj1185)
树型动态规划(poj2057,poj1947,poj2486,poj3140)
六.数学
组合数学:
1.容斥原理.
2.抽屉原理.
3.置换群与Polya定理(poj1286,poj2409,poj3270,poj1026).
4.递推关系和母函数.
数学.
1.高斯消元法(poj2947,poj1487, poj2065,poj1166,poj1222)
2.概率问题. (poj3071,poj3440)
3.GCD、扩展的欧几里德(中国剩余定理) (poj3101)
计算方法.
1.0/1分数规划. (poj2976)
2.三分法求解单峰(单谷)的极值.
3.矩阵法(poj3150,poj3422,poj3070)
4.迭代逼近(poj3301)
随机化算法(poj3318,poj2454)
杂题(poj1870,poj3296,poj3286,poj1095)
七.计算几何学.
坐标离散化.
扫描线算法(例如求矩形的面积和周长并,常和线段树或堆一起使用)
(poj1765,poj1177,poj1151,poj3277,poj2280,poj3004)
多边形的内核(半平面交)(poj3130,poj3335)
几何工具的综合应用.(poj1819,poj1066,poj2043,poj3227,poj2165,poj3429)
高级(regional中等难度):
一.基本算法要求:
代码快速写成,精简但不失风格
(poj2525,poj1684,poj1421,poj1048,poj2050,poj3306)
保证正确性和高效性. poj3434
二.图算法:
度限制最小生成树和第K最短路. (poj1639)
最短路,最小生成树,二分图,最大流问题的相关理论(主要是模型建立和求解)
(poj3155, poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446
最优比率生成树. (poj2728)
最小树形图(poj3164)
次小生成树.
无向图、有向图的最小环
三.数据结构.
trie图的建立和应用. (poj2778)
LCA和RMQ问题(LCA(最近公共祖先问题) 有离线算法(并查集+dfs) 和 在线算法(RMQ+dfs)).(poj1330)
双端队列和它的应用(维护一个单调的队列,常常在动态规划中起到优化状态转移的目的). (poj2823)
左偏树(可合并堆).
后缀树(非常有用的数据结构,也是赛区考题的热点).(poj3415,poj3294)
四.搜索
较麻烦的搜索题目训练(poj1069,poj3322,poj1475,poj1924,poj2049,poj3426)
广搜的状态优化:利用M进制数存储状态、转化为串用hash表判重、按位压缩存储状态、双向广搜、A*算法. (poj1768,poj1184,poj1872,poj1324,poj2046,poj1482)
深搜的优化:尽量用位运算、一定要加剪枝、函数参数尽可能少、层数不易过大、可以考虑双向搜索或者是轮换搜索、IDA*算法. (poj3131,poj2870,poj2286)
五.动态规划
需要用数据结构优化的动态规划.(poj2754,poj3378,poj3017)
四边形不等式理论.
较难的状态DP(poj3133)
六.数学
组合数学.
1.MoBius反演(poj2888,poj2154)
2.偏序关系理论.
博奕论.
1.极大极小过程(poj3317,poj1085)
2.Nim问题.
七.计算几何学.
半平面求交(poj3384,poj2540)
可视图的建立(poj2966)
点集最小圆覆盖.
对踵点(poj2079)
D. 一文讲透算法中的时间复杂度和空间复杂度计算方式
作为一名“程序猿”,大家应该都听过这么一句话:程序=数据结构+算法。
这句话是由瑞士计算机科学家尼古拉斯·沃斯(Niklaus Wirth)在 1984 年获得图灵奖时说的一句话,这位大佬还以这句话为名出了一本书《Algorithms + Data Structures=Programs》,从此这句话就成为了大家耳熟能详的一句名言。
随着时间的推移,不管这句话是不是非常准确,但至少能说明数据结构与算法对程序来说是非常核心的基础,如果我们想要写出更多优秀优雅的代码,那么数据结构与算法是必须要掌握好的。
很多人可能觉得,我不会算法,代码一样写得很"溜",算法这东西似乎用处不大。现在互联网的发达,我们想要什么几乎都可以在网上找到现成的,各种框架功能十分强大,似乎看起来确实不用算法也可以写出“好代码”。然而假如我们不懂算法,比如项目中用到了排序,我们如何评估代码的执行效率?再比如最常用的 ArrayList 和 LinkedList ,我们该如何选择,又比如说我们需要去集合中找某一个数,又该如何写出性能优秀的代码呢?
同样的代码,如何判断谁的代码是优秀的代码?可读性,可扩展性,健壮性可能都可以用来判定,然而这些东西我觉得并不能直接体现出你代码的优秀,因为对用户而言,访问你的代码响应速度快那就是优秀的代码,相反,动辄响应几秒甚至更长时间的接口,恐怕就算你可读性再好,再健壮也称不上是好代码。
所以说一段代码是否优秀,最直接的判断标准就是性能,而如果要写出高性能的代码,那么就必须要了解算法,而且抛开这个因素,但凡不想一辈子都写 CRUD 代码的,也需要去了解算法,我们使用的很多框架和中间件底层都有数据结构和算法的身影,学好算法对我们源码阅读时理解其设计思想也是大有裨益的。
要说功利性的目的,那就是面试,目前很多大厂的面试,算法基本必面,所以想进大厂的话,咱们也得好好学学算法。
提到算法,很多人的第一反应就是太难学了,学不会,或者说经常是看完就忘了,但是其实对于我们一个普通的开发者而言,因为并不需要我们去发明算法,我们需要的仅仅只是去灵活的运用算法,所以并不需要非常扎实的数据基础,当然基本的数学常识还是要有的。
如果说需要去发明设计一款算法,那就要去推导去证明算法的可行性,这种是需要具有非常扎实的数学基础的,一般人确实无法做到,然而我们普通程序员口中提到算法无非是二分查找法,哈希算法等,高级一点的就还有回溯,贪心,动态规划等等,这些所谓的算法都是已经有现成的公式了,我们要做的无非就是理解它,然后灵活的运用它。这就和我们以前学习数学公式一样,给你一个公式,然后你去做题,做题的过程其实就是去灵活地运用这个公式。
算法也是同理,都是有特定方法和特定思路的,我们也并不需要去推导证明这种方式为什么可行,所以学习算法没有其他诀窍,就是先理解思路,然后多练,等熟练了,自然就可以灵活运用了,也不会说学了立刻就忘了。学完就忘无非两个原因,一是没理解,二是没有练习巩固。
数据结构与算法经常是放在一起讲,这两者是没办法独立的,因为算法是为了达到某种目的的一种实现方式,而数据结构是一种载体,也就是说算法必须依赖数据结构这种载体,否则就是空谈。换句话说:数据结构是为算法服务的,而算法又需要作用在特定的数据结构之上。
一个算法到底好不好,我们如何去评价?前面我们提到了,你的代码好不好,最直观的就是看响应速度,算法也一样,同样实现一个目的(比如说排序),谁的算法速度快,我们就可以认为谁的算法更优,如果说两种算法实现的速度差不多,那么我们还可以去评价算法所占用的空间,谁占用的空间少,那么就可以认为谁的算法更优,这就是算法的基础:时间复杂度和空间复杂度。
学习算法之前,我们必须要学会如何分析时间复杂度和空间复杂度(也就是“快”和“省”),否则自己写出来的算法自己都不知道算法的效率。
接触过算法的都知道,算法的时间复杂度是用大写的“O”来表示的,比如: O(1) , O(n) , O(logn) , O(nlogn) , O(n²) 等等。
变量指的是变量,也就是一段代码的执行时间是随着变量的变化而变化的,而不变指的是常量,也就是不论我的变量如何改变,执行时间都不会改变。
接下来我们就实际的来分析下常用时间复杂度的例子来练习一下。
0(1) 复杂度算法也称之为常数阶算法。这里的 1 是用来代指常量,也就是说这个算法的效率是固定的,无论你的数据量如何变化,效率都一样,这种复杂度也是最优的一种算法。
上面的示例中不论有多少行代码,时间复杂度都是属于常数阶段。换言之:只要代码不存在 循环 , 递归 等循环类调用,不论代码有多少行,其复杂度都是常数阶。
O(n) 复杂度算法也称之为线性阶段。比如下面这个示例我们应该怎么分析复杂度呢?
前面常量阶没分析是因为常量阶比较容易理解,接下来我们就以线性阶这个为例子来分析下具体是怎么得到的。
我们假设每一行代码的执行时间是 T ,那么上面这段代码的执行复杂度是多少呢?
答案很明显,那就是 T+n*T ,也就是 (n+1)T ,而在算法中有一个原则,那就是常量可以被忽略,所以就得到了 nT ,换成大 O 表示法就是 O(n) 。
这只是一个简略的计算过程,大家也不用较真说每行代码执行时间可能不一样之类的,也不要较真说 for 循环占用了一行,下面的大括号也占用了一行,如果要较真这个,那我建议可以去想一下 1=1 为什么等于 2 。
算法中的复杂度反应的只是一个趋势,这里 O(n) 反应的就是一个趋势,也就是说随着 n 的变化,算法的执行时间是会降低的。
知道了上面的线性阶,那么平方阶就很好理解了,双层循环就是平方阶,同理,三次循环就是立方阶, k 次循环就是 k 次方阶。
O(logn) 也称之为对数阶,对数阶也很常见,像二分查找,二叉树之类的问题中会见到比较多的对数阶复杂度,但是对数阶也是比较难理解的一种算法复杂度。
下面我们还是来看一个例子:
这段代码又该如何分析复杂度呢?这段代码最关键的就是要分析出 while 循环中到底循环了多少次,我们观察这个循环,发现 i 并不是逐一递增,而是不断地翻倍: 1->2->4->8->16->32->64 一直到等于 n 为什么才会结束,所以我们得到了这样的一个公式: 2^x=n 。
也就是说我们只要计算出 x 的值,就得到了循环次数,而根据高中的数学知识我们可以得到 x=log2n ( 2 在下面,是底数,试了几种方法都打不出来,放弃了),所以根据上面线性阶的分析方法,我们省略常量,就得到了示例中的算法复杂度为 O(log2n) 。
同样的分析方式,下面的例子,我们可以很快地分析出复杂度就为 O(log3n) :
上面得到的 log3n 我们可以再做进一步的转换: log3n=log32 * log2n ,而 log32 (注意这几个地方的情况 3 是底数,在下面) 是一个常量,常量可以省略,所以也就得到了: O(log3n)=O(log2n) 。同样的道理,不论底数是多少,其实最终都可以转化成和 O(log2n) 相等,正因为如此,为了方便,我们算法中通常就会省略底数,直接写作 O(logn) 。
上面的数学公式大家如果忘了或者看不懂也没关系,只要记住不论对数的底数是多少,我们都算作 O(logn) ,而对于一个算法的复杂度是否是对数阶,还有一个简易的判断方法: 当循环中下标以指定倍数形式衰减,那么这就是一个对数阶 。
如果理解了上面的对数阶,那么这种线性对数阶就非常好理解了,只需要在对数阶的算法中再嵌一层循环就是线性对数阶:
分析了前面这些最常用的时间复杂度,其实我们可以得到以下规律:
除了上面常用的复杂度之外,另外还有指数阶,阶层阶,根号阶等,这些接触的相对会较少,我们就不特意做分析了,如果大家感兴趣的话,可以自己去了解下。
前面我们分析的都是只有一段代码比较复杂的情况下得到的复杂度结果,那么假如我一个算法中,有多段代码都比较复杂呢?这时候复杂度该如何分析?
我们先看下面这个例子:
这个例子中有三个循环,首先第一个,是一个常量,那么根据前面的结论,不论这个常量是多大,都属于常量级,所以第一个循环中的复杂度为 O(1) ,第二个和第三个循环我们前面也分析过,复杂度分别为 O(n) 和 O(n²) 。
也就是这一段代码中有三段代码产生了三种不同复杂度,而且这三个复杂度可以很明显得到的大小关系为: O(1)<o(n)<o(n²) span=""> </o(n)<o(n²)> ,像这种在同一个算法中有明确大小关系的,我们就可以直接取最大值作为这个算法的复杂度,所以这个例子中算法的复杂度就是 O(n²) 。
接下来我们再来看一个例子:
这个例子我们同样对三段循环分别分析可以分别得到如下复杂度: O(1) , O(m) , O(n) 。这时候我们只能知道 O(1) 最小可以忽略,但是后面两个无法却无法确定大小,所以这时候我们需要取两段循环复杂度之和来作为算法的复杂度,所以可以得到这个例子的算法复杂度为: O(m+n) 。
上面分析的时间复杂度都是比较简单的,实际算法中可能会比示例中复杂的多,而且我们示例中只要是循环都是无脑循环,也就是一定从头循环到尾,然而实际中我们有时候并不需要从头循环到尾,可能中途就会结束循环,所以我们根据实际情况,又可以将时间复杂度从以下四个方面来进一步分析:
这四种类型的时间复杂度在这里只会介绍前面三种,因为第四种比较复杂,而且使用场景也非常有限,而且对于这四种复杂度的分析,大家也作为了解就可以,不敢兴趣的朋友们可以跳过这一小部分,因为在绝大部分情况我们只需要分析最坏复杂度就行,也就是假设循环全部执行完毕场景下的时间复杂度。
我们通过一个例子来理解下最好时间复杂度:
这个方法就是在一个指定数组中找到指定元素的下标,找不到就返回 -1 ,这个方法比较简单,应该比较好理解。
注意这个方法中的循环体,如果找到元素,那么就直接返回,这就会有一个现象,那就是我这个循环体到底会循环多少次是不确定的,可能是 1 次,也可能是 n (假设数组的长度) 次,所以假如我们要找的元素就在数组中的第一个位置,那么我循环一次就找到了,这个算法的复杂度就是 O(1) ,这就是最好情况时间复杂度。
理解了最好时间复杂度,那么最坏时间复杂度也很好理解了,那就是数组中不存在我要找到元素,或者说最后一个值才是我要找的元素,那么这样我就必须循环完整个数组,那么时间复杂度就是 O(n) ,这也就是最坏时间复杂度。
最好时间复杂度和最坏时间复杂度毕竟只有特殊情况才会发生,概率还是相对较小,所以我们很容易就想到我们也需要有一个平均时间复杂度。
我们简单的来分析一下,为了便于分析,我们假设一个元素在数组和不在数组中的概率都为 1/2 ,然后假如在数组在,那么又假设元素出现在每个位置的概率也是一样的,也就是每个位置出现元素的概率为: 1/n 。
所以最终得到的平均时间复杂度应该等于元素在数组中和元素不在数组中两种情况相加。
因为元素在数组中的概率为 1/2 ,然后在每个位置出现的概率也为 1/n 。假如元素出现在第一个位置,复杂度为 1*(1/2n) ;假如元素出现在第二个位置,复杂度为 2 * (1/2n) ,最终得到当前场景下时间复杂度为: 1*(1/2n) + 2 * (1/2n) + ... + n*(1/2n) =(n+1)/4。
前面已经假定了元素不在数组中的概率为 1/2 ,所以当前场景下的时间复杂度为: n * (1/2) ,因为元素不在数组中,那么这个算法必然会将整个循环执行完毕,也就循环是 n 次。
最后我们把两种情况的复杂度之和相加就得到了平均时间复杂度: (n+1)/4 + n/2 = (3n+1)/4 ,最终我们将常数类的系数忽略掉,就得到了平均时间复杂度为 O(n) 。
均摊时间复杂度的算法需要使用摊还分析法,计算方式相对有点复杂,而且使用场景很有限,本文就不做过多介绍了。
空间复杂度全称就是渐进空间复杂度,用来表示算法的存储空间与数据规模之间的增长关系。和时间复杂度一样,空间复杂度也是用大 O 进行表示。
其实学会了分析时间复杂度,那么空间复杂度的分析就简单了,主要就看我们在一个算法当中到底有没有使用到了额外的空间来进行存储数据,然后判断这个额外空间的大小会不会随着 n 的变化而变化,从而得到空间复杂度。
我们来看一个给数组赋值例子,假设这就是一个算法,我们可以来分析下这个算法的空间复杂度:
一开始定义了一个变量,这里需要空间,但是这是一个常量级的(不随 n 的变化而变化),然后再定义了一个数组,数组的长度为 n ,这里数组也需要占用空间,而且数组的空间是随着 n 的变化而变化的,其余代码没有占用额外空间,所以我们就可以认为上面示例中的空间复杂度为 O(n) 。
对于算法的空间复杂度也可以简单的进行总结一下:
本文主要讲述了为什么要学习算法,也简单减少了数据结构与算法之间的关系,随后主要介绍了算法中的入门知识:时间复杂度和空间复杂度。想要学好算法,必须要掌握如何分析一个算法的时间复杂度和空间复杂度,只有自己会分析这两个个衡量算法主要性能的标准,才能更好的写出性能优秀的算法,同时我们也讲到了最好时间复杂度,最坏时间复杂度,平均时间复杂度和均摊时间复杂度,不过这四种复杂度的计算方式大家作为了解即可,等实际确实需要使用到再来回顾也不迟。
E. 为什么计算机也懂先算乘法除法
计算机里的加减乘除运算法则,是程序员根据数学里的运算法则,写入程序,告诉计算机的,计算机才懂得先算乘法,除法的。
F. C语言中自定义函数与系统函数的区别
自定义,顾名思义就是根据程序员的实际需要,自己定义的函数,函数有自己定义的参数,功能和返回值,自定义函数的目的是更好更方便的帮程序员更快的完成所需要的程序设计。
系统函数就是系统自带的函数,它是通用的,就是所有程序员都能使用的函数,它有规定的接口,参数形式,功能和返回值,是编译软件实现定义好的函数,供所有程序员调用。
特点:
系统函数是通用的,所有程序员都知道,都能用,使编译平台有普适性,但是不能满足各种程序员特定的变成需求。
自定义函数是自我定义的,只有每个程序员自己知道,完成自己想要完成的特定意义的函数,它一般是对系统函数的集成编程。比如从屏幕上获取一个字母,并判断它是大写还是小写,如果大写,就打印“大写”,是小写就打印“小写”,我们需要先用getchar()获取屏幕上的字母,再进行判断,再进行输出,如果以后我们很多程序都需要用到这三个步骤,那么我们可以写一个函数把他们三个步骤集成到一起,有自己的参数和返回值,那么我们就利用了系统函数构造成了我们自定义的函数。
希望对你有帮助,欢迎再次提问
G. 软件开发的程序员需要掌握多的数学知识
需要数学,但是这个数学不是说你现在学的数学这点知识,而是你是逻辑思维,如果你仅仅是想成为一个程序员,只是一个写代码的人,那你数学不需要太好,但是,如果你真想好好从事计算机这方面,尤其是想软件开发,你必须得学好数学,计算机本来就是从数学里分支出来的,你越往上走也就越接近数学,你相信吗,一个计算机的顶级专家不会写代码的人大有人在,什么是程序。有一本书是,程序=数据结构+算法。任何一门语言给你两个月你都能把基本的学的差不多,就想盖房子,写代码的程序员就相当于砖匠,你永远成不了设计师。一个大的正规的项目,有80%的时间是在设计,设计有哪些模块,用什么技术,怎么架构这个项目,怎么通信等等。。。。而等设计完了20%的时间给程序员把代码写出来。写了这么多,你自己好好想想,随便问一个高手,看看那个会告诉你计算机不需要数学,
需要注意的是,数学课本里的具体知识、公式,而是一种数学的思维方式、逻辑思维能力。最后祝你能够坚持走这条路,好运。