python(4)

目录

 

第十六章 数据结构

16.1 数据结构

16.1.1数组

16.1.2链表

16.1.3堆栈

16.1.4队列

16.1.5树

16.1.6哈希表

16.2算法

16.2.1算法概述

16.2.2时间复杂度

16.2.3空间复杂度

16.3查找

16.3.1顺序查找

16.3.2折半查找

16.4排序

16.4.1冒泡排序

16.4.2选择排序

16.4.3插入排序

16.4.4希尔排序

16.4.5快速排序

16.4.6归并排序

第17章 线程

17.1线程与进程的关系

17.2多线程

17.2.1计算密集与IO密集

17.2.2线程的创建

17.2.3线程相关操作

17.3线程同步

17.3.1并发修改的问题

17.3.2线程锁

17.3.3死锁

17.4线程队列

第18章 进程

18.1进程的创建(Process类)

18.2进程相关的操作

18.3进程的创建(fork方法)

18.4进程队列

第19章 网络编程

19.1网络基础

19.1.1计算机网络

19.1.2计算机通讯协议

19.1.3IP地址与端口号

19.2URL

19.2.1基本组成

19.2.2解析URL

19.2.3发起请求

19.3TCP

19.3.1UDP

19.3.2协程


第十六章 数据结构

16.1. 数据结构

16.1.1. 数组

16.1.2. 链表

16.1.3. 堆栈

16.1.4. 队列

16.1.5. 树

16.1.6. 哈希表

16.2. 算法

16.2.1. 算法概述

16.2.2. 时间复杂度

16.2.3. 空间复杂度

16.3. 查找

16.3.1. 顺序查找

16.3.2. 折半查找

16.4. 排序

16.4.1. 冒泡排序

16.4.2. 选择排序

16.4.3. 插入排序

16.4.4. 希尔排序

16.4.5. 快速排序

16.4.6. 归并排序

 

16.1 数据结构

数据,指能够输入计算机中,由计算机所处理的元素。结构,指数据之间的关系。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。其表现为计算机存储,组织与处理数据的方式。

常用的数据结构有:

  • 数组
  • 链表
  • 堆栈
  • 队列
  • 哈希表

说明:堆栈、队列、链表为线性存储结构(线性表),即多个元素的有序序列。堆栈,队列为操作受限的线性表。

16.1.1数组

数组采用的是连续存储的方式。通过其实地址与偏移量进行访问其中的每个元素。数组具有很好的随机访问特征。

16.1.2链表

链表存储的数据元素称为结点,在链表中,各个结点是有序的。每个结点分为两个部分:

  • 数据域
  • 指针域

根据链表指针域的数量,可以将链表分为:

  • 单向链表
  • 双向链表

思考:链表与数组有什么不同?

16.1.3堆栈

堆栈(简称栈)是一种操作受限的线性表,只能在表头增加或删除元素。我们通常将栈使用竖直的方式来表示,因此,表尾称为“栈顶”,表头称为“栈底”。

栈表现的特征为后进先出。(LIFO——Last In First Out)

16.1.4队列

队列是一种操作受限的线性表。只能在队尾增加元素,在队头删除元素。

队列表现的特征为先进先出(FIFO)。

当队列的两端都可以增加与删除元素时,这种队列称为双端队列。双端队列可以同时实现队列与堆栈的特征。

16.1.5树

树是由结点集及连接每对结点的有向边集组成。

如果存在有向边连接A到B,则A为B的父节点(父亲),B为A的子节点(孩子)。没有父节点的结点称为根,没有子节点的结点称为叶子。具有相同父节点的各结点称为兄弟。

从根到某一节点经过的边数称为路径长度。

二叉树是一种树形结构,其特征是任意结点的孩子数不超过两个。一棵二叉树要么为空,要么由根、左子树与右子树组成(左右子树可能为空)。

一棵深度(树的层数)为m,且具有2m-1个结点的二叉树称为满二叉树。

当一棵二叉树的所有结点编号(从上到下,从左到右进行)都与满二叉树一致时,该二叉树称为完全二叉树。

树的遍历可以分为以下三种:

  • 先序遍历
  • 中序遍历
  • 后序遍历

说明:遍历二叉树实际上是将树的非线性结构进行线性化操作。

