Python数据结构和算法笔记五:链表

链表

链表的概念概念
  • 链表是一种常见的链式结构(linked list)
  • 链表由节点链接而成
  • 每个链表的节点包含数据成员和指向下一个节点的指针
不同的链表类型
单链表(linked list)
  • 特点:

    • 1、可以方便地追加元素到链表尾部,O(1)
    • 2、不支持随机下标访问,查找元素地时间复杂度是O(n),需要从头开始一个个查找
  • class Node:
    	def __init__(self, value, next=None):
    		self.value = value
    		self.next = next
    
双链表(double linked list)
  • 特点:

    • 1、可以给单链表再加一个指针(节点前后都有一个指针,左指针和右指针),指向前一个节点
    • 2、双链表可以支持反向遍历
    • 3、知道了双链表的一个节点后,可以将它的左指针或右指针指向另一个节点,即将两个节点串起来,从而实现**O(1)**插入,同理删除也是O(1)
    • 4、双链表还可以首位指针相连形成一个循环双端链表(至少两个节点)
    • 5、双链表可以高效地往两头增加或删除元素
  • class Node:
    	def __init__(self, value, prev=None, next=None):
    		self.value, self.prev, self,next = value, prev, next
    
链表练习
如何将一个list构造一个链表并编写一个打印单链表的函数
  • class LinkedListNode():
        def __init__(self, value, next=None):
            self.value = value
            self.next = next
    
    
    def gen_linked_list(nums):
    
        if not nums:
            return None
        head = LinkedListNode(nums[0])
        cur = head
        for i in range(1, len(nums)):
            node = LinkedListNode(nums[i])
            cur.next = node
            cur = node
        return head
    
    
    def print_linked_list(head):
        cur = head
        while cur:
            print('{}->'.format(cur.value), end='')
            cur = cur.next
        print('nil')
    
    if __name__ == "__main__":
        nums = [1,2,3,4]
        head = gen_linked_list(nums)
        print_linked_list(head)
    
反转链表
  • 解法:

    • 第一种方法利用指针迭代,比较好理解

    • 第二种使用递归,找出最后一个节点,然后递归实现最后一个节点需要做的事情

    • class Solution(object):
          def reverseList(self, head):
              """
              :type head: ListNode
              :rtype: ListNode
              """
              # 迭代 时间复杂度:O(n),空间复杂度:O(1)
              # pre = None
              # cur = head
              # while cur:
              #     nextnode = cur.next
              #     cur.next = pre
              #     pre = cur
              #     cur = nextnode
              # return pre
      
              # 递归 时间复杂度:O(n),空间复杂度:O(1)
              if not (head and head.next):
                  return head
              newhead = self.reverseList(head.next)
              head.next.next = head
              head.next = None
              return newhead
      
      
      # when-changed -r -v -1 'filepath' pytest -s 'filepath'
      def test_reverselist():
          L = [1,2,3,4,5]
          head = gen_linked_list(L)
          print()
          print_linked_list(head)
          pre = Solution().reverseList(head)
          print_linked_list(pre)
      
      
合并两个排序的链表
  • 解法:

    • 纸上做草稿,使用指针完成

    • 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
      
      示例1:
      
      输入:1->2->4, 1->3->4
      输出:1->1->2->3->4->4
      
    • # Definition for singly-linked list.
      class ListNode(object):
          def __init__(self, x):
              self.val = x
              self.next = None
      
      class Solution(object):
          def mergeTwoLists(self, l1, l2):
              """
              :type l1: ListNode
              :type l2: ListNode
              :rtype: ListNode
              """
              pre = ListNode(0)
              cur = pre
              while l1 and l2:
                  if l1.val <= l2.val:
                      cur.next = l1
                      l1 = l1.next
                      cur = cur.next
                  else:
                      cur.next = l2
                      l2 = l2.next
                      cur = cur.next
      
              cur.next = l1 or l2
              return pre.next
      
      def test_mergeTwoLists():
          l1 = gen_linked_list([1,2,4])
          l2 = gen_linked_list([1,3,4])
          s = Solution().mergeTwoLists(l1, l2)
          print_linked_list(s)
      
      
      def gen_linked_list(nums):
      
          if not nums:
              return None
          head = ListNode(nums[0])
          cur = head
          for i in range(1, len(nums)):
              node = ListNode(nums[i])
              cur.next = node
              cur = node
          return head
      
      
      def print_linked_list(head):
          cur = head
          while cur:
              print('{}->'.format(cur.val), end='')
              cur = cur.next
          print('nil')
      
      
