概念性的内容可以参考下面这篇博文:(个人觉得博主写的很不错,如果侵权请私信联系删除。)
链表python基础知识_一蓑烟雨晴的博客-CSDN博客
前几天没有更新,觉得链表这里还是挺绕的,今天把这几天做的链表相关的题目都整理一下:
题目一:【模板】链表
请你实现一个链表。
操作:
insert x y:将y加入链表,插入在第一个值为x的结点之前。若链表中不存在值为x的结点,则插入在链表末尾。保证x,y为int型整数。
delete x:删除链表中第一个值为x的结点。若不存在值为x的结点,则不删除。输入描述:
第一行输入一个整数n (1≤n≤10^4),表示操作次数。
接下来的n行,每行一个字符串,表示一个操作。保证操作是题目描述中的一种。输出描述:
输出一行,将链表中所有结点的值按顺序输出。若链表为空,输出"NULL"(不含引号)。
示例:
输入 输出 5
insert 0 1
insert 0 3
insert 1 2
insert 3 4
delete 42 1 3 思路:
首先创建一个空列表,作为我们初始的链表;
通过input接收n(记得转化为int类型);
设计两个函数insert和delete;
我们所需要用到的主要知识点就是对列表元素查找指定位置,获取位置下标进行插入删除操作。
所以插入的核心代码是
def insert(x,y): if x not in Linklist: Linklist.append(y) else: loc = Linklist.index(x) Linklist.insert(loc,y)
删除的核心代码是
def delete(x): if x in Linklist: loc = Linklist.index(x) Linklist.pop(loc)
最后输出列表的内容,而不是输出整个列表:
if len(LinkList)==0: print('NULL') else: for i in LinkList: print(i,end=' ')
代码实现:
class Node: def __init__(self) -> None: self.data = [] def insert(self, x, y): if x in self.data: loc = self.data.index(x) self.data.insert(loc, y) else: self.data.append(y) def delete(self, x): if x in self.data: loc = self.data.index(x) self.data.pop(loc) else: print() def taverlse(self): for i in range(len(self.data)): print(self.data[i], end=" ") n = Node() n_input = int(input()) for ip in range(n_input): inp = input() func = inp.split(" ")[0] if func == "insert": n.insert(int(inp.split(" ")[1]), int(inp.split(" ")[2])) elif func == "delete": if n.data != []: n.delete(int(inp.split(" ")[1])) if not n.data: print("NULL") else: n.taverlse()
题目二:反转链表
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围:0≤n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
示例:
示例1 示例2 输入:
{1,2,3}返回值:
{3,2,1}输入:
{}返回值:
{}说明:
空链表则输出空思路:
将链表反转,就是将每个表元的指针从向后变成向前,那我们可以遍历原始链表,将遇到的节点一一指针逆向即可。指针怎么逆向?不过就是断掉当前节点向后的指针,改为向前罢了。
cur.next = pre
算法步骤:
- step 1:优先处理空链表,空链表不需要反转。
- step 2:我们可以设置两个指针,一个当前节点的指针,一个上一个节点的指针(初始为空)。
- step 3:遍历整个链表,每到一个节点,断开当前节点与后面节点的指针,并用临时变量记录后一个节点,然后当前节点指向上一个节点,即可以将指针逆向。
- step 4:再轮换当前指针与上一个指针,让它们进入下一个节点及下一个节点的前序节点。
图示:
代码实现:
# class ListNode: # def __init__(self, x): # self.val = x # self.next = None # # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param head ListNode类 # @return ListNode类 # class Solution: def ReverseList(self , head: ListNode) -> ListNode: # write code here #处理空链表 if not head: return None cur = head pre = None while cur: #断开链表,要记录后续一个 temp = cur.next #当前的next指向前一个 cur.next = pre #前一个更新为当前 pre = cur #当前更新为刚刚记录的后一个 cur = temp return pre
题目三:合并两个排序的链表
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0≤n≤1000,−1000≤节点值≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
或输入{-1,2,4},{1,3,4}时,合并后的链表为{-1,1,2,3,4,4},所以对应的输出为{-1,1,2,3,4,4},转换过程如下图所示:
示例:
示例1 示例2 示例3 输入:
{1,3,5},{2,4,6}返回值:
{1,2,3,4,5,6}输入:
{},{}返回值:
{}输入:
{-1,2,4},{1,3,4}返回值:
{-1,1,2,3,4,4}思路:
方法一:迭代
设置result为哑结点,放置于新链表之前,最后返回的就是result.next;设置cur为当前节点,从result开始,当两个链表都非空时进入循环,令新链表的下一个节点cur.next为val更小的节点,相应的链表节点后移一位,每次循环记得cur也要后移一位。如果循环结束后还有链表非空,cur指向非空链表,返回result.next。
代码实现:
# -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: # 返回合并后列表 def Merge(self, pHead1, pHead2): # write code here if not pHead1: return pHead2 if not pHead2: return pHead1 result = ListNode(-1) cur = result while pHead1 and pHead2: # 元素对比 if pHead1.val <= pHead2.val: cur.next = pHead1 pHead1 = pHead1.next else: cur.next = pHead2 pHead2 = pHead2.next # 指针右移动一位 cur = cur.next # 拼接未对比的链表 cur.next = pHead1 if pHead1 else pHead2 return result.next
方法二:递归
特殊情况:如果有一个链表为空,返回另一个链表;如果pHead1 节点值比小pHead2,下一个节点应该是 pHead1,应该return pHead1,在return之前,指定pHead1的下一个节点应该是pHead1.next和pHead2俩链表的合并后的头结点;如果pHead1 节点值比pHead2大,下一个节点应该是pHead2,应该return pHead2,在return之前,指定pHead2的下一个节点应该是pHead1和pHead2.next俩链表的合并后的头结点。
代码实现:
# class ListNode: # def __init__(self, x): # self.val = x # self.next = None # # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param pHead1 ListNode类 # @param pHead2 ListNode类 # @return ListNode类 # class Solution: def Merge(self , pHead1: ListNode, pHead2: ListNode) -> ListNode: # write code here # list1 list2为空的情况 if not pHead1 or not pHead2 : if pHead1 != None: return pHead1 else: return pHead2 # 两个链表元素依次对比 if(pHead1.val <= pHead2.val): # 递归计算 pHead1.next, pHead2 pHead1.next = self.Merge(pHead1.next, pHead2) return pHead1 else: # 递归计算 pHead1, pHead2.next pHead2.next = self.Merge(pHead1, pHead2.next) return pHead2
时间复杂度O(N+M):M N分别表示pHead1, pHead2的长度
空间复杂度O(N+M):迭代次数占用空间
题目四:删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
1.此题对比原题有改动
2.题目保证链表中节点的值互不相同
3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
数据范围:
0<=链表节点值<=10000
0<=链表长度<=10000
示例:
示例1 示例2 输入:
{2,5,1,9},5返回值:
{2,1,9}说明:
给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 2 -> 1 -> 9输入:
{2,5,1,9},1返回值:
{2,5,9}说明:
给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 2 -> 5 -> 9思路:
既然是整个链表元素都不相同,我们要删除给定的一个元素,那我们首先肯定要找到这个元素,然后考虑删除它。
删除一个链表节点,肯定是断掉它的前一个节点指向它的指针,然后指向它的后一个节点,即越过了需要删除的这个节点。
pre.next = cur.next
算法步骤:
- step 1:首先我们加入一个头部节点,方便于如果可能的话删除掉第一个元素。
- step 2:准备两个指针遍历链表,一个指针指向当前要遍历的元素,另一个指针指向该元素的前序节点,便于获取它的指针。
- step 3:遍历链表,找到目标节点,则断开连接,指向后一个。
- step 4:返回时去掉我们加入的头节点。
图示:
代码实现:
# class ListNode: # def __init__(self, x): # self.val = x # self.next = None # # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # # @param head ListNode类 # @param val int整型 # @return ListNode类 # class Solution: def deleteNode(self, head: ListNode, val: int) -> ListNode: # 加入一个头节点 res = ListNode(0) res.next = head # 前序节点 pre = res # 当前节点 cur = head # 遍历链表 while cur is not None: # 找到目标节点 if cur.val == val: # 断开连接 pre.next = cur.next break pre = cur cur = cur.next # 返回去掉头节点 return res.next