数据结构(data-structure)和算法(algorithm)三

93 阅读10分钟

数据结构(data-structure)和算法(algorithm)三

线性数据结构

  • 线性数据结构:从表观上来看的话,这个就是一个进行线性排布的数据结构

  • 数据是一个十分抽象的概念,通过后期的逐渐发展,通过分类就出现了程序设计语言中的一些不同的数据类型

    • 基本数据类型或者数据结构
    • 数据元素之间不是独立的,其中是存在着特定的关系的,这个关系便是结构的概念
    • 数据结构指的就是对象中每个元素之间的关系
  • 不同的编程语言给我们提供了很多的内置的数据结构类型,这些呐都是被系统提前定义好了的

    • 这种数据结构呐,就是每一种编程语言内置的数据结构了:列表,元组,字典
  • 但是还有一些在程序设计语言中是没有内置设置好的语言

    • 这个时候就需要我们人为的对数据进行布局设计,这种自定义的数据结构,就是自定义数据结构

      • 栈,队列、链表、容器

线性表的分类

  • 在程序中,经常需要将一组数据元素作为整体进行管理和使用,需要进行创建这种元素组,来实现我们对数据的管理

    • 同时在一个元素组中,这些数据个数会发生变化(可以实现增删改查的操作)
  • 为了实现这种类似的需求,我们就可以实现的是将每一组的元素看作是一个序列,使用序列中每个元素的位置和顺序

    • 来实现表示实际应用中的某种具有具体意义的信息,从而实现表示数据之间的某种关系
  • 这样的组织的一些序列元素的话,就是我们的线性表

    • 一个线性表的话,实际上不仅仅是记录了某种元素的一个集合,还是一种记录了元素之间的顺序关系
  • 我们根据线性表的实际意义的存储方式,共分为了两种实现模型

    • 顺序表: 将元素顺序的存放在连续的内存存储区域中,元素的顺序关系由其存储顺序自动表示
    • 链表:将元素存放在通过链接构造起来的一系列存储块中,分为了数据域和指针域两个部分组成
  • 常见的线性表含有:

image-20241119054644729.png

线性表常见操作

对于线性表实现插入元素的两种情况

  • 一种是实现的是我们的在数据结构的末尾添加元素,这个时候的时间复杂度为: o(1)

  • 一种就是实现的是在我们的线性数据结构的首位添加元素,这个时候时间复杂度为: o(线性表长度) —— o(n)

  • 因为实际上的话,在一个线性表的首位添加元素的时候,实现的是后面的元素内存地址都会向后依次移动

    • 所以说这个时候的问题规模就是我们线性表的长度(注意针对的是相同的数据类型)

在一个线性表中取出元素/修改元素

  • 我们实现获取一个线性表的元素的话,就是通过的是元素的指定的元素下标,然后指向其内存地址实现的获取我们的元素的
  • 所以说这样一一对应的获取我们的元素的方式时间复杂度就是: o(1)

在一个线性表中删除元素

  • 最好的状态是直接删除末尾的元素,这个时候的时间复杂度是 o(1)

  • 但是最坏的情况是随机的在一个线性表中删除元素,最坏的是删除第一个元素

    • 因为实现删除的时候,我们删除元素后面的元素都会一个一个的向前移动一位,这个时候的时间复杂度就是: o(n)
    • 这样的情况就是我们的一个十分经典的易错题: 就是只是通过一个循环实现删除元素中的多个元素
    • 所以说我们对这种问题的解决就是通过我们的从后往前进行删除,才是正解
  • 对于我们的实际开发中的话,一个程序的开发是分为了很多的功能的

    • 如果说某几个功能用户的使用量是十分多的,那么性能方面就要做到最优,让用户能够在短时间内吃到更过服务器上的资源
    • 如果说某几个功能用户的使用量是十分少的,那么性能方面就要做到一般,让用户能够在短时间内吃到的资源更少
  • 比方说:

    • 微信具有的一些功能,我们常使用的是我们的聊天、视频、朋友圈之类的功能,那么这些功能尽量做到最优
    • 对于一些设置方面的功能的话,就可以适当的对优化操作缓一缓的
  • 反正的话,我们的一个程序在实现设计的时候一定需要根据实际情况来进行判断的

    • 不能是每个方面全部实现兼顾或者说全部不兼顾,这个就是程序的设计

