python进阶篇-day08-数据结构与算法(线性结构介绍与链表实现)

news2024/9/17 3:18:17

数据的存储和组织形式

程序 = 数据结构 + 算法

一. 算法介绍

概述目的

都是可以提高程序的效率(性能), 面试高频考点

数据结构介绍

数据的存储和组织形式, 同样的空间, 不同的结构, 存储的数据不同, 操作方式也不同

算法介绍

为了解决实际的业务问题, 而考虑出来的方法和思路 => 算法

算法具有独立性, 即: 他是解决问题的思路和方法, 不依赖于语言, 用C能实现, 用Java能实现, 用python也能实现

算法5大特性

  1. 有输入 算法具有0个或者多个输入

  2. 有输出 算法至少有1个或多个输出

  3. 有穷性 算法在有限的步骤之后会自动结束而不会死循环, 并且每个步骤都在可接受的时间内完成

  4. 确定性 算法中的每一步都具有确定的含义, 不会出现二义性

  5. 可执行 算法的每一步都是可行的, 执行有限的次数完成

案例

经典案例: a + b + c = 1000, a^2 + b^2 = c^2

方式1: 穷举法, 155秒

import time
​
start = time.time()
for a in range(1001):
    for b in range(1001):
        for c in range(1001):
            if a ** 2 + b ** 2 == c ** 2 and a + b + c == 1000:
                print(a, b, c)
end = time.time()
print(f'执行了: {end - start}')

方式2: 穷举法, 88.9秒

import time
​
start = time.time()
for a in range(1001):
    for b in range(1001):
        for c in range(1001):
            if a + b + c == 1000 and a ** 2 + b ** 2 == c ** 2:
                print(a, b, c)
end = time.time()
print(f'执行了: {end - start}')

方式3: 代入法(从a, b可得c, 减少一层循环), 0.236秒

import time
​
start = time.time()
for a in range(1001):
    for b in range(1001):
        c = 1000 - a - b
        if a ** 2 + b ** 2 == c ** 2:
            print(a, b, c)
end = time.time()
print(f'执行了: {end - start}')

衡量算法的优略

不能单纯的只依靠程序的执行时间, 因为程序执行时间也会收到机器, 硬件, 其他硬件的影响.

所以可以假定 计算机执行每个步骤的时间都是固定的, 只考虑算法的 执行步骤即可.

代码执行总时间(T) = 操作步骤数量 * 操作步骤执行时间

时间复杂度

时间复杂度T 是关于n的函数

概述

可以反应一个算法的优略, 他表示一个算法随着问题规模的变化而表现出来的主要趋势.

大O标记法

忽略次要条件, 只考虑主要条件(随着问题规模变化而变化的内容), 就会得到: 大O标记法, 标记的效率

例如: 4中 方式1穷举法 的时间复杂度为 : O(n³)

例如: 4中 方式3代入法 的时间复杂度为 : O(n²)

时间复杂度计算

  1. 基本操作: O(1) => 固定步骤

  2. 顺序结构: 加法

  3. 循环结构: 乘法

  4. 分支结构: 取最大项

  5. 只考虑主要条件, 不考虑次要条件

细节

  1. 如非特别说明, 考虑时间复杂度都是考虑: 最坏时间复杂度, 它算是算法的一种保证

  2. 常见的时间复杂度, 效率从高到低分别是:

    O(1) > O(logn) > O(n) > O(nlogn) > O(n²) > O(n³)

空间复杂度(了解)

  1. 空间复杂度指的是: 算法的运算过程中, 临时占用的空间大小, 也用大O标记法标记, 具体如下:

    O(1) < O(logn) < O(n) < O(n²) < O(n³)

  2. 开发思想: 时空转换, 即: 用空间换空间 / 用空间换时间

  3. 数据结构是算法的载体

二. 数据结构

线性结构

特点:每个节点都只能有1个子节点(后继节点) 和 1个父节点(前驱节点)

代表:列表, 栈, 列表, 链表...