16.1.6哈希表

将一组关键字映射到一个有限的地址集上,这种表称为哈希(散列)表。哈希表中,关键字映射到相应的地址需要使用相关的算法,称为哈希函数。关键字映射后所得的存储地址称为哈希(散列)地址。

16.2算法

16.2.1算法概述

算法为解决特定问题而给出的一种方案描述。

算法效率的衡量:

  • 时间复杂度
  • 空间复杂度

16.2.2时间复杂度

时间负责度衡量的标准:

  • 执行时间
  • 执行规模(频度)
    • 最好执行次数
    • 平均执行次数
    • 最坏执行次数

常用的时间复杂度:

O(1) < O(logn) < O(n) < O(nlogn) < O(n ^ 2) < O(n ^ 2 logn) < O(n3)

16.2.3空间复杂度

空间复杂度为算法所需的存储空间。

16.3查找

16.3.1顺序查找

从前到后,顺序进行查找与关键字相符的元素。

时间复杂度:O(n)【O(1)】

16.3.2折半查找

选择中间的元素,进行比较,每次排除一半的元素。

时间复杂度:O(logn)【O(1)】。

思考:折半查找一定好于顺序查找吗?

16.4排序

从稳定性上将,排序可以分为两类:

  • 稳定排序
  • 不稳定排序

16.4.1冒泡排序

冒泡排序是进行两两比较,每次选出一个最大(小)元素,共执行n – 1次。

时间复杂度:最好:O(n) 平均:O(n2) 最差:O(n2)

稳定性:稳定。

16.4.2选择排序

每次选择一个最小(大)的元素,放入数组的最前端。共执行n – 1次。

时间复杂度:最好:O(n2) 平均:O(n2) 最差:O(n2)

稳定性:不稳定。

16.4.3插入排序

从第二个元素开始,插入到现有数组中,每个元素都保证插入之后,数组是有序的。

时间复杂度:最好:O(n) 平均:O(n2) 最差:O(n2)

稳定性:稳定。

从插入排序时间复杂度可知,插入排序适合于基本有序的情况。

16.4.4希尔排序

希尔排序(也称缩小增量排序),根据一定的增量,将记录分组,然后对每组进行插入排序。

因为在基本有序的情况下,插入排序的时间复杂度可以从O(n2)提升至O(n),故希尔排序是插入排序的一种改进版。

希尔排序的最优增量序列是一个非常复杂的问题,没有固定的选择原则,但通常情况下,应该令增量序列的每次减半,并且增量序列的最后一个值一定是1。

时间复杂度:最好:与间隔相关 平均:与间隔相关 最差:O(n2)

稳定性:不稳定。

16.4.5快速排序

从数组中选出一个中心点(pivot),使得中心点左侧的元素都小于(大于)该元素,中心点右侧的元素都大于(小于)该元素。然后针对中心点分割的这两个区间,继续执行递归的快速排序,直到整个数组区间全部有序。

时间复杂度:最好:O(nlogn) 平均:O(nlogn) 最差:O(n2)

稳定性:不稳定。

16.4.6归并排序

归并排序为将两个有序的数据集合并成一个有序的数据集。

最好:O(nlogn) 平均:O(nlogn) 最差:O(nlogn)

空间复杂度:O(n)

稳定性:稳定

第17章 线程

17.1. 线程与进程的关系

17.2. 多线程

17.2.1. 计算密集与IO密集

17.2.2. 线程的创建

17.2.3. 线程相关操作

17.3. 线程同步

17.3.1. 并发修改的问题

17.3.2. 线程锁

17.3.3. 死锁

17.4. 线程队列

17.1线程与进程的关系

程序是一些列的指令集,程序是静态的,当程序运行时,就会创建一个进程。

线程是进程的基本执行单元,一个进程至少要具有一个线程。

17.2多线程

多线程,就类似与操作系统中的多进程。简单的讲,就是可以同时并发执行多个任务,处理多件事情。这与我们经常所谓的边唱边跳,边说边做事一个道理。

17.2.1计算密集与IO密集

计算密集,是指在程序运行中,更多的时间为CPU在进行计算。而IO密集,则是更多的时间,在进行输入输出。

Python中,threading模块提供了关于线程的相关操作。

