数组、链表、跳表

590 阅读4分钟

数组

概念

  • 是一种线性表 数据结构
  • 内存中连续的存储区域 内存管理器直接访问 访问第0个元素 和 第N个元素的时间复杂度是一样的
  • 所以复杂度O(1)
  • 存储相同类型的数据

复杂度

  • 查找时间复杂度平均O(1)
  • 插入时间复杂度平均O(n)

插入操作

image.png 数组插入数据的时候 需要将index之后的元素 全部向后顺移一个位置 平均要移动一半的元素 是一个O(n)复杂度

删除的时候 将对应index的元素清除 index后面的元素 全部向前顺移一个位置

数据组复杂度

image.png

链表

插入 修改数据频繁的情况 数组不好用

代码实现 image.png

概念

  • 内存地址非连续的数据结构 结点的逻辑顺序是通过指针链接实现的
  • 单向链表
  • 双向链表
  • 循环链表
  • 静态链表

增加节点的操作 复杂度O(1) image.png

删除节点操作 复杂度O(1) image.png

读取节点操作 复杂度O(n)

链表的复杂度

image.png

单向链表

仅支持单方向,每个结点包括两部分:

  • 只有一个next指针(指向下一个结点)
  • 存储的数据
  • 头是 head
  • 尾是 tail
  • 最后一个tail的next指针是空

复杂度

  • 插入/删除O(1)
  • 查询O(n)

双向链表

支持双方向,每个结点包括3部分

  • next指针 指向下一个结点
  • prev指针 指向上一个结点
  • 存储数据

复杂度

  • 插入/删除O(1)
  • 查询O(n)

循环链表

定义:尾结点 指向 头结点;可以是单向或双向 tail的next指针指向head 优点: 链尾到链头方便;约瑟夫问题

静态链表

基本概念:

  • 用数组描述的链表
  • data区域 存放数据
  • cur区域(游标) 存放后继在数组中的下标

跳表

  • 必须是有序的链表 元素必须是有序的
  • 跳表对标得是平衡树和二分查找
  • 时间复杂度:插入、删除、搜索都是O(log n)
  • 优点: 原理简单、容易实现、方便扩展 常见的项目有 Redis、LevelDB
  • 缺点: 索引的维护成本高 每次增加/删除的时候 都要更新索引数据

一维数据想要加速时 可以升维

第一级索引 在原始链表的基础上 next指向的是 下下个节点

image.png 在第一级索引的基础上 next指向的是 下下个节点(二级索引节点)

一次类推 可以加多级索引

相关学习文档

www.jianshu.com/p/b1ab4a170… <LRU缓存算法>

leetcode-cn.com/problems/Ir…

redisbook.readthedocs.io/en/latest/i… <跳跃表>

www.geeksforgeeks.org/implementin… <Linked List 的标准实现代码>

www.zhihu.com/question/20… <为啥 redis 使用跳表(skiplist)而不是使用 red-black?>

练习题

1.从尾到头打印单链表 2.单链表实现约瑟夫环(JosephCircle) 3.逆置/反转单链表 4.K个节点为一组进行翻转 5.返回链表中间(1/2)节点(扩展返回链表1/K节点) 6.单链表排序(冒泡排序&快速排序) 7.查找单链表的中间节点,要求只能遍历一次链表 8.查找单链表的倒数第K个节点,要求只能遍历一次链表 9.删除链表的倒数第K个结点 10.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度 11.判断两个链表是否相交,若相交,求交点(假设链表不带环) 12.判断两个链表是否相交,若相交,求交点(假设链表可能带环) 13.求两个已排序单链表中相同的数据 14.合并两个有序链表,合并后依然有序

  1. leetcode.com/problems/re…
  2. leetcode.com/problems/sw…
  3. leetcode.com/problems/li…
  4. leetcode.com/problems/li…
  5. leetcode.com/problems/re…

python实现单向链表

参考连接juejin.im/post/684490…

# -*- coding: utf-8 -*-

# @File    : link_list.py
# @Date    : 2020-08-17
# @Author  : 


class Element(object):
    """
    元素类(叶子结点)
    """

    def __init__(self, value):
        self.value = value
        self.next = None

class LinkedList(object):
    """
    链表类
    """

    def __init__(self, head):
        self.head = head
        self.length = 0


    def append(self, new_node):
        """
        在链表的后面增加一个元素
        """

        # 指针从头开始 当前的头节点
        pointer = self.head

        if self.head:
            # 遍历 拿到最后一个节点
            while pointer.next:
                # 指针指向最后的尾节点
                pointer = pointer.next
            pointer.next = new_node
        else:
            self.head = new_node

        self.length += 1

    def get_position(self, index):
        """
        根据下标取数值
        第一个数值的下标为0
        :param index:
        :return:
        """
        # 异常判断: 如果下标小于0 或者大于链表长度 直接返回
        if index > self.length or index < 0:
            return None

        # 定义一个指针
        pointer = self.head
        # 定义起始查找的下标
        start_index = 0

        # 循环遍历链表 直到 start_index == index
        while pointer and start_index < index:
            pointer = pointer.next
            start_index += 1

        return pointer

    def insert(self, index, new_node):
        """
        向指定下标插入节点
        :param index:
        :param new_node:
        :return:
        """
        start_index = 0
        pointer = self.head

        if index < 0 or index > self.length:
            print "Index Error"
            return None

        if index == 0:
            new_node.next = self.head
            self.head = new_node
            return

        while pointer and start_index < index:

            if start_index == index -1 :
                new_node.next = pointer.next
                pointer.next = new_node
                return

            start_index += 1
            pointer = pointer.next


def main():
    innodb = LinkedList(head=Element(1))
    innodb.append(Element(3))
    innodb.append(Element(5))

    innodb.insert(6, Element("xx"))


if __name__ == '__main__':
    main()