zoukankan      html  css  js  c++  java
  • 《算法篇章》---学习笔记

    算法

    一丶时间复杂度

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

    时间复杂度排序

    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))
    

    六丶堆排序

    ### 堆排序
    # 堆调整
    def sift(data,low,high):
    	i =low   # i 是根节点
        j=2*i+1	  # 获得左孩子节点
        tmp=data[i]  # 获得根节点的值
        while  j<=high:
            if j<high and data[j]<data[j+1] # j<high是判断是否有右孩子节点, data[j]<data[j+1]右孩子大于左孩子,
            	j+=1		# j指向右孩子
            if data[j] > tmp :  # 判断孩子比最高的领导大
                data[i]=data[j]	# 孩子填充到父亲的空位上
                i=j				# 孩子成为新父亲
                j=2*i+1			# j成为新孩子
            else:
                break
        data[i]=tmp  # 最高领导放到父亲位置
        
        
    def heap_sort(data):
        n=len(data)
        for i in range(n//2-1,-1,-1): # 找到最后的堆的领导,
            sift(data,i,n-1) # 调整整个堆的大小
        for j in range(n-1,-1,-1): # 最大值退休
            data[0],data[j]=data[j],data[0] # 退休后,存放在最后的位置 ,堆就少一层
            sift(data,0,j-1) # 重新调整堆的顺序.
    
    ### 归并排序
    def megre(li,low,mid,high)
    	i=0    # 第一个指针
        j=mid+1 # 第二个指针
        li_data=[]
        while i<=mid and j<=high: # 两边都有数
            if li[i]<li[j]:
            	li_data.append(li[i])
                i+=1
            else:
                li_data.append(li[j])
                j+=1
        while i<=mid:
        	li_data.append(li[i])
            i+=1
        while j<=hight:
            li_data.append(li[j])
            j+=1
            
        li[low:hiht+1]=li_data # 替换
        
    def megre_sort(li,low,high):
        if low <high:
            mid=(low+high)//2
            megre_sort(li,low,mid)  # 递归去拆分数据
            megre_sort(li,mid+1,high) # 递归去拆分数据
            megre(li,low,mid,high)
    
    #### 快速排序   堆排序  归并排序 对比
    	# 三种算法的时间复杂度都是O(nlogn)
    	# 运行时间:快速<归并<堆排
    	# 特点:	
    		# 快速排序: 极端情况下的效率极低
    		# 归并排序:需要额外的内存,当数据量大时,不得不考虑内存的空间
    		# 堆排序: 速度相对较慢,但是稳定
    
  • 相关阅读:
    洛谷 P1208 [USACO1.3]混合牛奶 Mixing Milk( 普及-)
    洛谷 P1909 [NOIP2016 普及组] 买铅笔
    全排列问题
    集合的划分(setsub)
    自然数拆分-回溯
    洛谷 P4414 [COCI2006-2007#2] ABC
    洛谷 P5709 【深基2.习6】Apples Prologue
    洛谷 P4326 [COCI2006-2007#1] Herman
    平面分割
    洛谷 P1601 A+B Problem(高精)
  • 原文地址:https://www.cnblogs.com/dengz/p/11967813.html
Copyright © 2011-2022 走看看