算法程序设计扩容问题

  • 采用分离式结构的顺序表,若将数据区更换为存储空间更大的区域,就可以在不改变表对象的前提下对齐数据存储区进行扩容

  • 扩容数据结构表的基本的两种策略

    • 每次都进行增加固定的数目的存储位置

      • 节省空间,但是需要人扩容的次数较多,操作频繁
    • 每次扩容的策略以成倍的方式实现扩容

      • 减少了操作次数,但是浪费了空间资源,这个就是使用空间换时间的一种扩容方式了

Python 中的顺序表

Python 中的顺序表的话含有两种,一种就是list,一种就是元素tuple

tuple 是一种不可变的类型,即是说不变的顺序表,因此不支持使用动态的改变其内部的存储的方式

list 的底层实现的原理

其是一种元素个数可变的线性表,可以实现对元素的增删改查的操作

  • 基于下标的的高速的元素的访问以及更新机制,时间复杂度为 o(1)
  • 允许实现是任意元素进行加入,而且不断的加入元素的过程

链表

链表的底层的实现机制的话就是通过的是我们的数据域和指针域组成的,然后通过这个指针域指向下一个数据域

这样一个一个的进行连接起来的就是链表了

image-20241119192636027.png

链表实现保存的数据是一个十分零散杂乱的,但是实际上的话,基本上编程语言中实际上的话是不存在链表这一种内置的数据结构的

所以说这个是一种自定义的数据类型,其是由一个一个的节点构成的

一个链表的基本组成的话就是含有了两大部分的,一个就是数据域,一个就是指针域

那么我们该如何实现链表呐???

  • 首先先实现定义一个节点类型

  • 然后来进行定义链表

  • 链表的话是具有三种的: 单向链表、双向链表、循环链表

  • 在 C++ 种定义节点的方式含有两种,一种就是通过 class 来定义

    • 一种就是通过 结构体 来进行创建
  • 一个链表的话具有的功能是增删改查的操作

      • 添加多个元素
      • 添加一个元素
      • 根据元素的位置进行添加操作
      • 删除整个链表

      • 删除单个元素

        • 根据下标删除
        • 根据值删除
      • 根据下标修改元素
      • 根据值修改元素
      • 查询整个链表

      • 根据条件查询

        • 根据下标实现查询
# 先实现定义自己的一个一个的节点
class Node(object):
    def __init__(self, data) -> None:
        # 一个节点的数据域
        self.data = data
        # 一个节点的指针域,其存储的是下一个节点的内存地址
        self.next = None
​
​
class SingleLinkedList:
    # 在进行实例化的时候我们该有一个初始节点的,通过这个初始节点来实现寻找我们的链表,这个就是头节点了
    def __init__(self) -> None:
        self.__head = None
​
​
    # 获取链表长度
    def __get_length_link(self) -> int:
        """
        获取链表长度
        :return:
        """
        p = self.__head
        count = 1
        while p.next is not None:
            p = p.next  # 更新指向
            count += 1
        return count
​
​
    # 实现一个函数来保存数据
    def __save_data_in_list(self) -> list:
        """
        将链表元素组成一个列表返回
        :return:
        """
        data_list = []
        current = self.__head
        while current is not None:
            data_list.append(current.data)
            current = current.next
        return data_list
​
​
    # 添加单个节点操作
    def append_one_node(self, data) -> None:
        """
        一次性添加一个元素
        :param data: append element`s value
        :return: None
        """
        # 将元素转化为节点
        node = Node(data)
        # 判断头节点是否为空
        if self.__head is None:
            self.__head = node
​
        # 如果不为空,那就将节点挂载在有值的节点上面
        else:
            current = self.__head
            # 如果节点为空,那就直接实现节点的偏移
            while current.next:
                current = current.next
            current.next = node
