数据结构与算法——链表

一、单链表

表域元素val保存着数据项,链接域next保存同一表里的下个节点的标识,P为表头变量/表头指针

单链表.png

1、插入新元素

  • 初始化新节点cur

  • curnext字段链接到prev的下个节点

  • prev中的next字段链接到cur

    单链表插新元素1.png

单链表插新元素2.png

2、在开头插入节点

  • 初始化一个新节点cur

  • 将新节点链接到原始头结点head

  • cur指定为head

    单链表表头插入元素.png

3、删除操作

  • 找到cur的上一个结点prev及其下一个结点next

  • 链接prevcur的下一个结点next

    单链表删除元素.png

在第一步中,要找到prevnext,需要从头结点开始遍历链表,才能找到prev,平均时间复杂度是 O ( n ) O(n) O(n)

4、删除第一个节点

  • 给定原始链表和头结点head

  • 将头结点的下个节点指定为头结点head

    单链表删除头结点.png

5、单链表操作的复杂度

  • 创建链表: O ( 1 ) O(1) O(1)

  • 删除链表:在python里是 O ( 1 ) O(1) O(1)

  • 判断表空: O ( 1 ) O(1) O(1)

  • 首端加入元素: O ( 1 ) O(1) O(1)

  • 尾端加入元素,要找到表的最后节点: O ( n ) O(n) O(n)

  • 定位插入元素: O ( n ) O(n) O(n)

  • 首端删除元素: O ( 1 ) O(1) O(1)

  • 尾端删除元素: O ( n ) O(n) O(n)

  • 定位删除元素: O ( n ) O(n) O(n)

  • 扫描、定位、遍历: O ( n ) O(n) O(n)

6、循环单链表

在链表对象里记录尾节点,这样可以同时支持 O ( 1 ) O(1) O(1)的表头/表尾插入和 O ( 1 ) O(1) O(1)的表头删除

循环单链表.png

二、双链表

双链表除了next字段,还有一个prev字段,指向当前节点的前一个节点

双链表.png

1、添加操作

  • 初始化一个新节点cur

  • 链接curprevnext,链接curprevprev

  • cur重新链接prevnext

    双链表的添加操作.png

2、删除操作

双链表删除操作.png

3、循环双链表

循环双链表.png

三、代码实现

1、单链表

单链表中的节点应该具有两个属性:valnextval 是当前节点的值,next 是指向下一个节点的指针/引用。假设链表中的所有节点都是 0-index 的。在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1

  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。

  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。

  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。

  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

# 节点类,代表链表的每个节点
class Node(object):
    def __init__(self, x, next_=None):
        # 节点的值
        self.val = x
        # 节点的下个指向
        self.next = next_

