《算法篇章》---学习笔记

算法

一丶时间复杂度

# 评判规则:量化算法执行的操作 / 执行步骤的数量
# 最重要的项:时间复杂度表达式中最有意义的项

时间复杂度排序

O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

二丶数据结构

# 概念:
    对数据(基本数据类型(int,float,char))的组织方式被称作数据结构. 数据结构解决的是一组数据如何进行保存.
    

三丶python测试代码执行的时长

timeit模块:

# Timer类:该类是timeit模块中专门用于测量python代码的执行速度/时长的。
    原型为:class timeit.Timer(stmt='pass',setup='pass')。

    stmt参数:表示即将进行测试的代码块语句。

    setup:运行代码块语句时所需要的设置。

    timeit函数:timeit.Timer.timeit(number=100000),该函数返回代码块语句执行number次的平均耗时。
# 例如:
from timeit import Timer
def test01():
    alist = []
    for i in range(1000):
        alist.append(i)
    return alist
def test02():
    alist = []
    for i in range(1000):
        alist.insert(0,i)
    return alist
def test03():
    alist = []
    for i in range(1000):
        alist += [i]
    return alist
def test04():
    alist = list(range(1000))
    return alist
if __name__=='__main__':
    t=Timer('test01()','from __main__ import test01')
    
    print(t.timeit(1000)) # 执行1000次,所耗费的时间
    t1=Timer('test02()','from __main__ import test02')
    print(t1.timeit(1000)) # 执行1000次,所耗费的时间 

四丶栈

# 特性: 先进后出的数据结构
# 重要的两个概念: 栈顶 栈底
#### 使用python 类 实现栈
# 需求:
    Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈。
    push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容。
    pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改。
    peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈。
    isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。
    size() 返回栈中的 item 数量。不需要参数,并返回一个整数。
class Stack():
    def __init__(self):
        self.items=[]
        
    def push(self,item):
        '''入栈'''
        return self.items.append(item)
        
    def pop(self):
        '''出栈'''
        return self.items.pop() # 删除最后一个元素 , 利用列表的特性pop方法删除最后一个元素
    
    def peek(self):
        '获取栈顶元素的"索引"位置'
        return len(self.items)-1
        
    def isEmpty(self):
        '''判断是否是空栈'''
        return self.items==[] # 返回布尔值 
    
    def size(self):
        '''返回栈的大小'''
        return len(self.items)

五丶队列

# 特性: 先进先出
# 重要的两个概念: 对头  对尾
# python 类 实现 队列
    Queue() 创建一个空的新队列。 它不需要参数,并返回一个空队列。
    enqueue(item) 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。
    dequeue() 从队首移除项。它不需要参数并返回 item。 队列被修改。
    isEmpty() 查看队列是否为空。它不需要参数,并返回布尔值。
    size() 返回队列中的项数。它不需要参数,并返回一个整数。
class Queue():
    def __init__(self):
        self.items=[]
        
    def enqueue(self,item):
        '''入队'''
        return self.items.append(item)

    def dequeue(self):
        '''出队'''
        return self.items.pop(0) 
    
    def isEmpty(self):
        '''判断是否是空队'''
        return self.items==[] # 返回布尔值 

    def size(self):
        '''返回队的大小'''
        return len(self.items)
# 例题: 烫手的山芋
    # 规则如下:
        烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
        
     # 分析: 一秒之后再进行传递 , 7秒的时间,传递次数是6次,即:time-1

# 代码:
class Queue():
    def __init__(self):
        self.items = []
    def enqueue(self,item):
        self.items.insert(0,item)
    def dequeue(self):
        return self.items.pop()
    def size(self):
        return len(self.items)
    
#初始化了六个孩子
kids = ['A','B','C','D','E','F']
q=Queue() # 实例化一个队列
for kid in kids:
    q.enqueue(kid)

while q.size()>1: 
    for i in range(6):
         kid=q.dequeue()
          q.enqueue(kid)
     q.dequque() # 传递 7秒时间,剔除一个孩子
