Ethan's Blog


  • Home

  • Archives

  • Tags

  • Search

指针三剑客之二:树

Posted on 2023-04-10

数据结构介绍

作为单链表的升级版,我们通常接触的树都是二叉树 (Binary Tree),即每个节点最多有两个子节点;且除非题目说明,默认树中不存在循环结构。LeetCode 默认的树表示方法如下:

1
2
3
4
5
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right

可以看出,其与链表的主要差别就是多了一个子节点的指针。

Read more »

指针三剑客之一:链表

Posted on 2023-04-07

数据结构介绍

单向链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值,必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链表的长度,除非依赖其他数据结构储存长度。LeetCode 默认的链表表示方法如下:

1
2
3
4
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或指针出现问题。有两个小技巧可以解决这个问题:

  • 尽量处理当前节点的下一个节点而非当前节点本身。
  • 建立一个虚拟节点 (Dummy Node),使其指向当前链表的头节点,这样即使原链表所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。

一般来说,算法题不需要删除内存。在刷 LeetCode 的时候,如果想要删除一个节点,可以直接进行指针操作而无需回收内存。实际做软件工程时,对于无用的内存,笔者建议尽量显式回收,或利用智能指针。

Read more »

令人头大的字符串

Posted on 2023-04-06

引言

字符串可以看成是字符组成的数组。由于字符串是程序里经常需要处理的数据类型,因此有很多针对字符串处理的题目。

字符串比较

242. Valid Anagram
我们可以利用哈希表或者数组统计两个数组中每个数字出现的频次,若频次相同,则说明它们包含的字符完全相同。

205. Isomorphic Strings
我们可以将问题转化一下:记录两个字符串每个位置的字符第一次出现的位置,如果两个字符串中相同位置的字符与它们第一次出现的位置一样,那么这两个字符串同构。

647. Palindromic Substrings
我们可以从字符串的每个位置开始,向左向右延长,判断存在多少以当前位置为中轴的回文子字符串。

696. Count Binary Substrings
从左往右遍历数组,记录和当前位置数字相同且连续的长度,以及其之前连续的不同数字的长度。若不同数字的连续长度大于等于当前数字的连续长度,则说明存在一个且只存在一个以当前数字结尾的满足条件的子字符串。

Read more »

妙用数据结构

Posted on 2023-03-23

C++ STL

在刷题时,我们几乎一定会用到各种数据结构来辅助我们解决问题,因此我们必须熟悉各种数据结构的特点。C++ STL 提供的数据结构包括(实际底层细节可能因编译器而异):

  • Sequence Containers:维持顺序的容器:
    • vector:动态数组,是我们最常使用的数据结构之一,用于 O(1) 的随机读取。因为大部分算法的时间复杂度都会大于 O(n),因此我们经常新建 vector 来存储各种数据或中间变量。因为在尾部增删的复杂度是 O(1),我们也可以把它当作 stack 来用。
    • list:双向链表,也可以当作 stack 和 queue 来使用。由于 LeetCode 的题目多用 Node 来表示链表,且链表不支持快速随机读取,因此我们很少用到这个数据结构(LRU 除外)。
    • deque:双端队列,这是一个非常强大的数据结构,既支持 O(1) 随机读取,又支持 O(1) 时间的头部增删和尾部增删(因此可以当作 stack 和 queue 来使用),不过有一定的额外开销。
    • array:固定大小的数组,一般在刷题时我们不使用。
    • forward_list:单向链表,一般在刷题时我们不使用。
  • Container Adaptors:基于其它容器实现的容器:
    • stack:后入先出的数据结构,默认基于 deque 实现。stack 常用于深度优先搜索、一些字符串匹配问题以及单调栈问题。
    • queue:先入先出的数据结构,默认基于 deque 实现。queue 常用于广度优先搜索。
    • priority_queue:最大值先出的数据结构,默认基于 vector 实现堆结构。它可以在 O(nlogn) 的时间排序数组、O(logn) 的时间插入任意值、O(logn) 的时间删除最大值、O(1) 的时间获得最大值。priority_queue 常用于维护数据结构并快速获取最大值,并且可以自定义比较函数,比如通过存储负值或者更改比小函数为比大函数,即可实现最小值先出。
Read more »

神奇的位运算

Posted on 2023-03-23

常用技巧

位运算是算法题里比较特殊的一种类型,它们利用二进制位运算的特性进行一些奇妙的优化和计算。常用的位运算符号包括:“∧ 按位异或”、“& 按位与”、“| 按位或”、“~ 取反”、“<< 算术左移”和“>> 算术右移”。以下是一些常见的位运算特性,其中 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。还有更多的并不常用的技巧,若读者感兴趣可以自行研究,这里不再赘述。

Read more »
1…456…55
necusjz

necusjz

271 posts
16 tags
© 2016 - 2026 necusjz
Powered by Hexo
Theme - NexT.Mist