数据结构介绍
作为单链表的升级版,我们通常接触的树都是二叉树 (Binary Tree),即每个节点最多有两个子节点;且除非题目说明,默认树中不存在循环结构。LeetCode 默认的树表示方法如下:
1 | class TreeNode: |
可以看出,其与链表的主要差别就是多了一个子节点的指针。
作为单链表的升级版,我们通常接触的树都是二叉树 (Binary Tree),即每个节点最多有两个子节点;且除非题目说明,默认树中不存在循环结构。LeetCode 默认的树表示方法如下:
1 | class TreeNode: |
可以看出,其与链表的主要差别就是多了一个子节点的指针。
单向链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值,必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链表的长度,除非依赖其他数据结构储存长度。LeetCode 默认的链表表示方法如下:
1 | class ListNode: |
由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:
一般来说,算法题不需要删除内存。在刷 LeetCode 的时候,如果想要删除一个节点,可以直接进行指针操作而无需回收内存。实际做软件工程时,对于无用的内存,笔者建议尽量显式回收,或利用智能指针。
字符串可以看成是字符组成的数组。由于字符串是程序里经常需要处理的数据类型,因此有很多针对字符串处理的题目。
242. Valid Anagram
我们可以利用哈希表或者数组统计两个数组中每个数字出现的频次,若频次相同,则说明它们包含的字符完全相同。
205. Isomorphic Strings
我们可以将问题转化一下:记录两个字符串每个位置的字符第一次出现的位置,如果两个字符串中相同位置的字符与它们第一次出现的位置一样,那么这两个字符串同构。
647. Palindromic Substrings
我们可以从字符串的每个位置开始,向左向右延长,判断存在多少以当前位置为中轴的回文子字符串。
696. Count Binary Substrings
从左往右遍历数组,记录和当前位置数字相同且连续的长度,以及其之前连续的不同数字的长度。若不同数字的连续长度大于等于当前数字的连续长度,则说明存在一个且只存在一个以当前数字结尾的满足条件的子字符串。
在刷题时,我们几乎一定会用到各种数据结构来辅助我们解决问题,因此我们必须熟悉各种数据结构的特点。C++ STL 提供的数据结构包括(实际底层细节可能因编译器而异):
位运算是算法题里比较特殊的一种类型,它们利用二进制位运算的特性进行一些奇妙的优化和计算。常用的位运算符号包括:“∧ 按位异或”、“& 按位与”、“| 按位或”、“~ 取反”、“<< 算术左移”和“>> 算术右移”。以下是一些常见的位运算特性,其中 0s 和 1s 分别表示只由 0 或 1 构成的二进制数字:
| 按位异或 | 按位与 | 按位或 |
|---|---|---|
| x ∧ 0s = x | x & 0s = 0 | x | 0s = x |
| x ∧ 1s = ~x | x & 1s = x | x | 1s = 1s |
| x ∧ x = 0 | x & x = x | x | x = x |
除此之外,n & (n - 1) 可以去除 n 的位级表示中最低的那一位,例如对于二进制表示 11110100,减去 1 得到 11110011,这两个数按位与得到 11110000。n & -n 可以得到 n 的位级表示中最低的那一位,例如对于二进制表示 11110100,取负得到 00001100,这两个数按位与得到 00000100。还有更多的并不常用的技巧,若读者感兴趣可以自行研究,这里不再赘述。