导图社区 链表
详细描述链表一节中的各种基础概念,及面试时高概率算法题目,定义:每个节点由两部分构成,一个数据域,一个指针域,最后一个节点的指针域指向null,链表的入口节点称为链表的头结点。
编辑于2024-07-14 23:32:17链表
基础理论
链表的定义
每个节点由两部分构成,一个数据域,一个指针域,最后一个节点的指针域指向null,链表的入口节点称为链表的头结点
链表的分类
单链表
双链表
每一个节点有两个指针域,一个指向上一个节点,一个指向下一个节点
循环链表
链表首尾相连
存储方式
链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
代码定义
# 定义链表节点 class ListNode: def __init__(self, val, next=None): self.val = val self.next = next # 添加头结点 class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next
链表的性能分析
链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。每一次查询都要从头结点开始
移除链表元素
原链表移除
头结点和其他结点的移除方式不同:移除头结点,将头结点设置为后一一个节点,并且收回头结点
其他结点,将前一个结点的指针指向后一个节点,并且收回该节点
虚拟头结点移除
方式统一:将前一个节点的指针指向后一个节点即可
代码定义
class Solution: def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]: dummy_head = ListNode(next = head) current = dummy_head while current.next: if current.next.val == val: current.next = current.next.next else: current = current.next return dummy_head.next
链表的设计
基础操作
1.获取链表的第index个节点 2.在链表的最前面插入节点 3.在链表的最后面插入节点 4. 在链表的第index个节点前面插入一个节点 5.删除链表的第index个节点
代码定义
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class MyLinkedList(object): def __init__(self): self.null_node = ListNode() self.size = 0 def get(self, index): if index < 0 or index >= self.size: return -1 currnt = self.null_node.next for i in range(index): currnt = currnt.next return currnt.val def addAtHead(self, val): self.null_node.next = ListNode(val, self.null_node.next) self.size += 1 def addAtTail(self, val): current = self.null_node while current.next: current = current.next current.next = ListNode(val) self.size += 1 def addAtIndex(self, index, val): if index<0 or index>self.size: return current = self.null_node for i in range(index): current = current.next current.next = ListNode(val, current.next) self.size += 1 def deleteAtIndex(self, index): if index < 0 or index >= self.size: return current = self.null_node for i in range(index): current = current.next current.next = current.next.next self.size -= 1
环形链表
先判断是否有环:双指针解法,分别定义快慢指针fast和slow,fast每次移动两个节点,slow每次移动一个节点,若fast和slow在途中相遇说明这个链表有环
判断环的入口,在fast入环后,等追上刚入环的slow,至少已经走过了一圈,二者相遇时,slow走过了x+y,fast走过了x+y+n(y+z),fast是slow的两倍速,故有(x + y) * 2 = x + y + n (y + z) 整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针,当n=1,有x=z,意味着从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点,如果n>1就是fast指针在环形转n圈之后才遇到 slow指针
代码定义
class Solution(object): def detectCycle(self, head): slow = head fast = head while fast and fast.next: fast = fast.next.next slow = slow.next if slow == fast: slow = head while slow != fast: slow = slow.next fast = fast.next return slow return None
链表相交
链表相交的交点,指的是指针相同而非数值相同,将两个链表结尾靠在一起,指针A指向长链表,指针B指向短链表,将A先移动到和B头指针对齐的位置,再一起向后移动直到找到两个指针相同的节点,返回该节点即可
代码定义
class Solution(object): def getIntersectionNode(self, headA, headB): # 定义虚拟头节点,找到较长的列表 dummya = ListNode(next=headA) dummyb = ListNode(next=headB) sizea,sizeb = 0, 0 while headA: sizea += 1 headA = headA.next while headB: sizeb += 1 headB = headB.next #cura指向长的curb指向短的 if sizea > sizeb: curra = dummya currb = dummyb # 让cura先移动到和curb对齐的位置 for i in range(sizea-sizeb): curra =curra.next else: curra = dummyb currb = dummya for i in range(sizeb-sizea): curra =curra.next while curra: if curra == currb: return curra else: curra = curra.next currb = currb.next return None
删除链表中的倒数第N个节点
使用双指针解决:需要定义fast和slow两个指针,fast先走n+1步,确保slow能够指向删除节点的上一个节点,之后fast和slow同时移动,直到fast指向末尾的null时,然后根据slow.next来删除倒数第n个节点
代码定义
class Solution(object): def removeNthFromEnd(self, head, n): dummy = ListNode(next=head) fast, slow = dummy, dummy for i in range(n+1): fast = fast.next while fast: fast = fast.next slow = slow.next slow.next = slow.next.next return dummy.next
链表节点的两两交换
定义虚拟头结点, 分三步走:让头结点cur先指向2,再让2指向1,再让1指向3即可,中途需要保证节点的不丢失,交换结束后,cur后移两位
代码定义
class Solution(object): def swapPairs(self, head): dump_head = ListNode(next=head) curr_head = dump_head while curr_head.next and curr_head.next.next: temp1 = curr_head.next #第一个节点 temp2 = curr_head.next.next.next # 第三个节点 curr_head.next = curr_head.next.next # cur指向第二个节点 curr_head.next.next = temp1 # 第二个节点指向第一个 temp1.next = temp2 #第一个指向第三个 curr_head = curr_head.next.next #cur后移两个位置 return dump_head.next
链表的反转
双指针:定义一个cur指向头结点,再定义一个pre初始化为null,首先要把 cur->next 节点用tmp指针临时保存,因为接下来要改变 cur->next 的指向了,若不保存后续节点会丢失,将cur->next 指向pre,之后继续移动pre和cur指针,先移动pre,在移动cur
class Solution(object): def reverseList(self, head): curr = head pre = None while curr: temp = curr.next curr.next = pre pre = curr curr = temp return pre
递归
class Solution: def reverseList(self, head): return self.reverse(head, None) def reverse(self, cur, pre): if cur == None: return pre temp = cur.next cur.next = pre return self.reverse(temp, cur)