print(q.dequque())

六丶双端队列

# 同同列相比,有两个头部和尾部。可以在双端进行数据的插入和删除,提供了单数据结构中栈和队列的特性

# python 实现 双端队列
    Deque() 创建一个空的新 deque。它不需要参数,并返回空的 deque。
    addFront(item) 将一个新项添加到 deque 的首部。它需要 item 参数 并不返回任何内容。
    addRear(item) 将一个新项添加到 deque 的尾部。它需要 item 参数并不返回任何内容。
    removeFront() 从 deque 中删除首项。它不需要参数并返回 item。deque 被修改。
    removeRear() 从 deque 中删除尾项。它不需要参数并返回 item。deque 被修改。
    isEmpty() 测试 deque 是否为空。它不需要参数,并返回布尔值。
    size() 返回 deque 中的项数。它不需要参数,并返回一个整数。
class Deque():
    def __init__(self):
        self.items=[]
    
    def addFront(self,item):
        return self.items.insert(0,item)
        
    def addRear(self):
        return self.items.append(item)
    
    def removeFront(self):
        return self.items.pop(0)
    
    def removeRear(self):
        return self.items.pop()
        
    def isEmpty(self):
        return self.items == []

    def size(self):
        return len(self.items)
            
# 双端队列应用案例:回文检查
# 回文是一个字符串,读取首尾相同的字符,例如,radar toot madam。
q = Deque()
s = 'radar'
for i in s:
    q.addFront(i)

while q.size() > 1:
    if q.removeFront() != q.removeRear():
        print('不是回文')
        break
else:
    print('是回文')

七丶内存

# 1. 什么是变量
    变量就是引用。变量其实表示的就是计算机中的一块内存。(即:变量起始就是内存地址)
# 2.一块内存空间会有两个默认的属性
    # 空间大小
        bit:位
        byte:字节
        kb:1020字节
        mb
        gb
        tb
    # 内存地址
        作用:寻址

八丶顺序表

# 首先明确:  集合中存储的元素是有顺序的,顺序表的结构可以分为两种形式:单数据类型和多数据类型。
# 然后明白:  python中的列表和元组就属于多数据类型的顺序表
# 单数据类型: 内存使连续开辟的
# 多数据类型: 内存是非连续开辟的

# 顺序表的弊端:顺序表的结构需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁。

九丶链表

# 什么是链表?
    1. 相对于顺序表,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理且进行扩充时不需要进行数据搬迁。
    2. 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是每一个结点(数据存储单元)里存放下一个结点的信息(即地址)
# python 实现 链表
     is_empty():链表是否为空

     length():链表长度

     travel():遍历整个链表

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

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

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

     remove(item):删除节点

     search(item):查找节点是否存在
# 构建 Node节点
class Node():
    def __init__(self,item):
        self.item=item
        self.next=None
        
