Python描述数据结构之链表实战篇

前言

  刷题,尤其是像我这种刚开始刷题的新手,最重要的是要先有个能够执行的算法,能出个结果,即使这个算法很笨,结果很差,也没事,然后再在其基础之上进行改进算法,去做优化,先易后难,一定要思考,画画图,がんばれ(ง •̀_•́)ง!!!

  为了方便调试自己的代码,使其能直接运行在LeetCode上,所以,我根据前面几篇博客重新定义了以下函数:

函数名 功能
CreateSingleLinkList(list) 传入一个数组,将数组转换成无头结点的单链表
TraverseList(linklist) 传入一个单链表,将其节点的值打印出来
class ListNode(object):
    def __init__(self, val=None, next=None):
        self.val = val
        self.next = next


class LeetCodeSingleLinkList(object):
    def __init__(self):
        self.__head = ListNode(None)

    def CreateSingleLinkList(self, val_list):
        prehead = self.__head
        for val in val_list:
            new_node = ListNode(val)
            prehead.next = new_node
            prehead = prehead.next
        # 第一个节点是头结点, 所以要返回链表的下一个节点
        # 即与LeetCode的保持一致
        return self.__head.next


def TraverseList(result):
    # result是不含头结点的
    while result:
        print(result.val, end=' ')
        result = result.next
    print('')


if __name__ == '__main__':
    l1 = LeetCodeSingleLinkList().CreateSingleLinkList([1, 2, 4])
    TraverseList(l1)
    

  这里是LeetCode中有关链表的题库。另外还需要注意,val的数据类型,我这里统一定义为了None

1. LeetCode21: 合并两个有序链表

  LeetCode的第21题:合并两个有序链表
  看到这个题,很大可能想到的就是遍历两个链表,然后进行比较,所以就有了一个遍历的条件,即链表L1和链表L2都不为空。
  这里我们采用带头结点的单链表比较方便,我们可以定义一个指针prev,不断地去比较链表L1和链表L2对应的值,如果L1的值小,就将指针prev指向L1,然后L1后移,继续用其第二个结点的值与L2的第一个结点的值比较,每比较完一次,prev都要后移,指向新的结点,就这样遍历一遍。如果链表L1和链表L2的长度不一致,那么指针prev最后将指向较长的那个链表。
  实现代码如下:

def mergeTwoLists(l1, l2):
    prevhead = ListNode(None)
    prev = prevhead
    while l1 and l2:
        if l1.val <= l2.val:
            prev.next = l1
            l1 = l1.next
        else:
            prev.next = l2
            l2 = l2.next
        prev = prev.next
    prev.next = l1 if l1 else l2
    # 返回不带头结点的链表
    return prevhead.next

  运行结果,emmmm:

在这里插入图片描述

2. LeetCode237: 删除链表中的节点

  LeetCode的第237题:删除链表中的节点
  这个题给我的感觉就是比较坑,看到这个就先想到了双链表找前驱结点,或者用单链表带两个指针(一前一后)也可以找到,然后就不加思索的实现了,具体代码详见下面的3.3 剑指 Offer 18: 删除链表的节点
  然后提交的时候发现,函数没有传入链表,只传入了需要被删除的结点,而且还不要返回结果,莫名其妙(•́へ•́╬)懵圈。然后看了官方题解,才理解。。。。。。就是直接把要被删除节点的值被其下一个节点的值给覆盖掉,简单粗暴!!!∑(゚Д゚ノ)ノ就是没想到,看来还是涉世未深呐(这是我LeetCode简单系列的第二题(︶.̮︶✽))。
  代码如下:

def deleteNode(node):
    # 简单粗暴
    # 237. 删除链表中的节点
    node.val = node.next.val
    node.next = node.next.next

  代码如下:

在这里插入图片描述
  只击败了18%的用户,看来还有很大的提升空间呐!

3. 剑指 Offer 18: 删除链表的节点

   剑指 Offer的第18题:删除链表中的节点
  这个题才比较正常嘛,注意要看看题目的说明以及官方给的函数参数,单链表实现代码如下:

def deleteNode(head, val):
    # 给链表加上一个头结点
    fakehead = ListNode(None)
    fakehead.next = head
    prehead = fakehead
    while prehead.next:
        result = prehead
        prehead = prehead.next
        if prehead.val == val:
            if prehead.next is None:
                result.next = None
            else:
                result.next = prehead.next
            return fakehead.next
    return fakehead.next
    

  运行如下:

在这里插入图片描述

4. LeetCode234: 回文链表

  LeetCode第234题:回文链表
  官方给的依旧是单链表,另外,题目中只是判断是否为回文链表,即结果返回是布尔值TrueFalse,enmmm,既然这样,那完全可以用数组实现,哈哈哈哈哈哈,前面博客也说了,在Python中,数组和字典是基本的数据结构,我们可以基于这两种结构构造其他的数据结构。
  大致就是将单链表每个节点的值放在数组里,因为回文链表是个对称的结构,回文数组也是,只需判断原数组与翻转后的数组是否一样即可。
  代码如下:

def isPalindrome(head):
    prehead = head
    fake_list = []
    while prehead:
        fake_list.append(prehead.val)
        prehead = prehead.next
    return fake_list == fake_list[::-1]

  运行结果如下:

在这里插入图片描述

5. 面试题 02.02: 返回倒数第 k 个节点

  面试题 02.02:返回倒数第 k 个节点
  可以做成两个指针frontrear,两个指针的间距为 k k ,当后面那个指针rear走到最后时,前面的front指针就来到了倒数第 k k 个节点。

def kthToLast(head, k):
    front = head
    rear = head
    for i in range(k):
        rear = rear.next
    while rear:
        front = front.next
        rear = rear.next
    return front.val

  运行结果如下:

在这里插入图片描述

6. LeetCode83:删除排序链表中的重复元素

  LeetCode第83题:删除排序链表中的重复元素

  一失足成千古恨呐,本来是想执行一下代码,结果点成提交了(’∇’)シ┳━┳,少了一个条件。这个题还是很简单的(本来练习的就是简单题),题目中也说了是一个排序链表,如此就直接判断当前结点的值与其后继节点的值是否一样,不一样的话指针后移;若值一样,则将当前结点的next指向其后继结点的后继结点,最后返回处理后的链表。

def deleteDuplicates(head):
    prehead = head
    while prehead and prehead.next:
        if prehead.val == prehead.next.val:
            prehead.next = prehead.next.next
        else:
            prehead = prehead.next
    return head

  运行结果如下:

在这里插入图片描述

7. LeetCode1290:二进制链表转整数

  LeetCode第1290题:二进制链表转整数
  这个题目就是个二进制转十进制,不同的是这次我们不是从最低位开始,而是要从最高位,这也很容易。在学习C语言时就晓得十进制转二进制,就是不断除2,然后余数最下面的就是最高位,就像下面这样子:
在这里插入图片描述
  13的二进制就是1101,如果从最高位开始将其转换为十进制就是: 2 × 0 + 1 = 1 > 2 × 1 + 1 = 3 > 2 × 3 + 0 = 6 > 2 × 6 + 1 = 13 2\times0+1=1-->2\times1+1=3-->2\times3+0=6-->2\times6+1=13 ,代码实现如下:

def getDecimalValue(head):
    prehead = head
    result = 0
    while prehead:
        result = 2 * result + prehead.val
        prehead = prehead.next
    return result

  运行结果如下:

在这里插入图片描述
  当然,在Python可以直接将二进制转换成十进制,用int()函数就可以,代码如下:

def getDecimalValue(head):
    prehead = head
    result = '0b'
    while prehead:
        result += str(prehead.val)
        prehead = prehead.next
    return int(result, 2)

  运行结果如下:

在这里插入图片描述

结束语

  虽然以前学过数据结构,但平时没有刷过题,这也是第一次刷题,哈哈哈惭愧惭愧,所以就从LeetCode的简单题目入手,然后慢慢加深加大难度。做了这几个题,我觉得对于我这种新手来说,还是要画画图,需要的话在调试一下,哈哈哈哈,加油(ง •̀_•́)ง

猜你喜欢

转载自blog.csdn.net/qq_42730750/article/details/107869944