文章目录
- 1. 链表特点
- 2. 链表创建
- 3. 链表遍历通用方法
- 3.1 在链表的开头添加元素
- 3.2 在链表的结尾添加元素
- 3.3 删除链表的第一个元素
- 3.4 删除链表的最后一个元素
- 3.5 遍历链表
- 3.6 查找链表中的元素
- 3.7 反转链表
- 4. 常见面试题
- 4.1 相交链表
- 4.2 反转链表
- 4.3 环形链表
- 4.4 环形链表 II
- 4.5 合并两个有序链表
- 4.6 两数相加
- 4.7 删除链表的倒数第 N 个结点
- 4.8 两两交换链表中的节点
- 5. 参考文档
链表常见的使用方法逻辑整理
1. 链表特点
链表特点
- 由节点组成:链表是由一个个节点组成的数据结构,每个节点包含数据和指向下一个节点的引用(指针/链接)。
- 动态插入和删除:相对于数组,链表可以更轻松地进行节点的插入和删除操作,因为只需要调整节点的指针,而无需移动大量元素。
- 内存利用灵活:链表的节点在内存中不必连续存储,这使得链表可以更灵活地利用内存,但也可能导致对内存的随机访问效率较低。
- 随机访问效率低:链表的节点不能直接通过索引进行访问,需要从头节点开始逐个遍历,因此随机访问效率较低。
- 多种类型:链表有单向链表、双向链表和循环链表等不同类型,每种类型都有不同的特点和适用场景。
链表是一种由节点组成的线性数据结构,每个节点包含一个数据元素和一个指向下一个节点的指针。跟数据对比有差异
- 数组能够通过固定下标查找对应的数据,查找性能优越,但是每次插入/删除数据都需要将对应节点后续的数据整体挪移,因此数组的更新成本较大
- 链表更新节点时只需要重新插入一个新节点,并调整上下节点的指针,无需移动后续的所有节点,因此更新成本较小,但是当需要获取当个节点数据时,由于没有固定下标,因此必须从头开始查找,因此查询性能较差
两者类型的数据结构,分别在读写时表现有差异,因此需要根据不同的应用场景(读写的频率差异)进行合理使用
2. 链表创建
链表中的每一个元素都是一个节点,每个节点通常包含两部分:数据和下一个节点的引用。
class Node:def __init__(self, data):self.data = data # 节点存储的数据self.next = None # 默认下一个节点为空class LinkedList:def __init__(self):self.head = None # 初始链表为空
3. 链表遍历通用方法
3.1 在链表的开头添加元素
def add_first(self, data):new_node = Node(data) # 创建新的节点new_node.next = self.head # 将新节点指向当前的头节点self.head = new_node # 更新头节点为新节点LinkedList.add_first = add_first
3.2 在链表的结尾添加元素
def add_last(self, data):new_node = Node(data)if self.head is None: # 若链表为空,则直接将新节点设置为头节点self.head = new_nodereturnlast_node = self.head # 遍历到链表的尾部while last_node.next:last_node = last_node.nextlast_node.next = new_node # 在链表尾部添加新节点LinkedList.add_last = add_last
3.3 删除链表的第一个元素
def remove_first(self):if self.head:self.head = self.head.next # 更新头节点为下一个节点LinkedList.remove_first = remove_first
3.4 删除链表的最后一个元素
def remove_last(self):if self.head is None: # 若链表为空,直接返回returnif self.head.next is None: # 若链表只有一个元素,将头节点设置为空self.head = Nonereturnsecond_to_last = self.head # 找到倒数第二个节点while second_to_last.next.next:second_to_last = second_to_last.nextsecond_to_last.next = None # 将倒数第二个节点的next设置为空,从而删除最后一个节点LinkedList.remove_last = remove_last
3.5 遍历链表
def print_list(self):current_node = self.head # 从头节点开始遍历while current_node:print(current_node.data, end=" -> ")current_node = current_node.next # 移动到下一个节点print("None")LinkedList.print_list = print_list
3.6 查找链表中的元素
def search(self, target):current_node = self.head # 从头节点开始遍历while current_node:if current_node.data == target: # 若找到目标数据,返回Truereturn Truecurrent_node = current_node.next # 移动到下一个节点return False # 遍历完链表后,未找到目标数据,返回FalseLinkedList.search = search
3.7 反转链表
def reverse(self):prev = None # 上一个节点current = self.head # 当前节点while current:next_node = current.next # 记录下一个节点current.next = prev # 将当前节点指向上一个节点prev = current # 更新上一个节点为当前节点current = next_node # 移动到下一个节点self.head = prev # 更新头节点LinkedList.reverse = reverse
4. 常见面试题
一些总结
4.1 相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = Noneclass Solution(object):def getIntersectionNode(self, headA, headB):""":type head1, head1: ListNode:rtype: ListNode"""if headA is None or headB is None:returnpA, pB = headA, headBwhile pA != pB:pA = headB if pA is None else pA.nextpB = headA if pB is None else pB.nextreturn pA
4.2 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def reverseList(self, head):""":type head: ListNode:rtype: ListNode"""pre, cur = None, headwhile cur:next = cur.nextcur.next = prepre = curcur = nextreturn pre
4.3 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
# Definition for singly-linked list.
class ListNode(object):def __init__(self, x):self.val = xself.next = Noneclass Solution(object):def hasCycle(self, head):""":type head: ListNode:rtype: bool"""slow, fast = head, headwhile fast is not None and fast.next is not None and slow != None:slow = slow.nextfast = fast.next.nextif slow == fast:return Truereturn False
4.4 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = Noneclass Solution(object):def detectCycle(self, head):""":type head: ListNode:rtype: ListNode"""slow, fast = head, headwhile True:if not (fast and fast.next): returnslow = slow.nextfast = fast.next.nextif slow == fast:breakfirst= headwhile first != slow:first, slow = first.next, slow.nextreturn first
4.5 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def mergeTwoLists(self, list1, list2):""":type list1: Optional[ListNode]:type list2: Optional[ListNode]:rtype: Optional[ListNode]"""if not list1: return list2if not list2: return list1if list1.val <= list2.val:list1.next = self.mergeTwoLists(list1.next, list2)return list1list2.next = self.mergeTwoLists(list1, list2.next)return list2
4.6 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def addTwoNumbers(self, l1, l2):""":type l1: ListNode:type l2: ListNode:rtype: ListNode"""cur = dummy = ListNode()carry = 0while l1 or l2 or carry:# 做加合运算carry += (l1.val if l1 else 0) + (l2.val if l2 else 0)# 如果超过10,需要做进位调整cur.next = ListNode(carry%10)# 取余数carry //= 10# 在一个节点cur = cur.nextif l1: l1 = l1.nextif l2: l2 = l2.nextreturn dummy.next
4.7 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def removeNthFromEnd(self, head, n):""":type head: ListNode:type n: int:rtype: ListNode"""slow = dummy_head = ListNode(next=head)fast = headwhile fast and n:fast = fast.nextn -=1while fast:fast = fast.nextslow = slow.nextslow.next = slow.next.nextreturn dummy_head.next
4.8 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head = []
输出:[]
示例 3:
输入:head = [1]
输出:[1]
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):def swapPairs(self, head):""":type head: ListNode:rtype: ListNode"""p = ListNode(-1)a,b,p.next,tmp = p,p,head,pwhile b.next and b.next.next:a,b = a.next,b.next.nexttmp.next,a.next,b.next = b,b.next,atmp,b = a,areturn p.next
5. 参考文档
暂无,相关面试题请参考leetcode以及相关说明