17.2.2线程的创建

我们可以采用两种方式来创建线程:

  • 使用threading模块的Thread类,通过指定target与args(可选)参数。
  • 通过继承Thread类,重写run方法。

思考:直接调用run与调用start方法有什么不同?

线程的生命周期

线程的生命周期可以分为以下环节:

  • 新建:创建对象
  • 就绪:调用start后
  • 运行:获得CPU资源
  • 阻塞(挂起):失去CPU资源
  • 死亡:线程执行结束或抛出未捕获的异常

17.2.3线程相关操作

threading模块相关功能:

  • threading.active_count()
  • threading.enumerate()
  • threading.current_thread()
  • threading.get_ident()
  • threading.main_thread()

线程对象功能:

  • start()
  • run()
  • join(timeout=None)
  • name setName / getName
  • ident
  • is_alive()
  • daemon isDaemon() / setDaemon()

17.3线程同步

17.3.1并发修改的问题

当多线程并发运行时,多线程间很可能操作共享成员变量,此时,就需要对共享成员变量的操作进行同步,避免出现多线程的并发修改而引起的意外错误。

程序:多人同时购票。

17.3.2线程锁

我们可以通过threading.Lock()获得线程锁,来实现多个线程对共享区域的互斥访问。线程锁提供两个方法:

acquire():尝试去获得锁。

release():释放所获得的锁。

注意:要确保锁得到有效的释放。

17.3.3死锁

当两个或多个线程同时拥有自己的资源,而相互等待获得对方资源,导致程序永远陷入僵持状态,这就是死锁。

当多线程并发访问共享数据时,使用线程锁可以避免多线程对共享变量并发修改带来的危害,但同时有可能会产生死锁。

17.4线程队列

queue模块提供了队列的功能,该模块具有三个类:

  • queue.Queue(maxsize=0)
  • queue.LifoQueue(maxsize=0)
  • queue.PriorityQueue(maxsize=0)

队列类实现了内部的锁机制,因此,队列类型可以安全的用于多线程并发操作中。

其中,参数maxsize表示队列的最大元素个数,如果传递0或者负值,则表示无限容量。如果队列达到了最大容量,将会进行阻塞,直到有元素删除为止。

队列对象具有的方法如下:

  • qsize()
  • empty()
  • full()
  • put(item, block=True, timeout=None)
  • put_nowait(item) put(item, False)
  • get(block=True, timeout=None)
  • get_nowait() get(False)

第18章 进程

18.1. 多进程

18.1.1. 进程的创建(Process类)

18.1.2. 进程相关的操作

18.1.3. 进程的创建(fork方法)

18.1.4. 进程队列

在Python中,由于GIL的存在,我们在多线程环境下,无法充分利用多核多CPU带来的任何优势,因此,我们可以使用多进程来代替多线程,这样就可以不用受到GIL的制约。

18.1进程的创建(Process类)

Python中提供了multiprocessing模块来实现进程的相关操作。

我们可以通过与创建线程类似的方式创建子进程:

  • 通过Process类来创建一个子进程。
  • 通过继承Process类,重写run方法来创建子进程。

注意:在Windows平台,在创建子进程时,一定要使用如下的语法:

if __name__ == "__main__":

# 创建子进程语句,例如:

    p = multiprocessing.Process(target=…)

原因在于:在Windows平台上,当创建子进程时,会自动导入创建子进程的模块,因此,这样就会出现无限递归调用。为了避免这种情况,我们使用如上的判断进行限制。在Linux中无此限制。

练习:创建一个子进程,在if __name__ == "__main__"平行的级别上面,输出“当前进程执行完毕”,会发现什么情况?

18.2进程相关的操作

  • os.getpid()
  • os.getppid()
  • os.fork()

进程对象的相关操作:

  • run()
  • start()
  • is_alive()
  • name
  • daemon
  • pid

18.3进程的创建(fork方法)

在Linux操作系统中,我们也可以通过os模块的fork方法来创建子进程。fork方法会返回一个数值,对于父进程,会返回子进程的id,对于父进程,会返回0。

当创建子进程后,子进程会在fork的位置,继续向下执行。

18.4进程队列

进程具有独立的内存空间,因此,进程通讯不像线程通讯那样容易。

