链表的原理和实现python

为什么需要链表

数组的底层原理是顺序存储,是一块连续的内存空间,有了这块内存空间的首地址,就能直接通过索引计算出任意位置的元素地址。

链表不一样,一条链表并不需要一整块连续的内存空间存储元素。链表的元素可以分散在内存空间的天涯海角,通过每个节点上的next, prev 指针,将零散的内存块串联起来形成一个链式结构。

这样做的好处很明显,首先就是可以提高内存的利用效率,链表的节点不需要挨在一起,给点内存 new 出来一个节点就能用,操作系统会觉得这娃好养活。

可以抽象为一条弯曲的链子,通过一个个环连接在一起。

另外一个好处,它的节点要用的时候就能接上,不用的时候拆掉就行了,从来不需要考虑扩缩容和数据搬移的问题,理论上讲,链表是没有容量限制的(除非把所有内存都占满,这不太可能)。

当然,不可能只有好处没有局限性。数组最大的优势是支持通过索引快速访问元素,而链表就不支持

这个不难理解吧,因为元素并不是紧挨着的,所以如果你想要访问第 3 个链表元素,你就只能从头结点开始往顺着 next 指针往后找,直到找到第 3 个节点才行。

单链表

链表的创建

class Listnode:
    def __init__(self, x):
        self.val = x
        self.next = None


def create_linked_list(nums):
    head = Listnode(nums[0])
    cur = head
    for i in range(1, len(nums)):
        cur.next = Listnode(nums[i])
        cur = cur.next
    return head


def print_linked_list(current_node):
    while current_node:
        print(current_node.val, end='->')
        current_node = current_node.next
    print('None')


if __name__ == '__main__':
    nums = [1, 2, 3, 4, 5]
    linked_list = create_linked_list(nums)
    print_linked_list(linked_list)

最终的输出结果:1->2->3->4->5->None

链表的实现

class ListNode:
    def __init__(self, val):
        self.val = val
        self.next = None


