在程序设计中,为了优化算法可能会用到滑动窗口或者双指针的思想,这种算法能够蛮力情况下的复杂度\(O(n^2)\)降低为线性。滑动窗口的问题通常可以用双指针来解决,即用头尾两个指针来约束窗口大小。
网上对于这两个名词的定义和解释莫衷一是。个人理解,固定一段窗口/区间大小而衍生的问题可以理解为单纯的滑动窗口问题,而双指针思想不局限于解决滑动窗口问题,还包含快慢指针、对撞指针(双指针向内移动)等,本质在于以两个指针来维护问题域,并且能在均摊O(1)下得到可行解。
本文基于Python deque双端队列,以最容易理解的方式归纳滑动窗口的问题的求解方法。
deque双端队列
Python的双端队列类型在官方collections库中,是一种类似原生列表(list)的容器,实现了在队列两端快速添加(append/appendleft)和弹出(pop/popleft)。源码由C语言实现,可在Github的CPython项目下Modules/_collectionsmodule.c文件中找到。
deque本质是一个双向链表,每个节点是一个固定长度的block,可以包含64个元素。在源码注释中,设计者提到固定长度的block可以
1)避免频繁分配内存的开销,提高效率;
2)大幅减少了链表指针,提升数据/指针比,从而提升内存利用率;
3)有利于缓存局部性。
点击查看源代码
/* The block length may be set to any number over 1. Larger numbers
* reduce the number of calls to the memory allocator, give faster
* indexing and rotation, and reduce the link to data overhead ratio.
* Making the block length a power of two speeds-up the modulo
* and division calculations in deque_item() and deque_ass_item().
*/
#define BLOCKLEN 64
#define CENTER ((BLOCKLEN - 1) / 2)
#define MAXFREEBLOCKS 16
/* Data for deque objects is stored in a doubly-linked list of fixed
* length blocks. This assures that appends or pops never move any
* other data elements besides the one being appended or popped.
*
* Another advantage is that it completely avoids use of realloc(),
* resulting in more predictable performance.
*
* Textbook implementations of doubly-linked lists store one datum
* per link, but that gives them a 200% memory overhead (a prev and
* next link for each datum) and it costs one malloc() call per data
* element. By using fixed-length blocks, the link to data ratio is
* significantly improved and there are proportionally fewer calls
* to malloc() and free(). The data blocks of consecutive pointers
* also improve cache locality.