算法入门——python数据结构(链表)(9)

概念性的内容可以参考下面这篇博文:(个人觉得博主写的很不错,如果侵权请私信联系删除。)

链表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 4
2 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

弓早早o_O

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值