##Linked List - 链表 链表是线性表的一种。线性表是最基本、最简单、也是最经常使用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素以外,其它数据元素都是首尾相接的。线性表有两种存储方式,一种是顺序存储结构,另外一种是链式存储结构。咱们经常使用的数组就是一种典型的顺序存储结构。node
相反,链式存储结构就是两个相邻的元素在内存中可能不是相邻的,每个元素都有一个指针域,指针域通常是存储着到下一个元素的指针。这种存储方式的优势是插入和删除的时间复杂度为 O(1),不会浪费太多内存,添加元素的时候才会申请内存,删除元素会释放内存。缺点是访问的时间复杂度最坏为 O(n)。数组
顺序表的特性是随机读取,也就是访问一个元素的时间复杂度是O(1),链式表的特性是插入和删除的时间复杂度为O(1)。数据结构
链表就是链式存储的线性表。根据指针域的不一样,链表分为单向链表、双向链表、循环链表等等。翻译
请看示例:指针
class listNode: def __init__(self, val): self.val = val self.next = None
##链表的基本操做 ###反转链表 ####单向链表code
链表的基本形式是:1 -> 2 -> 3 -> null,反转须要变为 3 -> 2 -> 1 -> null。这里要注意:内存
示例:ci
class listNode: def __init__(self, val): self.val = val self.next = None def reserse(self, head): prev = None while head: temp = head.next head.next = prev prev = head head = temp return prev
####双向链表it
和单向链表的区别在于:双向链表的反转核心在于next和prev域的交换,还须要注意的是当前节点和上一个节点的递推。ast
class DListNode: def __init__(self, val): self.val = val self.prev = self.next = null def reverse(self, head): curt = None while head: curt = head head = curt.next curt.next = curt.prev curt.prev = head return curt
###删除链表中的某个节点 删除链表中的某个节点必定须要知道这个点的前继节点,因此须要一直有指针指向前继节点。还有一种删除是伪删除,是指复制一个和要删除节点值同样的节点,而后删除,这样就没必要知道其真正的前继节点了。
而后只须要把 prev -> next = prev -> next -> next 便可。可是因为链表表头可能在这个过程当中产生变化,致使咱们须要一些特别的技巧去处理这种状况。就是下面提到的 Dummy Node。
###链表指针的鲁棒性 综合上面讨论的两种基本操做,链表操做时的鲁棒性问题主要包含两个状况:
Dummy node 是一个虚拟节点,也能够认为是标杆节点。Dummy node 就是在链表表头 head 前加一个节点指向 head,即 dummy -> head。Dummy node 的使用多针对单链表没有前向指针的问题,保证链表的 head 不会在删除操做中丢失。除此以外,还有一种用法比较少见,就是使用 dummy node 来进行head的删除操做,好比 Remove Duplicates From Sorted List II,通常的方法current = current.next 是没法删除 head 元素的,因此这个时候若是有一个dummy node在head的前面。
因此,当链表的 head 有可能变化(被修改或者被删除)时,使用 dummy node 能够很好的简化代码,最终返回 dummy.next 即新的链表。
快慢指针 快慢指针也是一个能够用于不少问题的技巧。所谓快慢指针中的快慢指的是指针向前移动的步长,每次移动的步长较大即为快,步长较小即为慢,经常使用的快慢指针通常是在单链表中让快指针每次向前移动2,慢指针则每次向前移动1。快慢两个指针都从链表头开始遍历,因而快指针到达链表末尾的时候慢指针恰好到达中间位置,因而能够获得中间元素的值。快慢指针在链表相关问题中主要有两个应用:
快速找出未知长度单链表的中间节点 设置两个指针 fast、slow 都指向单链表的头节点,其中fast的移动速度是slow的2倍,当*fast指向末尾节点的时候,slow正好就在中间了。 判断单链表是否有环 利用快慢指针的原理,一样设置两个指针 *fast、*slow 都指向单链表的头节点,其中 fast的移动速度是slow的2倍。若是 *fast = NULL,说明该单链表 以 NULL结尾,不是循环链表;若是 *fast = *slow,则快指针追上慢指针,说明该链表是循环链表。
###快慢指针 快慢指针也是一个能够用于不少问题的技巧。所谓快慢指针中的快慢指的是指针向前移动的步长,每次移动的步长较大即为快,步长较小即为慢,经常使用的快慢指针通常是在单链表中让快指针每次向前移动2,慢指针则每次向前移动1。快慢两个指针都从链表头开始遍历,因而快指针到达链表末尾的时候慢指针恰好到达中间位置,因而能够获得中间元素的值。快慢指针在链表相关问题中主要有两个应用:
快速找出未知长度单链表的中间节点 设置两个指针 *fast、*slow 都指向单链表的头节点,其中*fast的移动速度是*slow的2倍,当*fast指向末尾节点的时候,slow正好就在中间了。 判断单链表是否有环 利用快慢指针的原理,一样设置两个指针 *fast、*slow 都指向单链表的头节点,其中 *fast的移动速度是*slow的2倍。若是 *fast = NULL,说明该单链表 以 NULL结尾,不是循环链表;若是 *fast = *slow,则快指针追上慢指针,说明该链表是循环链表。
class NodeCircle: def __init__(self, val): self.val = val self.next = None def has_circle(self, head): slow = head fast = head while (slow and fast): fast = fast.next slow = slow.next if fast: fast = fast.next if fast == slow: break if fast and slow and (fast == slow): return True else: return False