数据结构和算法基础
数据结构基本概念
什么是数据结构?
- 数据
数据即信息的载体,是能够输入到计算机中并且能被计算机识别、存储和处理的符号总称。
- 数据元素
数据元素是数据的基本单位,又称之为记录(Record)。一般,数据元素由若干基本项(或称字段、域、属性)组成。
- 数据结构
数据结构指的是数据元素及数据元素之间的相互关系,或组织数据的形式。
数据之间的结构关系
- 逻辑结构
表示数据之间的抽象关系(如邻接关系、从属关系等),按每个元素可能具有的直接前趋数和直接后继数将逻辑结构分为“线性结构”和“非线性结构”两大类。
- 存储结构
逻辑结构在计算机中的具体实现方法,分为顺序存储方法、链接存储方法、索引存储方法、散列存储方法。
逻辑结构(关系)
- 特点:
- 只是描述数据结构中数据元素之间的联系规律
- 是从具体问题中抽象出来的数学模型,是独立于计算机存储器的(与机器无关)
- 逻辑结构分类
- 线性结构
对于数据结构课程而言,简单地说,线性结构是n个数据元素的有序(次序)集合。
- 集合中必存在唯一的一个"第一个元素";
- 集合中必存在唯一的一个"最后的元素";
- 除最后元素之外,其它数据元素均有唯一的"后继";
- 除第一元素之外,其它数据元素均有唯一的"前驱"。
- 树形结构(层次结构)
树形结构指的是数据元素之间存在着“一对多”的树形关系的数据结构,是一类重要的非线性数据结构。在树形结构中,树根结点没有前驱结点,其余每个结点有且只有一个前驱结点。叶子结点没有后续结点,其余每个结点的后续节点数可以是一个也可以是多个。
- 图状结构(网状结构)
图是一种比较复杂的数据结构。在图结构中任意两个元素之间都可能有关系,也就是说这是一种多对多的关系。
- 其他结构
除了以上几种常见的逻辑结构外,数据结构中还包含其他的结构,比如集合等。有时根据实际情况抽象的模型不止是简单的某一种,也可能拥有更多的特征。
存储结构(关系)
- 特点:
- 是数据的逻辑结构在计算机存储器中的映象(或表示)
- 存储结构是通过计算机程序来实现的,因而是依赖于具体的计算机语言的。
- 存储结构分类
- 顺序存储
顺序存储(Sequential Storage):将数据结构中各元素按照其逻辑顺序存放于存储器一片连续的存储空间中。
- 链式存储
链式存储(Linked Storage):将数据结构中各元素分布到存储器的不同点,用记录下一个结点位置的方式建立它们之间的联系,由此得到的存储结构为链式存储结构。
- 索引存储
索引存储(Indexed Storage):在存储数据的同时,建立一个附加的索引表,即索引存储结构=数据文件+索引表。
线性表
线性表的定义是描述其逻辑结构,而通常会在线性表上进行的查找、插入、删除等操作。
线性表作为一种基本的数据结构类型,在计算机存储器中的映象(或表示)一般有两种形式,一种是顺序映象,一种是链式映象。
线性表的顺序存储
- 定义
若将线性表L=(a0,a1, ……,an-1)中的各元素依次存储于计算机一片连续的存储空间,这种机制表示为线性表的顺序存储结构。
- 特点
- 逻辑上相邻的元素 ai, ai+1,其存储位置也是相邻的;
- 存储密度高,方便对数据的遍历查找。
- 对表的插入和删除等运算的效率较差。
- 程序实现
在Python中,list存放于一片单一连续的内存块,故可借助于列表类型来描述线性表的顺序存储结构,而且列表本身就提供了丰富的接口满足这种数据结构的运算。
1 2 3 >>>L = [1,2,3,4] 4 >>>L.append(10) #尾部增加元素 5 L 6 [1, 2, 3, 4, 10] 7 8 >>>L.insert(1,20) #插入元素 9 L 10 [1, 20, 2, 3, 4, 10] 11 12 >>>L.remove(3) #删除元素 13 L 14 [1, 20, 2, 4, 10] 15 16 >>>L[4] = 30 #修改 17 L 18 [1, 20, 2, 4, 30] 19 20 >>>L.index(2) #查找 21 2
线性表的链式存储
- 定义
将线性表L=(a0,a1,……,an-1)中各元素分布在存储器的不同存储块,称为结点,每个结点(尾节点除外)中都持有一个指向下一个节点的引用,这样所得到的存储结构为链表结构。
- 特点
- 逻辑上相邻的元素 ai, ai+1,其存储位置也不一定相邻;
- 存储稀疏,不必开辟整块存储空间。
- 对表的插入和删除等运算的效率较高。
- 逻辑结构复杂,不利于遍历。
- 程序实现
1 """ 2 linklist.py 3 功能: 实现单链表的构建和功能操作 4 重点代码 5 """ 6 7 8 # 创建节点类 9 class Node: 10 """ 11 思路: 将自定义的类视为节点的生成类,实例对象中 12 包含数据部分和指向下一个节点的next 13 """ 14 15 def __init__(self, val, next=None): 16 self.val = val # 有用数据 17 self.next = next # 循环下一个节点关系 18 19 20 # 做链表操作 21 class LinkList: 22 """ 23 思路: 单链表类,生成对象可以进行增删改查操作 24 具体操作通过调用具体方法完成 25 """ 26 27 def __init__(self): 28 """ 29 初始化链表,标记一个链表的开端,以便于获取后续 30 的节点 31 """ 32 self.head = Node(None) 33 34 # 通过list_为链表添加一组节点 35 def init_list(self, list_): 36 p = self.head # p 作为移动变量 37 for item in list_: 38 p.next = Node(item) 39 p = p.next 40 41 # 遍历链表 42 def show(self): 43 p = self.head.next # 第一个有效节点 44 while p is not None: 45 print(p.val) 46 p = p.next # p向后移动 47 48 # 判断链表为空 49 def is_empty(self): 50 if self.head.next is None: 51 return True 52 else: 53 return False 54 55 # 清空链表 56 def clear(self): 57 self.head.next = None 58 59 # 尾部插入 60 def append(self, val): 61 p = self.head 62 while p.next is not None: 63 p = p.next 64 p.next = Node(val) 65 66 # 头部插入 67 def head_insert(self, val): 68 node = Node(val) 69 node.next = self.head.next 70 self.head.next = node 71 72 # 指定插入位置 73 def insert(self, index, val): 74 p = self.head 75 for i in range(index): 76 # 超出位置最大范围结束循环 77 if p.next is None: 78 break 79 p = p.next 80 81 node = Node(val) 82 node.next = p.next 83 p.next = node 84 85 # 删除节点 86 def delete(self, x): 87 p = self.head 88 # 结束循环必然两个条件其一为假 89 while p.next and p.next.val != x: 90 p = p.next 91 92 if p.next is None: 93 raise ValueError("x not in linklist") 94 else: 95 p.next = p.next.next 96 97 # 获取某个节点值,传入节点位置获取节点值 98 def get_index(self,index): 99 if index < 0: 100 raise IndexError("index out of range") 101 p = self.head.next 102 for i in range(index): 103 if p.next is None: 104 raise IndexError("index out of range") 105 p = p.next 106 return p.val
1 from linklist import * 2 import time 3 4 l = [] 5 for i in range(999999): 6 l.append(i) 7 8 link = LinkList() 9 link.init_list(l) 10 11 tm = time.time() 12 # for i in l: 13 # print(i) # 列表 14 # link.show() # 链表 15 16 # l.append(8866) 17 # link.append(8866) # 尾插入 18 19 # l.insert(0,8866) 20 # link.head_insert(8866) # 头插 21 22 # link.insert(100,9999) 23 24 # link.delete(1) #删除 25 l.remove(1) 26 27 # link.show() 28 # print(link.get_index(-1)) 29 30 print("time: ",time.time()-tm)
栈和队列
栈
- 定义
栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈),允许进行操作的一端称为“栈顶”,另一固定端称为“栈底”,当栈中没有元素时称为“空栈”。
- 特点:
- 栈只能在一端进行数据操作
- 栈模型具有先进后出或者叫做后进先出的规律
- 栈的代码实现
栈的操作有入栈(压栈),出栈(弹栈),判断栈的空满等操作。
顺序存储代码实现:
1 """ 2 sstack.py 栈模型的顺序存储 3 重点代码 4 5 思路总结: 6 1. 列表即顺序存储,但功能多,不符合栈的模型特征 7 2. 利用列表 将其封装,提供接口方法 8 """ 9 10 # 自定义异常 11 class StackError(Exception): 12 pass 13 14 # 顺序栈类 15 class SStack: 16 def __init__(self): 17 # 空列表就是栈的存储空间 18 # 列表的最后一个元素作为栈顶 19 self._elems = [] 20 21 # 判断列表是否为空 22 def is_empty(self): 23 return self._elems == [] 24 25 # 入栈 26 def push(self,val): 27 self._elems.append(val) 28 29 # 出栈 30 def pop(self): 31 if self.is_empty(): 32 raise StackError("Stack is empty") 33 # 弹出并返回 34 return self._elems.pop() 35 36 # 查看栈顶元素 37 def top(self): 38 if self.is_empty(): 39 raise StackError("Stack is empty") 40 return self._elems[-1] 41 42 43 if __name__ == "__main__": 44 st = SStack() # 初始化栈 45 st.push(10) 46 st.push(20) 47 st.push(30) 48 while not st.is_empty(): 49 print(st.pop())
链式存储代码实现:
1 """ 2 lstack.py 栈的链式栈 3 重点代码 4 5 思路分析: 6 1. 源于链表结构 7 2. 封装栈的操作方法(入栈,出栈,栈空,栈顶元素) 8 3. 链表的开头作为栈顶 ? (不用每次遍历) 9 """ 10 11 # 自定义异常 12 class StackError(Exception): 13 pass 14 15 # 节点类 16 class Node: 17 def __init__(self, val, next=None): 18 self.val = val 19 self.next = next 20 21 # 链式栈操作 22 class LStack: 23 def __init__(self): 24 # 标记栈的栈顶位置 25 self._top = None 26 27 def is_empty(self): 28 return self._top is None 29 30 def push(self,val): 31 self._top = Node(val,self._top) 32 33 def pop(self): 34 if self._top is None: 35 raise StackError("Stack is emtpy") 36 value = self._top.val 37 self._top = self._top.next 38 return value 39 40 def top(self): 41 if self._top is None: 42 raise StackError("Stack is emtpy") 43 return self._top.val 44 45 if __name__ == "__main__": 46 ls = LStack() 47 ls.push(1) 48 ls.push(2) 49 ls.push(3) 50 print(ls.pop()) 51 print(ls.pop())
队列
- 定义
队列是限制在两端进行插入操作和删除操作的线性表,允许进行存入操作的一端称为“队尾”,允许进行删除操作的一端称为“队头”。
- 特点
- 队列只能在队头和队尾进行数据操作
- 队列模型具有先进先出或者叫做后进后出的规律
- 队列的代码实现
队列的操作有入队,出队,判断队列的空满等操作。
顺序存储代码实现:
1 """ 2 squeue.py 队列的顺序存储 3 4 思路分析: 5 1. 基于列表完成数据存储 6 2. 通过封装规定数据操作 7 3. 先确定列表的哪一段作为队头 8 """ 9 10 # 自定义队列异常 11 class QueueError(Exception): 12 pass 13 14 # 队列操作 15 class SQueue: 16 # 初始化 17 def __init__(self): 18 self._elems = [] 19 20 # 判断队列是否为空 21 def is_empty(self): 22 return self._elems == [] 23 24 # 入队 列表尾部定义为队尾 25 def enqueue(self,val): 26 self._elems.append(val) 27 28 # 出队 列表的第一个元素 29 def dequeue(self): 30 if not self._elems: 31 raise QueueError("Queue is empty") 32 return self._elems.pop(0) 33 34 if __name__ == "__main__": 35 sq = SQueue() 36 for i in range(10): 37 sq.enqueue(i) 38 39 ########### 将队列翻转 ############# 40 from sstack import * 41 st = SStack() 42 # 循环出队入栈 43 while not sq.is_empty(): 44 st.push(sq.dequeue()) 45 # 循环出栈入队 46 while not st.is_empty(): 47 sq.enqueue(st.pop()) 48 49 while not sq.is_empty(): 50 print(sq.dequeue())
链式存储代码实现: 待补充lqueue.py
树形结构
基础概念
- 定义
树(Tree)是n(n≥0)个节点的有限集合T,它满足两个条件:有且仅有一个特定的称为根(Root)的节点;其余的节点可以分为m(m≥0)个互不相交的有限集合T1、T2、……、Tm,其中每一个集合又是一棵树,并称为其根的子树(Subtree)。
- 基本概念
- 一个节点的子树的个数称为该节点的度数,一棵树的度数是指该树中节点的最大度数。
- 度数为零的节点称为树叶或终端节点,度数不为零的节点称为分支节点,除根节点外的分支节点称为内部节点。
- 一个节点的子树之根节点称为该节点的子节点,该节点称为它们的父节点,同一节点的各个子节点之间称为兄弟节点。一棵树的根节点没有父节点,叶节点没有子节点。
- 一个节点系列k1,k2, ……,ki,ki+1, ……,kj,并满足ki是ki+1的父节点,就称为一条从k1到kj的路径,路径的长度为j-1,即路径中的边数。路径中前面的节点是后面节点的祖先,后面节点是前面节点的子孙。
- 节点的层数等于父节点的层数加一,根节点的层数定义为一。树中节点层数的最大值称为该树的高度或深度。
- m(m≥0)棵互不相交的树的集合称为森林。树去掉根节点就成为森林,森林加上一个新的根节点就成为树。
二叉树
定义与特征
- 定义
二叉树(Binary Tree)是n(n≥0)个节点的有限集合,它或者是空集(n=0),或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。二叉树与普通有序树不同,二叉树严格区分左孩子和右孩子,即使只有一个子节点也要区分左右。
- 二叉树的特征
-
二叉树第i(i≥1)层上的节点最多为2i−12^{i-1}2i−1个。
-
深度为k(k≥1)的二叉树最多有2k-12^k-12k-1个节点。
-
在任意一棵二叉树中,树叶的数目比度数为2的节点的数目多一。
-
满二叉树 :深度为k(k≥1)时有2k-12^k-12k-1个节点的二叉树。
-
完全二叉树 :只有最下面两层有度数小于2的节点,且最下面一层的叶节点集中在最左边的若干位置上。
二叉树的遍历
遍历 :沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次。
先序遍历: 先访问树根,再访问左子树,最后访问右子树;
中序遍历: 先访问左子树,再访问树根,最后访问右子树;
后序遍历: 先访问左子树,再访问右子树,最后访问树根;
层次遍历: 从根节点开始,逐层从左向右进行遍历。
1 /*求二叉树的结点总数*/ 2 #include<stdio.h> 3 #define maxsize 100 4 typedef char datatype; 5 /*二叉链表类型定义*/ 6 typedef struct Binnode 7 { 8 datatype data; /*数据域*/ 9 struct BinNode* lchild,*rchild; /*指向左、右孩子的指针*/ 10 }BinNode,*Bintree; 11 /*按先序创建二叉树*/ 12 Bintree CreateTree(Bintree T) 13 { 14 datatype ch; 15 scanf("%c",&ch); 16 if(ch=='#') 17 return NULL; 18 else 19 { 20 T=(Bintree)malloc(sizeof(BinNode)); 21 T->data=ch; 22 T->lchild=CreateTree(T->lchild);/*创建左子树*/ 23 T->rchild=CreateTree(T->rchild);/*创建右子树*/ 24 return T; 25 } 26 } 27 /*求二叉树结点总数*/ 28 int Count(Bintree T) 29 { 30 if(T==NULL) 31 return 0; /*空二叉树结点数为0*/ 32 else /*左右子树结点总数加1*/ 33 return Count(T->lchild)+Count(T->rchild)+1; 34 } 35 main() 36 { 37 Bintree t; 38 printf("请按先序的方式输入二叉树的结点元素(注:#表示节点为空):"); 39 t=CreateTree(t); 40 printf("二叉树结点总数:%d",Count(t)); 41 } 42 --------------------- 43 作者:云淡风轻58 44 来源:CSDN 45 原文:https://blog.csdn.net/dwenxue/article/details/72477492 46 版权声明:本文为博主原创文章,转载请附上博文链接!
递归思想和实践
- 什么是递归?
所谓递归函数是指一个函数的函数体中直接调用或间接调用了该函数自身的函数。这里的直接调用是指一个函数的函数体中含有调用自身的语句,间接调用是指一个函数在函数体里有调用了其它函数,而其它函数又反过来调用了该函数的情况。
- 递归函数调用的执行过程分为两个阶段
递推阶段:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件。
回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解。
- 优点与缺点
优点:递归可以把问题简单化,让思路更为清晰,代码更简洁
缺点:递归因系统环境影响大,当递归深度太大时,可能会得到不可预知的结果
递归示例:
1 """ 2 求一个数的阶乘 3 递归实现 4 """ 5 6 def recursion(num): 7 if num <= 1: 8 return 1 9 return num * recursion(num - 1) 10 11 print("n! = ",recursion(5))
二叉树的代码实现
二叉树顺序存储
二叉树本身是一种递归结构,可以使用Python list 进行存储。但是如果二叉树的结构比较稀疏的话浪费的空间是比较多的。
- 空结点用None表示
- 非空二叉树用包含三个元素的列表[d,l,r]表示,其中d表示根结点,l,r左子树和右子树。
['A',['B',None,None
],
['C',['D',['F',None,None],
['G',None,None],
],
['E',['H',None,None],
['I',None,None],
],
]
]
二叉树链式存储
二叉树遍历:
1 """ 2 bitree.py 二叉树的简单实践 3 4 思路分析: 5 1. 使用链式存储,一个Node表示一个树的节点 6 2. 节点考虑使用两个属性变量分别表示左连接和右连接 7 """ 8 from squeue import * 9 10 # 二叉树节点 11 class Node: 12 def __init__(self,val,left=None,right = None): 13 self.val = val 14 self.left = left 15 self.right = right 16 17 # 二叉树遍历类 18 class Bitree: 19 def __init__(self,root = None): 20 self.root = root 21 22 # 先序遍历 23 def preOrder(self,node): 24 if node is None: # 终止条件 25 return 26 print(node.val,end = '') 27 self.preOrder(node.left) 28 self.preOrder(node.right) 29 30 # 中序遍历 31 def inOrder(self, node): 32 if node is None: # 终止条件 33 return 34 self.inOrder(node.left) 35 print(node.val,end = "") 36 self.inOrder(node.right) 37 38 # 后序遍历 39 def postOrder(self, node): 40 if node is None: # 终止条件 41 return 42 self.postOrder(node.left) 43 self.postOrder(node.right) 44 print(node.val, end="") 45 46 # 层次遍历 47 def levelOrder(self,node): 48 """ 49 让初始节点先入队,谁出队就遍历谁,并且让它的左右孩子分别入队,直到队列为空 50 """ 51 sq = SQueue() 52 sq.enqueue(node) # 初始节点入队 53 while not sq.is_empty(): 54 node = sq.dequeue() 55 # 打印出队元素 56 print(node.val,end='') 57 if node.left: 58 sq.enqueue(node.left) 59 if node.right: 60 sq.enqueue(node.right) 61 62 63 64 if __name__ == "__main__": 65 # B F G D I H E C A 66 # 根据后续遍历构建二叉树 67 b = Node('B') 68 f = Node('F') 69 g = Node('G') 70 d = Node('D',f,g) 71 i = Node('I') 72 h = Node('H') 73 e = Node('E',i,h) 74 c = Node('C',d,e) 75 a = Node('A',b,c) #树根 76 77 # 将a作为遍历的起始位置 78 bt = Bitree(a) 79 80 bt.preOrder(bt.root) 81 print() 82 bt.inOrder(bt.root) 83 print() 84 bt.postOrder(bt.root) 85 print() 86 bt.levelOrder(bt.root)
算法基础
基础概念特征
- 定义
算法(Algorithm)是一个有穷规则(或语句、指令)的有序集合。它确定了解决某一问题的一个运算序列。对于问题的初始输入,通过算法有限步的运行,产生一个或多个输出。
数据的逻辑结构与存储结构密切相关:
- 算法设计: 取决于选定的逻辑结构
- 算法实现: 依赖于采用的存储结构
- 算法的特性
- 有穷性 —— 算法执行的步骤(或规则)是有限的;
- 确定性 —— 每个计算步骤无二义性;
- 可行性 —— 每个计算步骤能够在有限的时间内完成;
- 输入 ,输出 —— 存在数据的输入和出输出
- 评价算法好坏的方法
- 正确性:运行正确是一个算法的前提。
- 可读性:容易理解、容易编程和调试、容易维护。
- 健壮性:考虑情况全面,不容以出现运行错误。
- 时间效率高:算法消耗的时间少。
- 储存量低:占用较少的存储空间。
时间复杂度计算
算法效率——用依据该算法编制的程序在计算机上执行所消耗的时间来度量。“O”表示一个数量级的概念。根据算法中语句执行的最大次数(频度)来 估算一个算法执行时间的数量级。
计算方法:
写出程序中所有运算语句执行的次数,进行加和
如果得到的结果是常量则时间复杂度为1
如果得到的结果中存在变量n则取n的最高次幂作为时间复杂度
下图表示随问题规模n的增大,算法执行时间的增长率。
排序和查找
排序
排序(Sort)是将无序的记录序列(或称文件)调整成有序的序列。
常见排序方法:
- 冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
- 选择排序
工作原理为,首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
- 插入排序
对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
- 快速排序
步骤:
从数列中挑出一个元素,称为 "基准"(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
常见排序代码实现:
1 """ 2 sort.py 排序算法训练 3 """ 4 5 # 冒泡 6 def bubble(list_): 7 n = len(list_) 8 # 外层表示比较多少轮 9 for i in range(n - 1): 10 # 表示每轮两两比较的次数 11 for j in range(n - 1 - i): 12 # 从小到大排序 13 if list_[j] > list_[j + 1]: 14 list_[j],list_[j + 1] = list_[j + 1],list_[j] 15 16 # 完成一轮交换 17 def sub_sort(list_,low,high): 18 # 选定基准 19 x = list_[low] 20 # low向后 high向前 21 while low < high: 22 # 后面的数往前放 23 while list_[high] >= x and high > low: 24 high -= 1 25 list_[low] = list_[high] 26 # 前面的数往后放 27 while list_[low] < x and low < high: 28 low += 1 29 list_[high] = list_[low] 30 31 list_[low] = x 32 return low 33 34 35 def quick(list_,low,high): 36 # low 表示列表第一个元素索引,high表示最后一个元素索引 37 if low < high: 38 key = sub_sort(list_,low,high) 39 quick(list_,low,key - 1) 40 quick(list_, key + 1,high) 41 42 43 44 l = [4,9,3,1,2,5,8,4] 45 # bubble(l) 46 quick(l,0,len(l)-1) 47 print(l)
1 """ 2 sort.py 排序算法训练 3 """ 4 5 # 冒泡 6 def bubble(list_): 7 n = len(list_) 8 # 外层表示比较多少轮 9 for i in range(n - 1): 10 # 表示每轮两两比较的次数 11 for j in range(n - 1 - i): 12 # 从小到大排序 13 if list_[j] > list_[j + 1]: 14 list_[j],list_[j + 1] = list_[j + 1],list_[j] 15 16 # 完成一轮交换 17 def sub_sort(list_,low,high): 18 # 选定基准 19 x = list_[low] 20 # low向后 high向前 21 while low < high: 22 # 后面的数往前放 23 while list_[high] >= x and high > low: 24 high -= 1 25 list_[low] = list_[high] 26 # 前面的数往后放 27 while list_[low] < x and low < high: 28 low += 1 29 list_[high] = list_[low] 30 31 list_[low] = x 32 return low 33 34 35 def quick(list_,low,high): 36 # low 表示列表第一个元素索引,high表示最后一个元素索引 37 if low < high: 38 key = sub_sort(list_,low,high) 39 quick(list_,low,key - 1) 40 quick(list_, key + 1,high) 41 42 43 # 选择排序 44 def select(list_): 45 # 没轮选出一个最小值,需要 len(list_) - 1 轮 46 for i in range(len(list_) - 1): 47 min = i # 假设 list_[i] 为最小值 48 for j in range(i + 1,len(list_)): 49 if list_[min] > list_[j]: 50 min = j # 擂主换人 51 # 进行交换,将最小值换到应该在的位置 52 if min != i: 53 list_[i],list_[min] = list_[min],list_[i] 54 55 # 插入排序 56 def insert(list_): 57 # 控制每次比较的数是谁,从第二个数开始 58 for i in range(1,len(list_)): 59 x = list_[i] # 空出list_[i]的位置 60 j = i - 1 61 while j >= 0 and list_[j] > x: 62 list_[j + 1] = list_[j] 63 j -= 1 64 list_[j + 1] = x 65 66 67 l = [4,9,3,1,2,5,8,4] 68 # bubble(l) 69 # quick(l,0,len(l)-1) 70 # select(l) 71 insert(l) 72 73 print(l)
查找
查找(或检索)是在给定信息集上寻找特定信息元素的过程。
二分法查找
当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。
二分查找代码实现:
1 """ 2 search.py 二分查找方法训练 3 """ 4 5 # list_为有序数列,key为要查找的关键值,返回key在数列中的索引号 6 def search(list_,key): 7 # 第一个数index 最后一个数index 8 low,high = 0,len(list_) - 1 9 10 # 循环每次去除一半内容 11 while low <= high: 12 mid = (low + high) // 2 13 # 取后半部分 14 if list_[mid] < key: 15 low = mid + 1 16 # 取前半部分 17 elif list_[mid] > key: 18 high = mid - 1 19 else: 20 return mid 21 22 23 l = [1,2,3,4,5,6,7,8,9,10] 24 print("Key index:",search(l,10))
exercise
1 """ 2 一段文字中有()[]{},编写一个接口程序去判断括号是否匹配正确 3 """ 4 5 from lstack import * 6 7 text = "The core (of) extensible programming [is] defining functions. Python allows {mandatory [and]} optional (arguments, {keyword} arguments), and even arbitrary argument lists." 8 9 # 将验证条件提前定义好 10 parens = "()[]{}" # 特殊处理的字符集 11 left_parens = "([{" # 入栈字符集 12 # 验证匹配关系 13 opposite = {'}':'{',']':'[',')':'('} 14 15 ls = LStack() # 存储括号的栈 16 17 # 编写生成器,用来遍历字符串,不断的提供括号及其位置 18 def parent(text): 19 # i 遍历字符串的索引位置 20 i,text_len = 0,len(text) 21 22 # 开始遍历字符串 23 while True: 24 while i < text_len and text[i] not in parens: 25 i += 1 26 27 # 到字符串结尾了 28 if i >= text_len: 29 return 30 else: 31 yield text[i],i 32 i += 1 33 34 # 功能函数判断提供的括号是否匹配 35 def ver(): 36 for pr,i in parent(text): 37 if pr in left_parens: 38 ls.push((pr,i)) # 左括号入栈 39 elif ls.is_empty() or ls.pop()[0] != opposite[pr]: 40 print("Unmatching is found at %d for %s"%(i,pr)) 41 break 42 else: 43 if ls.is_empty(): 44 print("All parentheses are matched") 45 else: 46 # 左括号多了 47 d = ls.pop() 48 print("Unmatching is found at %d for %s" %(d[1],d[0])) 49 50 # 逻辑验证 51 ver()
1 """ 2 求一个数的阶乘 3 """ 4 5 # n次 6 7 def fun(num): 8 result = 1 9 for i in range(1,num + 1): 10 result *= i 11 return result 12 13 print(fun(5))