# 链表类
class MyLinkedList(object):
    # 初始化
    def __init__(self):
        # 初始化头部节点
        self.head = None
        # 记录链表长度
        self.length = 0
        
    # 打印链表所有节点
    def print_all(self):
        p = self.head
        res = []
        res.append(p.val)
        while p.next:
            p = p.next
            res.append(p.val)
        print(res)
	
    # 根据索引index获得节点
    def get(self, index):
        """
        获得第index个节点
        """
        # 头部节点
        p = self.head
        # 若链表为空或index小于0,则返回-1
        if not p or index < 0:
            return -1
        # 遍历链表找到第index个节点
        while index >= 1:
            # 若index大于链表长度
            if p.next == None:
                return -1
            else:
                p = p.next
            index -= 1   
        return p.val

    def addAtHead(self, val):
        """
        在头部添加节点
        """
        # 新建值为val的节点,next指向head节点的next,然后将此节点指定为head节点
        self.head = Node(val, self.head)
        self.length += 1

    def addAtTail(self, val):
        """
        在尾部添加节点
        """
        # 如果链表为空,则在头部添加节点
        if self.head == None:
            self.head = Node(val)
            self.length += 1
            return
        # 链表不为空,先获得头部节点
        p = self.head
        # 遍历节点,找到最后一个节点
        while p.next is not None:
            p = p.next
        # 最后一个节点指向新建节点
        p.next = Node(val)
        self.length += 1

    def addAtIndex(self, index, val):
        """
        在第index插入节点。若index等于链表长度,则在尾部添加,若大于节点长度,则节点不会被插入
        """
		# 若index等于链表长度,节点添加为尾部
        if index == self.length:
            self.addAtTail(val)
        # 若index==0,节点添加在头部
        elif index == 0:
            self.addAtHead(val)
        # 若index大于链表长度或为负,不插入节点
        elif index + 1 > self.length or index < 0:
            return
        else:
            p = self.head
            # 获得插入位置的前一个节点
            while index > 1:
                index -= 1
                p = p.next
            # 新建当前节点,next指向前一个节点的next
            cur = Node(val, p.next)
            # 前一个节点的next指向当前节点
            p.next = cur
            self.length += 1

    def deleteAtIndex(self, index):
        """
        删除第index个节点
        """
        # 若index大于链表长度或为负,不删除节点
        if index + 1 > self.length or index < 0:
            return
        # 若index==0,删除头部节点
        elif index == 0:
            # 重新指定头部节点为当前头部的next
            self.head = self.head.next
            self.length -= 1
            return
        else:
            p = self.head
            # 获得指定位置的前一个节点
            while index > 1:
                p = p.next
                index -= 1
            # 若前一个节点不为倒数第二个节点(也就是删除除头结点、尾节点以外的中间节点)
            if p.next.next is not None:
                p.next = p.next.next
            # 若指定位置的前一个节点为倒数第二个节点(也就是删除尾节点)
            else:
                p.next = None
            self.length -= 1

2、实战练习

(1)判断环形链表

此题为leetcode第141题
给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 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
        """
        '''
        # 方法一 双链表
        p1 = p2 = head
        while p2 and p2.next:
            p1 = p1.next
            p2 = p2.next.next
            if p1 == p2:
                return True
        return False
        '''
        ### 方法二  哈希表
        if head==None:
            return False
        hash_table=set()
        q=head
        while head:
            if head in hash_table:
                return True
            else:
                hash_table.add(head)  ##set 没有append 只有add
                head=head.next
        return False

(2)环形链表(二)

此题为leetcode第142题
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。为了表示给定链表中的环,我们使用整数 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 detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        p1 = p2 = head
        while p2 and p2.next:
            p1 = p1.next
            p2 = p2.next.next
            if p1 == p2:
                p = head
                while p != p1:
                    p = p.next
                    p1 = p1.next
                return p
        return None

(3)相交链表

此题为leetcode第160题
编写一个程序,找到两个单链表相交的起始节点。
注意:

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
# Definition for singly-linked list.
# 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
        """
        '''
        # 方法一 哈希表
        pa, pb = headA, headB
        table = set()
        while pa:
            if pa not in table:
                table.add(pa)
            pa = pa.next
        while pb:
            if pb in table:
                return pb
            pb = pb.next
        return None
        '''
        # 方法二 双指针
        pA = headA
        pB = headB
        while pA != pB:
            pA = pA.next if pA is not None else headB
            pB = pB.next if pB is not None else headA
        return pA

(4)删除链表的倒数第N个节点

此题为leetcode第19题
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        '''
        p = head
        length = 0
        table = []
        while p:
            table.append(p)
            length += 1
            p = p.next
        if n == 1:
            if length == 1:
                return None
            else:
                table[-n-1].next = None
                return head
        if n == length:
            return table[-n+1]
        table[-n-1].next = table[-n-1].next.next
        return head
        '''
        #构建双指针p1与p2,p1先走n步,然后一同运动,当p1指向表尾,p2指向的next即是倒数第N个节点,删除即可。
        dummy = ListNode(0)
        dummy.next = head
        
        fast = slow = dummy
        for i in range(n):
            fast = fast.next
        while fast.next:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return dummy.next

(5)反转链表

此题为leetcode第206题
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        # 递归
#         if not head or not head.next:
#             return head
    
#         res = self.reverseList(head.next)
#         head.next.next = head
#         head.next = None
#         return res
        # 非递归
        prev, cur = None, head
        while cur:
            cur.next, prev, cur = prev, cur, cur.next
        return prev

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值