​
​
    # 添加多个节点
    def append_many_node(self, list_data: list) -> None:
        """
        一次性插入多个元素
        :param list_data:
        :return:
        """
        # 直接遍历列表,实现循环添加单个节点
        for data in list_data:
            self.append_one_node(data)
​
​
    # 通过下标添加节点
    def append_node_by_index(self, index: int, data) -> None:
        """
        通过下标插入元素
        :param index:
        :param data:
        :return:
        """
        if index < 1:
            raise IndexError("Index must be greater than 0 \n")
        if index > self.__get_length_link():
            raise IndexError("index out of range!!!\n")
​
        current_node = self.__head
        node = Node(data)
​
        if index == 1:
            node.next = self.__head
            self.__head = node
        else:
            # 中间节点的插入
            current_index = 1  # 记录节点的下标
            # 实现偏移
            while current_node and current_index < index - 1:
                current_node = current_node.next
                current_index += 1
            # 插入新节点
            node.next = current_node.next
            current_node.next = node
​
​
    # 遍历显示所有的元素
    def show_link_data(self) -> None:
        """
        显示链表啊所有的元素
        :return:
        """
        if self.__head is None:
            print("链表为空")
        else:
            # 从头结点依次遍历整个链表元素
            current = self.__head
            while current is not None:
                print(current.data, end=" ")
                current = current.next
            print("\n")
​
​
    # 通过下标实现查找元素: 这里的使用列表实现的话,时间复杂度有是常数级别的查找元素
    def show_data_by_index(self, index: int) -> None:
        """
        通过下标显示元素
        :param index:
        :return:
        """
        # 或者说和随机插入元素的思路实现一样的,但是这里的话我是前面提前将数据添加入了一个列表中的
        if index > self.__get_length_link():
            raise IndexError("index out of range!!!")
        data_link_list = self.__save_data_in_list()
        print(f"第{index}个元素为: {data_link_list[index - 1]}\n")
​
​
    # 删除整个链表
    def clear_link(self) -> None:
        """
        清空链表
        :return:
        """
        self.__head = None
​
​
    # 根据下标删除元素
    def delete_link_by_index(self, index: int) -> None:
        """
        根据下标删除元素
        :param index:
        :return:
        """
        if index < 1:
            raise IndexError("Index must be greater than 0 \n")
        if index > self.__get_length_link():
            raise IndexError("index out of range!!!\n")
​
        if index == 1:
            self.__head = self.__head.next
            return
​
        current = self.__head
        current_index = 1
        while current and current_index < index - 1:
            current = current.next
            current_index += 1
        new_current = current.next
        current.next = current.next.next
        new_current.next = None
​
​
    # 根据值来进行删除元素
    def delete_link_by_value(self, value) -> None:
        """
        根据值来删除元素
        :param value:
        :return:
        """
        # 头节点直接改变头节点指向即可
        if self.__head.data == value:
            self.__head = self.__head.next
            return
​
        current = self.__head
        while current.next:
            if current.next.data == value:
                current.next = current.next.next
                return
            current = current.next
        if current.next is None:
            raise ValueError("value is not find")
​
​
    # 根据下标修改元素
    def set_link_by_index(self, index: int, data) -> None:
        """
        根据下标修改元素
        :param index:
        :param data:
        :return:
        """
        if index < 1:
            raise IndexError("Index must be greater than 0 \n")
        if index > self.__get_length_link():
            raise IndexError("index out of range!!!\n")
​
        if index == 1:
            self.__head.data = data
            return
​
        current = self.__head
        current_index = 1
        # 偏移指针
        while current and current_index < index:
            current_index += 1
            current = current.next
        current.data = data
​
​
    # 根据值来进行修改链表的值
    def set_link_by_value(self, value, data) -> None:
        """
        根据值来进行修改元素
        :param value: 指定需要用来查找的指标
        :param data: 需要进行替换的元素
        :return:
        """
        current = self.__head
        if current.data == value:
            current.data = data
            return
​
        while current.next:
            if current.next.data == value:
                current.next.data = data
                return
            current = current.next
​
        if current.next is None:
            raise ValueError("value is not find!!!")