# 构建链表
class Link():
    def __init__(self):
        # head的属性永远指向第一个节点的地址,如果链表为空则指向None
        self.head=None
    

    def add(self,item):
        ' 给链表头 添加节点'
        node=Node(item)
        node.next=self.head  # 新节点的next 指向 head节点
        self.head=node       # head节点 重新被 node节点取代
         
    def append(self,item):
        '链表尾部添加元素'
        node=Node(item)
        
        # 1. 判断是否是头结点
        if self.head==None:
            retrun self.head=node
        # 2. 循环到最后一个节点,需要pre     
        cur = self.head
        pre=None
        while cur:
            pre=cur
            cur=cur.next
            
        pre.next=node
        
    def search(self,item): # 查找
        find=False
        cur=self.head
        while cur:
            if cur.item==item:
                find=True
                retrun
            cur=cur.next
        return find
    
    # 指定位置增加
    def insert(self,pos,item):
        node=Node(item)
        if pos==0:
            self.add(item)
        
        cur=self.head
        pre=None
        
        for i in range(pos) # 循环到指定的位置
            pre=cur
            cur=cur.next
        # 批注: 此时 可以获得上一个节点和当前节点
        pre.next=node # 上一个节点的next 指向新的节点
        node.next=cur # 新节点的next 执行那个cur(当前的节点)
    
    def remove(self,item):
        cur=self.head
        pre=None
        
        # 1. 删除的值是第一个节点
        if self.head.item==item:
            self.head=self.head.next
            return
        # 2. 删除的值不是第一个节点
        while cur:
            if cur.item==item: # 
                pre.next=cur.next
                return
            pre=cur
            cur=cur.next
                
            
        
    
    def is_empty(self):
        retrun self.head==None
    
    def length(self):
        count=0   # 计数
        cur = self.head # 当前节点
        while cur:
            count+=1  # 自增
            cur=cur.next 
         return cur
         
    def travel(self):
        '遍历'
        cur = self.head  # 记录当前节点
        while cur: # 当前节点为空,则说明指向了最后,也就是完成了循环遍历
            print(cur) # 输出值
            cur=cur.next
         return cur 

十丶二分查找

# 1.必须是有序的
# 2.对半砍, 时间复杂读nlogn
def search(data,item):
    start=0
    end=len(data)-1
    
    while start <= end:
        mid=(start+end)//2
        if data[mid]==item:
            return True
        elif data[mid]>item:
            end=mid-1
        elif data[mid]<item:
            start=mid+1
    
data=[i for i in range(1, 10000)]
print(search(data,33))

十一丶二叉树排序

### 分为:
    # 普通二叉树
    # 排序二叉树
# 包含:
    # 1. 根节点
    # 2. 左右叶子节点
    # 3. 子树:完整的子树和非完整的子树
        - 每一颗子树根节点可以作为另一颗子树的左右叶子节点
        - 每一个根节点都可以做为一颗子树的根节点
        
# 普通二叉树实现
class Node():
    def __init(self,item):
        self.item=item
        self.left=None
        self.right=None

class Tree():
    def __init__(self):
        self.root=None
    
    # 插入: 只在空的地方插入值
    def insert(self,item):
        # 1.实例化一个node节点
        node=Node(item)
        # 2. 第一个位置是否为空
        if self.root==None:
            self.root=node
            return 
        # 3. 循环 , 并且判断哪个位置是空的
            # 思路: 拿到每一个节点,放入列表里,如果这个节点存在左右叶子节点,把左右叶子节点添加queue列表中,如果这个节点的左或右节点为空,即可插入到这个位置.
        
        cur=self.root # 拿到第一个节点
        queue=[cur]
        while True:
            leaf=self.queue.pop(0)# 从列表中获得第一个元素
             if leaf.left==None:
                # 左插入
                leaf.left=node
                break
             else:
                # 当前节点的左节点都不为空 , 把左节点添加到列表中
                queue.append(leaf.left)
                
             if leaf.right==None:
                # 右插入
                leaf.right=node
                break
             else:
                # 当前节点的右节点都不为空,把右节点添加到列表中
                queue.append(leaf.right)
             
            
        # 遍历(广度遍历:一行一行来)
        def travel(self):
            cur=self.root
            queue=[cur]
            while queue: # 当列表为空时,说明遍历完了
                leaf=queue.pop(0)
                print(leaf.item) # da
                if leaf.left!=None:
                    queue.append(leaf.left)
                 if leaf.right!=None:
                     queue.append(leaf.right)
            
# 二叉树如的遍历
    # 广度遍历:横向
    # 深度遍历: 纵向.  一定是作用在每一颗子树
        # 前序遍历: 根左右
        # 中序遍历: 左根右
        # 后序遍历: 左右根

# 前序遍历
def front_sort(self,root): 
    # 根->左->右
    # 利用递归的特性: 
    if root==None:
        return
    print(root.item)
    self.front_sort(root.left)
    self.front_sort(root.right)
    
    