顺序表

  1. 概述: 属于线性结构的一种, 例如: 栈, 队列都属于顺序表

  2. 特点: 所有元素在内存中以 连续 的内存空间 来存

分类

一体式存储: 地址和数据一起存储

分离式存储: 地址和数据分离存储

扩容策略
  1. 每次扩容增加固定的条数目, 拿时间环空间

  2. 每次扩容容量翻倍, 拿空间换时间

细节:

顺序表主要存储 数据区 和信息区, 数据区和信息区在一起的叫: 一体式存储, 分开存储 的叫: 分离式存储

顺序表增删

方式1: 末尾增删: 时间复杂度: O(1)

方式2: 中间增删(非保序) 时间复杂度: O(1)

方式3: 中间增删(保序) 时间复杂度: O(n)

链表

  1. 概述: 他是由节点组成, 每个节点由 数值域地址域 组成

  2. 特点: 所有元素在内存中以 非连续 的内存空间来存储, 即: 有地就行

链表介绍
  1. 概述: 属于线性结构, 即: 每个节点都只能有1个子节点(后继节点) 和 1个父节点(前驱节点)

    链表可以看作是 用链条把所有节点连接起来的 一种结构

  2. 节点概述: 由元素域(数值域) 和地址域(连接域)组成, 其中 数值域存储的是 数值, 地址域存储的是下个节点的地址

分类(根据节点)

  1. 单向链表:

    每个节点由1个数值域和1个地址域组成, 且每个节点的地址域指向下个节点的地址, 最后1个节点的地址域为None

  2. 单项循环链表:

    每个节点由1个数值域和1个地址域组成, 且每个节点的地址域指向下个节点的地址, 最后1个节点的地址域为: 第1个节点的地址

  3. 双向链表

    每个节点由1个数值域和2个地址域组成, 且每个节点的地址域分别指向前1个节点的地址 和 后1个节点的地址.

    第1个节点的(前)地址域为: None, 最后1个节点的(后)地址域为: None

  4. 双项循环链表

    每个节点由1个数值域和2个地址域组成, 且每个节点的地址域分别指向前1个节点的地址 和 后1个节点的地址.

    第1个节点的(前)地址域为: 最后1个节点的地址,

    最后1个节点的(后)地址域为: 第1个节点的地址

单链表演示
分析

节点类: Single Node

属性:

item 表示: 数值域

next 表示: 地址域, 即: 指向下个节点的地址

链表类: SingleLinkedList

属性:

head 表示: 头节点, 即: 默认指向链表第一个节点的地址

行为:

is_empty(self)链表是否为空

length(self)链表长度

travel(self) 遍历整个链表

add(self,item)链表头部添加元素

append(self,item)链表尾部添加元素

insert(self,pos,item)指定位置添加元素

remove(self,item)删除节点

search(self,item)查找节点是否存在

代码
# 定义节点类
class SingleNode(object):
    # 初始化节点属性
    def __init__(self, item):
        self.item = item
        self.next = None
​
​
# 定义链表类
class SingleLinkedList(object):
    # 初始化链表属性
    def __init__(self, head=None):
        self.head = head
​
    # 判断链表是否为空
    def is_empty(self):
        return self.head is None
​
    # 链表长度
    def length(self):
        # 定义计数器
        count = 0
        # 定义当前节点, 初值指向头节点
        cur = self.head
        # 判断当前节点是否为空, 不为空遍历
        while cur is not None:
            # 计数器加1
            count += 1
            # 设置当前节点指向下个节点
            cur = cur.next
        # 返回链表长度计数器
        return count
​
    # 遍历链表
    def travel(self):
        # 定义当前节点, 初值指向头节点
        cur = self.head
        # 判断当前节点是否为空, 不为空遍历
        while cur is not None:
            print(cur.item)
            # 设置当前节点指向下个节点
            cur = cur.next
​
    # 头插法
    def add(self, item):
        # 将要添加的数据封装成节点
        new_node = SingleNode(item)
        # 设置新节点的地址域为头节点
        new_node.next = self.head
        # 将头节点为新节点
        self.head = new_node
