|
| 1 | +# 关于学算法的⼀些经验 |
| 2 | + |
| 3 | +## 作为⼀个⼩⽩,算法该如何学习? |
| 4 | + |
| 5 | + 对于算法的学习,我也是从⼀个⼩⽩⼀步步⾛来,当然,现在仍然很菜,,,不过,鉴于我觉得还有⼀ 些⼈⽐我更菜了,我决定谈谈我算法学习过程⾛过的坑,以及⾃⼰总结的⼀些经验,之前也有写过⼀篇 类似的,那时粉丝才⼏千,这篇算是修正版。 |
| 6 | + |
| 7 | +#### 切勿盲⽬刷题:刷题前的知识积累 |
| 8 | + |
| 9 | +说实话,想要提⾼⾃⼰的算法,真的没啥捷径,我觉得最好的捷径就是脚踏实地着多动⼿去刷题,多刷题。 |
| 10 | + |
| 11 | + 但是,我必须提醒的是,如果你是⼩⽩,也就是说,你连常⻅的数据结构,如链表、树以及常⻅的算法 思想,如递归、枚举、动态规划这些都没学过,那么,我不建议你盲⽬疯狂着去刷题的。 |
| 12 | + |
| 13 | +⽽是先去找本 书先去学习这些必要的知识,然后再去刷题。 因为,如果这些基础都不懂的话,估计⼀道题做了⼏个⼩时,然后看答案都看不懂,做题没有任何思路,这是很难受的。 |
| 14 | + |
| 15 | +久⽽久之,估计没啥动⼒了,我刚开始就是这样,⼀道题答案看⼀天,然⽽还是不⼤懂,什么回溯啊,暴⼒啊,还不知道是啥意思。 |
| 16 | + |
| 17 | +也就是说,假如你要去诸如 LeetCode 这些⽹站刷题,那么,你要先具备⼀定的基础,这些基础包括: |
| 18 | + |
| 19 | +1、常⻅数据结构:链表、树(如⼆叉树)。(是的,链表和⼆叉树是重点,图这些可以先放着) |
| 20 | + |
| 21 | +2、常⻅算法思想:贪婪法、分治法、穷举法、动态规划,回溯法。(贪婪、穷举、分治是基础,动态 规划有难度,可以先放着) |
| 22 | + |
| 23 | +以上列出来的算是最基本的吧。就是说你刷题之前,要把这些过⼀遍再去刷题。如果你连这些最基本的 都不知道的话,那么你再刷题的过程中,会很难受的,思路也会相对⽐较少。 |
| 24 | + |
| 25 | +总之,千万不要急,先把这些基本的过⼀遍,⼒求理解,再去刷题。 |
| 26 | + |
| 27 | +总结下: 提⾼数据结构与算法没啥捷径,最好的捷径就是多刷题。但是,刷题的前提是你要先学会⼀些基本的数 据结构与算法思想。 |
| 28 | + |
| 29 | +#### AC 不是⽬的,我们要追求完美 |
| 30 | + |
| 31 | +如何刷题?如何对待⼀道算法题? 我觉得,在做题的时候,⼀定要追求完美,千万不要把⼀道题做出来之后,提交通过,哇,舒服!然后 就赶紧下⼀道。 |
| 32 | + |
| 33 | +然⽽,我认为这意义不⼤,因为⼀道题的解法太多了,有些解法态粗糙了,我们应该要寻找最优的⽅法。 |
| 34 | + |
| 35 | +算法能⼒的提升和做题的数量是有⼀定的关系,但并不是线性关系。 |
| 36 | + |
| 37 | +也就是说,在做题的时候,要⼒求⼀题多解,如果⾃⼰实在想不出来其他办法了,可以去看看别⼈是怎么做的,千万不要觉得模仿别⼈的 做法是件丢⼈的事。反正我就算作出了最优解,也会经常去看看⼤神的代码⻓啥样,有时候发现,⼤神果然是⼤神,代码好简洁! |
| 38 | + |
| 39 | +我做题的时候,我⼀看到⼀道题,可能第⼀想法就是⽤很粗糙的⽅式做,因为很多题采⽤暴⼒法都会很 容易做,就是时间复杂度很⾼。之后,我就会慢慢思考,看看有没其他⽅法来降低时间复杂度或空间复 杂度,例如是否可以状态保存,剪纸,是否可以采⽤动规等等。最后,我会去看⼀下别⼈的做法,当然,并不是每道题都会这样执⾏。就是看到有意思的题目会下意识的去思考有没有更好的结题方法。 |
| 40 | + |
| 41 | +衡量⼀道算法题的好坏⽆⾮就是时间复杂度和空间复杂度,所以我们要⼒求完美,就要把这两个降到最 低,令他们相辅相成。 |
| 42 | + |
| 43 | +举道例题吧(举了⽆数次了,哈哈): |
| 44 | + |
| 45 | +**问题: ⼀只⻘蛙⼀次可以跳上 1 级台阶,也可以跳上 2 级。求该⻘蛙跳上⼀个 n 级的台阶总共有多少种跳 法?** |
| 46 | + |
| 47 | +**⽅法1::暴⼒递归** |
| 48 | + |
| 49 | +这道题不难,或许你会采取下⾯的做法: |
| 50 | + |
| 51 | +```c++ |
| 52 | +int solve(int n) { |
| 53 | + if (n <=2) { |
| 54 | + return n; |
| 55 | + } else { |
| 56 | + return solve(n-1) + solve(n-2); |
| 57 | + } |
| 58 | +} |
| 59 | +``` |
| 60 | +
|
| 61 | +这种做法的时间复杂度很⾼,指数级别了。但是如果你提交之后侥幸通过了,然后你就接着下⼀道题 了,那么你就要好好想想了。 |
| 62 | +
|
| 63 | +**⽅法⼆:空间换时间** |
| 64 | +
|
| 65 | +⼒求完美,我们可以考虑⽤空间换时间:这道题如何你去仔细想⼀想,会发现有很多是重复执⾏了。 |
| 66 | +
|
| 67 | +不⾏你可以画个图 |
| 68 | +
|
| 69 | + |
| 70 | +
|
| 71 | +所以可以采取下⾯的⽅法: |
| 72 | +
|
| 73 | +```c++ |
| 74 | +// ⽤⼀个 HashMap 来保存已经计算过的状态 |
| 75 | +static std::map<int,int>mp; |
| 76 | +int solve(int n) { |
| 77 | + if (n <= 2) { |
| 78 | + return n; |
| 79 | + } else { |
| 80 | + if (mp.count(n)) { |
| 81 | + return mp[n]; |
| 82 | + } else { |
| 83 | + int m = solve(n-1) + solve(n-2); |
| 84 | + mp[n]=m; |
| 85 | + return m; |
| 86 | + } |
| 87 | + } |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +```java |
| 92 | +static Map<Integer,Integer> map = new HashMap(); |
| 93 | +public static int solve(int n){ |
| 94 | + if(n <= 2){ |
| 95 | + return n; |
| 96 | + } else {//是否计算过 |
| 97 | + if(map.containsKey(n)){ |
| 98 | + return map.get(n); |
| 99 | + }else{ |
| 100 | + int m = solve(n-1) + solve(n-2); |
| 101 | + map.put(n, m); |
| 102 | + return m; |
| 103 | + } |
| 104 | + } |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | +# 链表刷题指南 |
| 111 | + |
| 112 | +## LeetCode-206.翻转链表 |
| 113 | + |
| 114 | +最简单的迭代法: |
| 115 | + |
| 116 | +```c++ |
| 117 | +class Solution { |
| 118 | +public: |
| 119 | + ListNode* reverseList(ListNode* head) { |
| 120 | + ListNode* cur = nullptr, *pre = head; |
| 121 | + while (pre != nullptr) { |
| 122 | + ListNode* next = pre->next; |
| 123 | + pre->next = cur; |
| 124 | + cur = pre; |
| 125 | + pre = next; |
| 126 | + } |
| 127 | + return cur; |
| 128 | + } |
| 129 | +}; |
| 130 | +``` |
| 131 | +
|
0 commit comments