我们可以使用进程队列来完成进程之间的通讯。进程队列的用法与线程队列非常相似。

第19章 网络编程

19.1. 网络基础

19.1.1. 计算机网络

19.1.2. 计算机通讯协议

19.1.3. IP地址与端口号

19.2. URL

19.2.1. 基本组成

19.2.2. 解析URL

19.2.3. 发起请求

19.3. TCP

19.4. UDP

19.5. 协程

19.1网络基础

19.1.1计算机网络

计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,从而实现信息与资源共享的计算机系统。

网络分为如下的七层结构:

19.1.2计算机通讯协议

计算机网络中的计算机可以相互间进行信息交流。而要进行交流,就需要具有一定的规则,我们称之为通讯协议。可以认为,协议就是一种规范。

相关协议:

    • HTTP(HyperText Transfer Protocol )超文本传输协议
    • IP(Internet Protocol)互联网协议
    • TCP(Transmission Control Protocol)传输控制协议
    • UDP(User Datagram Protocol)用户数据报协议
    • FTP(File Transfer Protocol)文件传输协议

TCP/IP协议经常放在一起使用。在TCP/IP协议下,网络通常也分成如下四层的结构:

TCP与UDP协议

  • TCP协议是基于连接的协议,该协议提供点对点的通道,在两台计算机设备间进行可靠的数据传输。TCP协议保证一方发送的数据可以准确的被另一方接收,并且接收数据的顺序与发送的顺序一致。(HTTP,FTP)
  • UDP协议是基于数据报的协议(不是基于连接的协议),该协议发送独立的数据报(数据包),各个数据报之间不受影响。UDP协议不保证接收端一定会接收到发送端发送的数据报,同时,也不保证接收数据报的顺序与发送的顺序一致。(ping)

19.1.3IP地址与端口号

在计算机网络中,可能会存在多台计算机,我们使用IP地址来区分每台计算机。可以认为,IP地址就是每台计算机在计算机网络中的唯一标识。IP地址使用32位数据来表示。我们习惯上将IP地址分为4段,每段8位。(0~255)

端口号用来标识不同的程序。端口号使用16位数据来表示。(0~65535)其中,0~1023端口被常用的系统服务所占用。我们使用的服务不应使用这些端口。

数据传输时,会携带IP地址与端口号信息。其中,IP地址用来识别数据传输到哪台计算机,而端口号用来识别,传输到计算机的哪个应用程序。

19.2URL

19.2.1基本组成

URL(Uniform Resource Locator)统一资源定位,为一个字符串序列,引用网络上的一个资源地址,该地址可以是文件,目录,数据库查询等。

URL分为两个部分:(二者使用://进行分隔)

  • 协议标识符 使用哪种协议获取相关资源
  • 资源名称 资源的详细地址

例如:http://www.somewebsite.com

其中,资源名称的格式取决于所使用的协议,大部分协议的格式如下:

  • 主机名称
  • 文件名称(路径)
  • 端口号
  • 相关引用

例如:http://127.0.0.1:8080/index.html#bak

说明:通常主机名称与文件名称是必须的,而端口号与相关引用是可选的。

19.2.2解析URL

我们可以通过urlparse函数来对指定的URL进行解析,结果分为六部分:

  • 协议
  • 网络地址
  • 路径
  • 路径参数
  • 查询参数
  • 引用片段

函数返回ParseResult类型的对象(元组的子类型),我们可以通过索引(或者属性名)来访问对应的部分。

19.2.3发起请求

我们可以向指定的URL发送请求,获得服务端的回应信息。

访问本地资源。

下载指定的URL资源。

19.3TCP

网络上的两个程序建立双向的通讯连接,这个连接的一端称为一个socket,每个socket会绑定一个特定的端口号。客户端与服务端通过socket进行数据读取与写入。

我们可以使用socket模块提供的功能,来实现TCP与UDP协议下的网络通讯。

19.3.1UDP

数据报是相互独立的信息,通过网络发送。对于接收端,是否能接收,接收时间,接收的顺序都是不确定的。

19.3.2协程

协程是比线程更小的执行单元。比起线程,协程的切换更加简单容易,减少性能消耗。

之前学习的生成器,就可以实现协程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值