​
    # 尾插
    def append(self, item):
        # 将要添加的节点封装成节点
        new_node = SingleNode(item)
        # 判断当前头节点是否为空
        if self.head is None:
            # 为空直接将头节点设置为新节点
            self.head = new_node
        else:
            # 不为空则遍历链表
            cur = self.head
            # 判断当前节点的地址域是否不为空
            while cur.next is not None:
                # 这里是当前节点的地址域不为空, 继续遍历
                cur = cur.next
            # 这里找到最后节点, 将最后节点的地址域设置为新节点
            cur.next = new_node
​
    # 插入到指定位置(索引从0开始)
    def insert(self, pos, item):
        # 判断插入的位置是否合法
        if pos <= 0:
            # 小于0, 则在头部插入
            self.add(item)
        elif pos >= self.length():
            # 大于链表长度, 则在尾部插入
            self.append(item)
        else:
            # 定义计数器
            count = 0
            # 定义存储当前的节点
            cur = self.head
            # 判断是否为要插入位置的前一个节点
            while count < pos - 1:
                cur = cur.next
                count += 1
            # 具体插入动作
            new_node = SingleNode(item)
            # 顺序不能变
            new_node.next = cur.next
            cur.next = new_node
​
    # 删除节点
    def remove(self, item):
        # 当前节点
        cur = self.head
        # 当前节点的前一个节点
        pre = None
        while cur is not None:
            if cur.item == item:
                # 被删除的为头节点
                if cur == self.head:
                    # 将头节点设为当前节点地址域
                    self.head = cur.next
                else:
                    # 设置当前的前一个节点为当前节点的地址域
                    pre.next = cur.next
                break
            else:
                # 将当前节点设为前一个节点
                pre = cur
                # 将当前节点的地址域设为当前节点
                cur = cur.next
​
    # 查询元素
    def search(self, item):
        # 定义变量存储当前节点
        cur = self.head
        # 遍历
        while cur is not None:
            if cur.item == item:
                return True
            else:
                cur = cur.next
        return False
​
​
if __name__ == '__main__':
    # 测试节点类
    node1 = SingleNode('张三')
    print(node1)
    print(node1.item)
    print(node1.next)
    print('-' * 21)
​
    # 测试创建空链表
    linkedlist1 = SingleLinkedList()
    print(linkedlist1.head)
    print('-' * 21)
​
    # 测试创建链表, 头节点指向node1节点
    linkedlist2 = SingleLinkedList(node1)
    print(linkedlist2.head)
    print(linkedlist2.head.item)
    print(linkedlist2.head.next)
    print('-' * 21)
​
    # 测试头添加
    linkedlist2.add('李四')
    linkedlist2.add('王五')
​
    # 测试尾部添加
    linkedlist2.append('麻子')
    linkedlist2.append('孙二')
​
    # 添加到指定位置
    linkedlist2.insert(-10, '-10')
    linkedlist2.insert(200, '200')
    linkedlist2.insert(2, '2')
​
    # 删除元素
    linkedlist2.remove('-10')
    linkedlist2.remove('2')
    linkedlist2.remove('200')
​
    # 查找
    print('查找')
    print(linkedlist2.search('-2'))
    print(linkedlist2.search('麻子'))
    print('-' * 21)
​
    # 测试是否为空
    print('空')
    print(linkedlist1.is_empty())
    print(linkedlist2.is_empty())
    print('-' * 21)
​
    # 链表长度
    print('长度')
    print(linkedlist1.length())
    print(linkedlist2.length())
    print('-' * 21)
​
    # 遍历链表
    print('遍历')
    print('')
    linkedlist1.travel()
    linkedlist2.travel()
    # print('-' * 21)
​

非线性结构

特点: 每个节点都可以有n个子节点(后继节点) 和 n个父节点(前驱节点)

代表: 树, 图......

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2117063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

龙芯+FreeRTOS+LVGL实战笔记(新)——06添加二级按钮