# 中序遍历
def middle_sort(self,root):
    if root==None:
        return
    self.middle_sort(root.left)
    print(root.item)
    self.middle_sort(root.right)
    
    
# 后序遍历
def after_sort(self,root):
    if root==None:
        return
    self.after_sort(root.left)
    self.after_sort(root.right)
    print(root.item)
#### 排序二叉树 
    # 当向排序二叉树中插入节点的时候,让节点和树的根节点进行大小比较。比根节点大的节点需要插入到树的右侧,否则插入到树的左侧
    # 特点: 左边的都比根节点小, 右边的都比根节点大

class Node():
    def __init__(self,item):
        self.item=item
        self.left=None
        self.right=None
class SortTree():
    def __init__(self):
        self.root=None
    
    # 有序插入
    def inert(self,item):
        node=Node(item)
        if self.root==None:
            self.root=node
            
        cur=self.root
        while True:
            # a,b,c,e节点为例: a是root根节点, 1.当b节点小于a节点,往根节点的左边进行插入,如果左边的值不为None,则更改根节点为b.(利用局部子树的原理实现)
            if cur.left>item: # 往左插入
                if cur.left==None:
                    cur.left=node
                    return
                else:
                    cur=cur.left
            else: # 往右插入
                if cur.right==None:
                    cur.right=None:
                    return
                else:
                    cur=cur.right
                

十二丶排序算法

# 冒泡
def sort(data):
    for i in range(len(data) - 1):
        for j in range(len(data) - 1 - i):
            if data[j] > data[j + 1]:
                data[j], data[j + 1] = data[j + 1], data[j]

    return data
# 选择
def sort(data):
    for i in range(len(data)-1):
        max_index=0
        for j in range(len(data)-1-i):
            if data[max_index]<data[j+1]:
                max_index=j+1
            data[max_index],data[len(data)-1-i]=data[len(data)-1-i],data[max_index]
    return data
# 插入
def sort(data):
    for i in range(1,len(data)): #   从数据的第二个位置开始进行, 当i=0时,没有必要进行一个比较,有序的列表里只有一个元素.
        while i >0:
            if data[i]>data[i-1]:
                data[i],data[i-1]=data[i-1],data[i]
                i-=1
            else:
                break
     return data
            
# 快排 
    # : 递归+二分
def sort(data):
    first=0
    end=len(data)-1
   
# 希尔排序: 插入排序就是增量为1的希尔排序
    ### 增量: 
        # 初始值: 元素个数整除2
        # 增量表示分组的组数

def sort(data):
    gap=len(data)//2

    while gap>0: # 判断增量 ,当增量为 1时,则是插入排序
        for i in range(1,len(data)):
            while i>0:
                if data[i]<data[i-gap]:
                    data[i],data[i - gap]=data[i-gap],data[i]
                    i-=gap
                else:
                    break
        gap//=2
    return data
data=[8,5,2,1,3,7,6,4,9]
print(sort(data))          
# 快排
    设定一个基数mid,基数的初始值就是乱序序列中的第一个元素值
    将除了基数剩下所有的数值跟基数做比较。比较之后需要达到的一个效果就是:
    基数左侧放置的都是比它小的数
    基数右侧放置的都是比它大的数
    
#   
def sort(data,start,end):
    low=start
    high=end


    if low>high: # 结束递归的条件
        return

    mid=data[start]
    while low<high:
        # 从右开始
        while low<high:
            if data[high]<mid:
                # 往左移动
                data[low]=data[high]
                break
            else:
                high-=1

        while low<high:
            if data[low]<mid:
                low+=1
            else:
                data[high]=data[low]
                break

        if low==high:
            data[high]=mid

    sort(data,start,high-1)
    sort(data,high+1,end)

    return data



alist = [6  ,1,  2, 7,  9,  3 , 4,  5, 10,  8]
print(sort(alist,0,len(alist)-1))

猜你喜欢

转载自www.cnblogs.com/dengl/p/11967813.html