相交链表
  • 解法:

    • 第一种方式,将长链表砍掉一部分使得它的长度与短链表一样长,然后判断

    • 第二种方式,将两个链表相交即长度都相等了,再判断

    • 编写一个程序,找到两个单链表相交的起始节点。
      
    • 在这里插入图片描述

    • class ListNode(object):
          def __init__(self, x):
              self.val = x
              self.next = None
      
      class Solution(object):
          def getIntersectionNode(self, headA, headB):
              """
              :type head1, head1: ListNode
              :rtype: ListNode
              """
              # 第一种方式,将长链表砍掉一部分使得它的长度与短链表一样长
              # if headA is None or headB is None:
              #     return None 
      
              # curA = headA
              # lenA = 0
              # while curA:
              #     lenA += 1
              #     curA = curA.next
      
              # curB = headB
              # lenB = 0
              # while curB:
              #     lenB += 1
              #     curB = curB.next
              
              # ldiff = abs(lenA-lenB) 
              # if lenA > lenB:
              #     for _ in range(ldiff):
              #         headA = headA.next
              # else:
              #     for _ in range(ldiff):
              #         headB = headB.next
              
              # while headA and headB:
              #     if headA == headB:
              #         # print(headA,headB)
              #         return headA
              #     headA = headA.next
              #     headB = headB.next
              
              # return None
      
              # 第二种方式,将两个链表相交即长度都相等了,再判断
              ha, hb = headA, headB
              while ha != hb:
                  ha = ha.next if ha else headB
                  hb = hb.next if hb else headA
              return ha
      
环形链表
  • 思路:

    • 1、开辟一个新的内存空间即创建一个set(集合),利用集合元素不重复的特性

    • 2、快慢指针,若闭环,快慢指针一定会相遇

    • 给定一个链表,判断链表中是否有环。
      
      为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
      
    • 在这里插入图片描述

    • # Definition for singly-linked list.
      class ListNode(object):
          def __init__(self, x):
              self.val = x
              self.next = None
      
      class Solution(object):
          def hasCycle(self, head):
              """
              :type head: ListNode
              :rtype: bool
              """
              # 第一种使用hash表存储(集合,根据元素不重复的特性)
              # 时间复杂度O(n),空间复杂度O(n)
              # hash = {}
              # cur = head
              # while cur:
              #     if hash.get(cur.next) is not None:
              #         return True
              #     hash[cur] = 1
              #     cur = cur.next
              # return False
              
              # 利用快慢指针
              # 时间复杂度O(n),空间复杂度O(1)
              slow, fast = head, head
              while fast and fast.next:
                  slow = slow.next
                  fast = fast.next.next
                  if slow == fast:
                      return True
              return False
      
两数相加
  • 思路:

    • 1、竖式计算,定义多几个变量(指针)进行操作,会方便很多,重点在于获取值的方式,%10为获取个位,//10获取进位

    • 2、使用递归的方式

    • 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
      如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
      您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
      
      示例:
      输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
      输出:7 -> 0 -> 8
      原因:342 + 465 = 807
      
    • class ListNode(object):
          def __init__(self, x):
              self.val = x
              self.next = None
      
      class Solution:
      	# 迭代的方式
          def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
              # 迭代的方式,创建了新的链表,空间复杂度O(n),时间复杂度O(n)
              pre = cur = ListNode(None) # pre变量用于返回,cur用于指向新创建的节点
              s = 0                       # 中间暂存变量
              while l1 or l2 or s:
                  # if l1 == None:
                  #     l1 = ListNode(0)
                  # if l2 == None:
                  #     l2 = ListNode(0)
                  s += (l1.val if l1 else 0) + (l2.val if l2 else 0)  # 注意是+=,存储l1和l2节点的值
                  cur.next = ListNode(s%10) # 新节点赋值  %10取个位
                  s = s//10   # 取进位的值
                  cur = cur.next # 使指向新节点的指针向前指
                  l1 = l1.next if l1 else None # l1向前走
                  l2 = l2.next if l2 else None # l2向前走
              return pre.next # 返回新节点的首节点
      
      # 递归的方式,添加参数carry
      # class Solution:
          # def addTwoNumbers(self, l1: ListNode, l2: ListNode, carry=0) -> ListNode:
          #     if l1==None and l2==None and carry==0:  
          #         return None
      
          #     if l1==None and l2==None and carry==1 :
          #         return ListNode(1)
              
          #     if l1==None :
          #         l1 = ListNode(0)
          #     if l2==None :
          #         l2 = ListNode(0)
      
          #     l1.val, carry = (l1.val+l2.val+carry)%10, (l1.val+l2.val+carry)//10
          #     l1.next = self.addTwoNumbers(l1.next, l2.next, carry)
      
          #     return l1
      
      
      def test_addTwoNumbers():
          l1 = gen_linked_list([2,4,3,2])
          l2 = gen_linked_list([5,6,4])
          s = Solution().addTwoNumbers(l1,l2)
          print_linked_list(s)
      
      def gen_linked_list(nums):
      
          if not nums:
              return None
          head = ListNode(nums[0])
          cur = head
          for i in range(1, len(nums)):
              node = ListNode(nums[i])
              cur.next = node
              cur = node
          return head
      
      
      def print_linked_list(head):
          cur = head
          while cur:
              print('{}->'.format(cur.val), end='')
              cur = cur.next
          print('nil')
      
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值