本专栏是笔者另一个专栏《龙芯+RT-Thread+LVGL实战笔记》的姊妹篇,主要的区别在于实时操作系统的不同,章节的安排和任务的推进保持一致,并对源码做了完善与优化,各位可以先到本人主页下去浏览另一专栏的博客列表(目前已撰写36篇,图1所示),再决定是否订阅。此外,也可以…

超强的截图工具:PixPin

你是否还在为寻找一款功能强大、操作简便的截图工具而烦恼&#xff1f;市面上那么多工具&#xff0c;常常让人无从选择。今天&#xff0c;想给大家安利一款神器——PixPin&#xff0c;一款真正解放双手的截图工具。 想象一下&#xff0c;你只需要按下快捷键就能轻松完成多种截…

雷电9模拟器安装magisk和lsposed

模拟器环境配置 1、开启root 2、开启System.vmdk可写入 安装magisk 1、新建模拟器、开启root权限、并安装debug版magisk 下载地址去上面吾爱论坛作者文章下载吧&#xff01;支持他一下&#xff01; 2、打开magisk的app&#xff0c;点击安装 如果弹出获取权限&#xff0c;直接…

【Socket网络编程原理实践】

socket 基于 TCP/IP协议实现&#xff0c;在网络模型中属于传输层 Java 网络编程中的核心概念 IP 地址&#xff1a;用于标识网络中的计算机端口号&#xff1a;用于标识计算机上的应用程序或进程Socket&#xff08;套接字&#xff09;&#xff1a;网络通信的基本单位&#xff0…

冒泡排序算法介绍

冒泡排序算法介绍 如果真的累了&#xff0c;就拉上窗帘关上手机关掉闹钟深呼吸一口气钻进被窝&#xff0c;好好地睡一觉&#xff0c;难熬的日子总需要一些温暖&#xff0c;而什么都不如被窝的温暖来的踏实。 冒泡排序是一种经典的排序算法&#xff0c;它通过重复遍历待排序的序…

如何恢复回收站中已删除/清空的文件

回收站清空后如何恢复已删除的文件&#xff1f;是否可以恢复永久删除的文件&#xff1f;或者最糟糕的是&#xff0c;如果文件直接被删除怎么办&#xff1f;本文将向您展示清空回收站后恢复已删除数据的最佳方法。 回收站清空后如何恢复已删除的文件&#xff1f; “回收站清空后…

从零开始搭建GPU深度学习环境(pytorch)

傻乎乎的我&#xff0c;突然发现我自己的笔记本电脑居然有gpu&#xff0c;这个电脑是我弟在2017年购入的。 电脑已经按照了cpu环境&#xff0c;现在增加gpu环境 参考torch的cpu版本和gpu版本有什么区别 torch与cuda版本_mob64ca13f6035c的技术博客_51CTO博客 前言&#xff1a…

Vue3使用Uni-ui的popup弹出层组件

由于uni-ui中有些组件文档的基于vue2编写的&#xff0c;比如popup组件 下面是vue3的写法 除了文档中要求的aleterDialog外&#xff0c;还得利用v-if设置一个isDialog判断 // template // script 解决

数学建模笔记——TOPSIS[优劣解距离]法