class MyLinkedList(object):

    def __init__(self):
        self.size = 0
        self.head = ListNode(0)

    def get(self, index):
        """
        从链表中获取指定位置的元素值。
        :param index: 要获取元素的位置索引,从0开始计数。
        :type index: int
        :return: 如果索引有效,则返回对应位置的元素值;否则返回-1。
        :rtype: int
        """
        # 检查索引是否超出链表范围
        if index < 0 or index >= self.size:
            return -1
        cur = self.head
        # 遍历链表至指定索引位置
        for _ in range(index + 1):
            cur = cur.next
        return cur.val  # 返回指定位置的元素值

    def addAtHead(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.addAtIndex(0, val)

    def addAtTail(self, val):
        """
        :type val: int
        :rtype: None
        """
        self.addAtIndex(self.size, val)

    def addAtIndex(self, index, val):
        """
        在指定索引处插入一个新节点。

        :param index: 要插入节点的索引位置。如果索引大于当前链表长度,则不做任何操作;
                      如果索引小于0,则将节点插入到链表开头。
        :param val: 要插入的节点的值。
        :return: None
        """
        if index > self.size:  # 如果索引大于链表长度,则直接返回,不做任何操作
            return
        if index < 0:  # 如果索引小于0,将节点插入到链表开头
            index = 0
        self.size += 1  # 更新链表大小
        pre = self.head
        # 定位到要插入节点的前一个节点
        for _ in range(index):
            pre = pre.next  # 逐个遍历,找到正确的位置
        to_add = ListNode(val)  # 创建新节点
        to_add.next = pre.next  # 新节点指向pre节点的next节点
        pre.next = to_add  # pre的next指向新节点

    def deleteAtIndex(self, index):
        """
        从链表中删除指定索引的元素。

        :param index: 要删除的元素的索引。如果索引无效(小于0或大于等于链表长度),则不进行任何操作。
        :type index: int
        :return: None
        """
        if index < 0 or index >= self.size:
            return
        self.size -= 1
        pre = self.head
        # 移动到要删除节点的前一个节点
        for _ in range(index):
            pre = pre.next
        # 要删除节点的前一个节点next指向删除节点的后一个节点
        pre.next = pre.next.next

双链表

链表的创建

class ListNode:
    def __init__(self, val):
        self.val = val
        self.next = None
        self.prev = None


def create_double_linked_list(arr):
    if not arr:
        return None
    head = ListNode(arr[0])
    cur = head
    for i in range(1, len(arr)):
        node = ListNode(arr[i])
        cur.next = node
        node.prev = cur
        cur = cur.next
    return head


def print_double_linked_list(head):
    while head:
        print(head.val, end='->')
        head = head.next


if __name__ == '__main__':
    head = create_double_linked_list([1, 2, 3, 4, 5])
    print_double_linked_list(head)

链表的实现

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
        self.prev = None


class MyLinkedList:

    def __init__(self):
        self.size = 0
        self.head, self.tail = ListNode(0), ListNode(0)
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, index: int) -> int:
        """
        从链表中获取指定位置的元素值。
        """
        # 检查索引是否超出链表范围
        if index < 0 or index >= self.size:
            return -1
        # 选择从头结点还是尾结点开始遍历取决于索引的位置,以优化遍历效率
        # 以索引值作为分割,左边短就从头结点开始遍历,右边短就从尾结点开始遍历
        if index + 1 < self.size - index:
            curr = self.head
            # 遍历链表到达指定索引位置
            for _ in range(index + 1):
                curr = curr.next
        else:
            curr = self.tail
            # 遍历链表到达指定索引位置
            for _ in range(self.size - index):
                curr = curr.prev
        # 遍历链表到达指定索引位置
        return curr.val

    def addAtHead(self, val: int) -> None:
        self.addAtIndex(0, val)

    def addAtTail(self, val: int) -> None:
        self.addAtIndex(self.size, val)

    def addAtIndex(self, index: int, val: int) -> None:
        """
        在双向链表的指定位置插入一个新节点,并保持双向链表的有序。
        """
        # 处理index大于链表长度的情况,直接返回不进行操作
        if index > self.size:
            return
        # 将负索引调整为0
        if index < 0:
            index = 0

        # 确定插入位置的前驱节点和后继节点
        if index < self.size - index:
            # 当index在链表前半部分时,正常计算前驱节点和后继节点
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next
        else:
            # 当index在链表后半部分时,从尾部向前计算前驱节点和后继节点
            succ = self.tail
            for _ in range(self.size - index):
                succ = succ.prev
            pred = succ.prev

        # 更新链表大小
        self.size += 1
        # 创建新节点并插入到链表中
        to_add = ListNode(val)
        to_add.next = succ
        to_add.prev = pred
        pred.next = to_add
        succ.prev = to_add

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size:
            return
        if index < self.size - index:
            # 当index在链表前半部分时,正常计算前驱节点和后继节点
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next.next
        else:
            # 当index在链表后半部分时,从尾部向前计算前驱节点和后继节点
            succ = self.tail
            for _ in range(self.size - index - 1):
                succ = succ.prev
            pred = succ.prev.prev
        self.size -= 1
        pred.next = succ
        succ.prev = pred

相关推荐

  1. 原理实现python

    2024-05-09 08:12:09       34 阅读
  2. python实现循环双向

    2024-05-09 08:12:09       34 阅读
  3. 【数据结构算法】简单实现

    2024-05-09 08:12:09       64 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-09 08:12:09       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-09 08:12:09       101 阅读
  3. 在Django里面运行非项目文件

    2024-05-09 08:12:09       82 阅读
  4. Python语言-面向对象

    2024-05-09 08:12:09       91 阅读

热门阅读

  1. 在Linux中,用户的软件一般安装在哪里比较好

    2024-05-09 08:12:09       34 阅读
  2. 【算法】用存入下标的方法来巧解单调队列

    2024-05-09 08:12:09       38 阅读
  3. 【linux】隐藏文件,vim 或 gedit 打开隐藏文件

    2024-05-09 08:12:09       30 阅读
  4. 探索九型人格测试API接口:神奇之处暗藏何处?

    2024-05-09 08:12:09       27 阅读
  5. Amazon IoT 服务的组件

    2024-05-09 08:12:09       33 阅读
  6. docker安装部署FastGPT

    2024-05-09 08:12:09       29 阅读
  7. selenium 同样的class如何选择第二个

    2024-05-09 08:12:09       32 阅读