算法训练营总结

经过60天的坚持打卡,系统的过了一遍leetcode经典题目,包括数组(二分查找)、链表、哈希表、字符串、栈与队列、二叉树、回溯、贪心、动态规划、单调栈。由于有一定的基础,也是本着练习C++的想法入门的,所有的题目会先用熟悉的python写一遍,再用C++写一遍。在入门C++方面,群友助教答疑非常有用,如果是自己看书或者资料,很难快速的定位问题。在有经验的人帮助下,可以非常快速的解决问题,开始上手leetcode类型的代码,给下一步编写C++程序奠定了很好的基础。

1. 数组

数组是较为经典的,卡哥把二分查找讲得非常清晰,以前虽然能写,但是没有仔细思考过区间左开右弼和左闭右开的问题,从而理解了精确的处理边界条件的重要。以前的思路是:模糊的尽可能多的包含被查找的区间,这样不会遗漏,可是这样也带来了新的问题: 永远都无法收敛到目标位置,无法结束循环。debug的时候,发现这种不精确的写法在第二轮就找到了目标值,但是没有终止循环。

接着就是快慢指针,滑动窗口也是经典方法。C++中的数组即array, 一般vector较为常见,因为vector可以改变长度,而array需要resize,操作需要考虑的细节较多。C++中 inf用了INT_MAX。

2. 链表

链表是对指针的不错练习,不像数组可以循秩访问,只能依靠指针逐个遍历。学到的技巧有dummy_head。

遇到了一个leetcode里linkedinList结构特有的报错,链表结构需要逻辑更为完整,C++版本没有提前单独处理head=NULL的情况,需要在while里说清楚corner case。参考解决方案

“在C++中,如果一个类的成员变量是基本数据类型(如int)并且没有在构造函数中显式初始化,那么该变量的初始值是未定义的。这意味着它可能包含任何值,这个值取决于程序所运行的系统、编译器以及内存的状态。” 和局部变量一样,是未定义的,那可能是任意值。

在经典题目反转链表中体会了C++的精确性,双指针法直接反转指针方向,Python版本虽然没有申明每个变量的类型,但是写的时候心里的明白pre是ListNode类型,不然会报错。

C++ 不像 python,go 那种能这样交换两个变量的值。逗号运算符它会忽略掉左边的表达式。所以这里没有Python的简洁版写法。只能利用中间变量逐步替换。

C++记得清除缓存变量, 处理溢出。

学了C++的n--的用法。 C++:i++、++i、--i、i--、+=、-=、*=、/=你真的了解吗?

环形链表是目前比较有意思的一个题,方法一是遍历,记录出现过的node,第二次出现的node即为入口,时间O(n), 但是内存O(n^2) [1+2+3+...+n ~ n^2]。那么就开始考虑是否可以优化内存到O(n),这个思路是经典的龟兔赛跑问题,用快慢指针的话一定会相遇,且图中x=z,在快指针只套了慢指针一圈的情况下,如果快指针比慢指针快了n圈(n>=2)的推导就比较妙了,x = (y+z)*k + z, 发现和n=1的情况是一样的,所以当快慢指针在met处相遇后,把慢指针放回head,那么下一次快慢指针相遇一定在x处。核心仍然是x=z。

C++关于集合的数据结构有set, unordered_set, map, unordered_map; unordered_set更接近Python中的set概念,但是这个题不能用unordered_set,因为集合内部元素是LinkList, unordered_set底层是hash_table, LinkList没有对应的hash函数,故不支持。所以适合map结构,map结构内部可以是LinkNode作为key。

3. 哈希表

在D4已经遇到了set/map的结构对比了,这里再补充一下对比。(from carlprogrming)

在242有效的字母异位词 这里学到C++中array结构代替python中字典结构的用法; 如果不用字母作为Key,而是数值(当前字母和字母a的差值),那么可以直接用数组结构(array), 因为数组也是由Index和value的对应关系来表示的, 和字典的key, value结构类似底层都是hash table。能用数组的时候尽量用数组,比另外两种结构set/map要快。

Python中的ord()函数: 数组方法需要把index确定为int格式, python里用ord()函数实现,Python ord() 函数是一个内置函数,可以将一个字符转换为对应的 ASCII 或 Unicode 数值,或者将一个 ASCII 或 Unicode 数值转换为对应的字符。

4. 字符串

学习了ACM模式的编程,不太熟悉,hackerrank不少面试就是ACM模式,还是值得练习的。

学习了KMP算法,搁置了一段时间又回看学习了一下,需要复习几次,理解前缀表的概念,还是很巧妙的。

5. 栈和队列

一个FIFO一个FILO,经典题目用队列实现栈,用栈实现队列,这个系列python和C++用的底层结构不同,C++中就是stack queue对应栈和队列,Python中collections.deque实现stack和queue, Python里的list底层是array+LinkNode实现,还不太一样。

6. 二叉树

二叉树既可以链式存储(利用指针,类似栈和队列),也可以用数组表示。

深度优先遍历
前序遍历(递归法,迭代法)
中序遍历(递归法,迭代法)
后序遍历(递归法,迭代法)
广度优先遍历
层次遍历(迭代法)

栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用递归的方式来实现的。

而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。

优先掌握递归遍历(前中后序),层次遍历在某些问题上较好用。

二叉树深度和高度是不同的概念,顺序是相反的,高度从叶子往根部递增,深度是从根部往叶子递增;求深度用的是前序遍历,求高度用的是后序遍历。

二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

C++语法上有一些区别,1. vector没有max的方法,需要冒泡比较; 2. vector切片的方式需要call begin(), end(); 3. 数组的index需要判断是否在区间内再进行操作。

二叉搜索树是有序的,和普通的二叉树不同,左子树>根节点>右子树。

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉搜索树

Python引用传递和值传递的问题,当arr作为参数传入的时候,由于arr是python的基本类型,所以是引用传递;如果在C++语法中仅传入参数 值传递,则不会修改。Python 中的函数传参是通过对象引用进行的,如果传的是一个不可变对象(在其他语言中可能指基本类型),函数中对这个参数的更改是不会影响到函数外的,这个传递也就是值传递。如果传的是 list,字典,集合作为参数,传递的参数可以看做是引用传递,对其进行的更改就能影响到函数外

7. 回溯

回溯算法本质上是暴力搜索,类似于二叉树遍历的模版。暴力搜索在实际问题中容易超时,大部分问题需要考虑剪枝,一般是从遍历时每一层的个数i这里做文章。重点是剪枝,条件判断。超难题目有时间需要多复习:N皇后、旅行规划、解数独。

8. 贪心

中心思想是局部最优可以推出全局最优。那么需要考虑清楚局部case、递推关系、终止条件。

经典题目有:股票买卖、跳跃游戏、加油站、分发糖果、找零钱、引爆气球、区间问题。

9. 动态规划

从经典题目斐波那契数列开始,动态规划的难点是理清楚递推关系,当然,有的题目里初始化条件也是很重要的,这部分花了17天的学习时间,1D2D的表,以及2D的dp表压缩成1D的表,遍历的顺序是在考虑递推关系后决定的。

10. 单调栈

单调栈的题目较为简单,关键在于对这个数据结构的利用,接雨水和柱状图中的最大矩形题目较为经典,可以经常思考一下。

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值