数学建模笔记——TOPSIS[优劣解距离法] TOPSIS(优劣解距离)法1. 基本概念2. 模型原理3. 基本步骤4. 典型例题4.1 矩阵正向化4.2 正向矩阵标准化4.3 计算得分并归一化4.4 python代码实现 TOPSIS(优劣解距离)法 1. 基本概念 C. L.Hwang和 K.Yoon于1981年首次提出 TOPSIS(Techni…

【Linux网络】详解TCP协议(1)

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux网络 &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 TCP协议 的相关内容。 如果看到最后您觉得这篇文章写得不错&am…

力扣每日一题 有序数组的平方 双指针 逆向思维

Problem: 977. 有序数组的平方 &#x1f468;‍&#x1f3eb; 灵神题解 class Solution {public int[] sortedSquares(int[] nums) {int n nums.length;int [] ans new int[n];int p n-1;int i 0;int j n-1;while(p > 0){int x nums[i] * nums[i];int y nums[j] * n…

结构体小知识

目录 前言1.结构体数组1.1结构体数组理解1.2结构体数组知识运用1.3 -> 操作符 2. 知识拓展 前言 本期blog是对上一期指针知识的知识补充&#xff0c;如果各位大佬感兴趣的话&#xff0c;可以结合起来一起看&#xff01; 1.结构体数组 1.1结构体数组理解 结构体数组在本…

关系的规范化与范式详解

在数据库设计中&#xff0c;关系的规范化是确保数据结构合理性、减少冗余和异常的关键步骤。如果你是一个数据库设计的初学者&#xff0c;这篇文章将为你深入浅出地讲解 关系规范化 和 范式 的核心概念&#xff0c;并通过简洁的示例帮助你加深理解。 关系的规范化&#xff1a;…

JavaScript进阶day1

目录 1.作用域 1.1 局部作用域 1.2 全局作用域 1.3 作用域链 1.4 JS垃圾回收机制 1.4.1 什么是垃圾回收机制&#xff1f; 1.4.2 内存的生命周期 1.4.3 算法说明 1.5 闭包 1.6 变量提升 2.函数进阶 2.1 函数提升 2.2 函数参数 2.2.1 动态参数 2.2.2 剩余参数 2.…

GB2312编码(加2020H、8080H原理)

区位码、内码、国标码 转换及原理 背景答题思考相关资料 背景 问题: 某汉字的国标码为5650H&#xff0c;那么它的机内码为&#xff08; B &#xff09;。A E6E0H B D6D0H C C6C0H D 8080H答题 思考 为什么要加上2020H和8080H&#xff1f;区位码、内码、国标码怎么转换非常简单…

【硬件知识】关于RAM的“那些事”

文章目录 一、DRAM&#xff08;动态随机存取存储器&#xff09;二、SRAM&#xff08;静态随机存取存储器&#xff09;三、DRAM和SRAM的差异与区别 一、DRAM&#xff08;动态随机存取存储器&#xff09; 工作原理&#xff1a;DRAM使用电容来存储数据。每一位数据通过一个电容和…

【深度学习讲解笔记】第1章-机器学习基础

1.机器学习是什么 机器学习&#xff08;Machine Learning&#xff0c;ML&#xff09;&#xff0c;顾名思义就是让机器学会做一件事情&#xff0c;比如语音识别&#xff0c;机器听一段声音&#xff0c;产生这段声音对应的文字。或是识别图片中有几个人&#xff0c;几辆车。这些…

2024年语音识别转文字工具的崛起

无论是繁忙的会议记录、远程教学的即时笔记&#xff0c;还是日常生活的语音备忘&#xff0c;只需轻轻一说&#xff0c;便能瞬间转化为清晰可编辑的文字&#xff0c;这种便捷与高效无疑为现代生活增添了无限可能。本文将带你深入探索语音识别转文字工具的奥秘。 1.365在线转文字…

【Python篇】matplotlib超详细教程-由入门到精通(上篇)

文章目录 第一部分&#xff1a;基础概念与简单绘图1.1 matplotlib 简介1.2 创建第一个折线图1.3 图表的基本组成元素 第二部分&#xff1a;图表样式与修饰2.1 修改图表样式2.2 添加图例2.3 调整坐标轴与刻度 第三部分&#xff1a;绘制不同类型的图表3.1 散点图 (Scatter Plot)3…

使用 Homebrew 在 macOS 上安装 Conda

Homebrew 是一个流行的 macOS 包管理器&#xff0c;可以帮助你安装和管理各种软件包。 以下是使用 Homebrew 安装 Conda 的步骤&#xff1a; 1. 安装 Homebrew 如果你还没有安装 Homebrew&#xff0c;可以通过以下命令安装&#xff1a; /bin/bash -c "$(curl -fsSL htt…