算法学习

  • 博客地址:https://www.cnblogs.com/bobo-zhang/p/10514873.html

  • 算法

    • 所谓的算法就是对问题进行处理且求解的一种实现思路或者思想。

    • 案例: a+b+c = 1000 a2 + b2 = c**2 (a,b,c均为自然数),求出a,b,c可能的组合?

    • for a in range(0,1001):
          for b in range(0,1001):
              for c in range(0,1001):
                  if a**2+b**2 == c**2 and a+b+c==1000:
                      print(a,b,c)
      
    • for a in range(0,1001):
          for b in range(0,1001):
              c = 1000 - a - b
              if a**2+b**2 == c**2 and a+b+c==1000:
                      print(a,b,c)
      
    • 如何取衡量一个算法性能的好坏优劣?

      • 计算算法执行的耗时
      • 量化算法执行消耗计算机的资源大小
      • 时间复杂度(推荐)
    • 时间复杂度

      • 量化执法执行步骤的数量。

      • 案例:

      • 1 a=5
         2 b=6
         3 c=10
         4 for i in range(n):
         5    for j in range(n):
         6       x = i * i
         7       y = j * j
         8       z = i * j
         9 for k in range(n):
        10    w = a*k + 45
        11    v = b*b
        12 d = 33
        
        • 执行步骤:4+2n+3n**2

        • 时间复杂度的表示方法:大O记法

          • 将执行步骤数量表示的表达式中最有意义的一项取出,放置在大O后面的括号即可。

          • 4+2n+3n**2最有意义的一项是 n**2,大O记法:O(n**2)
            
          • 常见的时间复杂度: O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

  • 数据结构

    • 对组织方式就被称作为数据结构 。或者,认为所有不同形式的数据结构都可以表示一种容器,容器是用来装载数据。

    • 案例: 需要存储一些学生的学生信息(name,score),那么这些数据应该如何组织呢?查询某一个具体学生的时间复杂度是什么呢?

    • #方式1
      [{'name':'zhangsan','score':100},
       {'name':'lisi','score':99}
      ] 
      
      #方式2
      [('zhangsan',100),
       ('lisi',99)
      ] 
      
      #方式3
      {'zhangsan':{'score':100},
       'lisi':{'score':99}
      } 
      
    • 需求:对指定学生的成绩进行查询?上述的数据结构那种形式是最好的?

      • 方式3最好,因为时间复杂度为O(1),剩下两个为O(n)
    • 算法和数据结构之间的关系?

      • 因此认为算法是为了解决实际问题而设计的,数据结构是算法需要处理问题的载体 。
  • 栈:先进后出

    • 栈顶,栈底
    • 元素的添加和提取都是从栈顶向栈底的方向进行。
  • 案例:浏览器的回退按钮。

  • 实现:

  • class Stack():
    	def __init__(self):
    		self.items = [] #构建一个空栈
    	def push(self,item): #添加元素
    		self.items.append(item)
    	def pop(self):#取出元素
    		return self.items.pop()
    	def isEmpty(self):
    		return self.items == []
    	def length(self):
    		return len(self.items)
    
    
    s = Stack()
    s.push(1)
    s.push(2)
    s.push(3)
    
    for i in range(s.length()):
    	print(s.pop())
    
    
  • 队列:先进先出

    • 队头,队尾
    • 元素必须是从队尾向队头的方向进行添加,且必须从队头以此取出元素。
  • 案例:一个教室有10台电脑和一台打印机联网。电脑给打印机发送的打印任务必须放置在队列中。

  • 队列的实现:

  • class Queue():
    	def __init__(self):
    		self.items = []
    	def enqueue(self,item):#添加元素
    		self.items.insert(0,item)
    	def dequeue(self):#取出元素
    		return self.items.pop()
    	def isEmpty(self):
    		return self.items == []
    
  • 面试题:烫手的山芋

    • 6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。

    • 找已知的条件:

      • 把圈屡直了就是一个队列的结构
        • 只可以从队头取元素,从队尾添加元素
      • 山芋是1s中向后传递一次
      • 一轮游戏是7s的时长,一轮游戏山芋需要被传递6次
      • 当队列中只剩下一个孩子的时候游戏结束
      • 核心:必须要让手中有山芋的孩子作为队头元素!
        • 如何实现:山芋不动,人动
    • class Queue():
      	def __init__(self):
      		self.items = []
      	def enqueue(self,item):#添加元素
      		self.items.insert(0,item)
      	def dequeue(self):#取出元素
      		return self.items.pop()
      	def isEmpty(self):
      		return self.items == []
      	def length(self):
      		return len(self.items)
      
      
      kids = ['A','B','C','D','E','F']
      queue = Queue()
      for kid in kids:#将孩子入队列
      	queue.enqueue(kid)
      
      while queue.length() > 1: #多轮游戏进行时
      	#实现一轮游戏策略
      	for i in range(6): #一轮游戏山芋被传递的次数
      		item = queue.dequeue()
      		queue.enqueue(item)
      	queue.dequeue() #一轮游戏后将队头元素淘汰
      
      print('最终的获胜者是:',queue.dequeue())
      
    • 如何使用两个队列实现一个栈?(使用两个先进先出的数据结构实现一种先进后出的效果)

      • q1 = Queue()
        q2 = Queue()
        #数据存储到q1中
        alist = [1,2,3,4,5]
        for item in alist:
        	q1.enqueue(item)
        
        while q1.length() >= 1:
        	for i in range(q1.length()-1):
        		item = q1.dequeue()
        		q2.enqueue(item)
        	print(q1.dequeue())
        	q1,q2 = q2,q1
        
    • 链表

      • 计算机本质作用是什么?

        • 存储和运算二进制的数据!
      • 变量表示的是什么?

        • 面向对象的语言中所有的变量都是引用。

        • a = 10表示什么?

          • 在计算机的内存中开启一块内存空间,然后将10这个数据存储到该块内存空间中。a就是用来引用这个块内存空间的。通俗理解,所谓的变量或者引用表示的就是计算机内存中某一块内存空间。
          • 在计算机中开辟的每一块内存空间都拥有两个默认的属性:
            • 大小:决定可以存储最大的数据范围是什么
              • bit位:只能存放以为二进制的数。最大可以存储的数据是1,最小是0
              • byte字节:8bit。
            • 地址:用来让cpu寻址
          • 指向:如果一个变量引用了某一块内存空间,则该变量指向该块内存空间。
        • 计算机在为数据开辟内存的时候,内存空间大小是根据数据实际值的大小开辟呢还是统一大小开辟呢?

          • 统一开辟固定大小的内存
            • 整型数据:4字节
            • 字符串:一个字符一个字节
            • 小数:4、8字节
        • 为什么要有链表数据结构呢?

          • 顺序表:内存开辟是连续
            • 集合中存储的元素是有顺序的。顺序表的结构可以分为两种形式:单数据类型和多数据类型。
          • 弊端: 顺序表的结构需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁。
        • 实现:

        • class Node():
          	#构建一个节点
          	def __init__(self,item):
          		self.item = item
          		self.next = None
          
          class Link():
          	def __init__(self): #构建空链表
          		self.head = None
          	def add(self,item):#向链表头部插入节点:类比于列表的insert(0,item)
          		node = Node(item)
          		node.next = self.head
          		self.head = node
          
          	def travle(self):
          		# print(self.head.item)
          		# print(self.head.next.item)
          		# print(self.head.next.next.item)
          		cur = self.head  #head要永远指向头结点
          		while cur:
          			print(cur.item)
          			cur = cur.next
          	def isEmpty(self):
          		return self.head == None
          
          	def size(self):
          		cur = self.head
          		count = 0
          		while cur:
          			count += 1
          			cur = cur.next
          		return count
          	def append(self,item): #向链表尾部添加新的节点
          		node = Node(item)
          		if self.isEmpty():#链表为空
          			self.head = node
          			return
          		#链表为非空
          		pre = None #pre永远指向cur前一个节点
          		cur = self.head
          		while cur:
          			pre = cur
          			cur = cur.next
          		pre.next = node
          
          	def insert(self,pos,item):#向pos表示的指定位置添加节点
          		if pos == 0:
          			self.add(item)
          			return
          		node = Node(item)
          		#pos的位置的值刚好可以作为pre和cur向后偏移次数的值
          		pre = None
          		cur = self.head
          		for i in range(pos):
          			pre = cur
          			cur = cur.next
          		pre.next = node
          		node.next = cur
          
          	def remove(self,item): #删除item表示的节点
          		pre = None
          		cur = self.head
          		#如果删除的是第一个节点
          		if item == self.head.item:
          			self.head = self.head.next
          			return
          		while cur:
          			pre = cur
          			cur = cur.next
          			if cur:
          				if cur.item == item:
          					pre.next = cur.next
          					return
          
  • 博客地址:https://www.cnblogs.com/bobo-zhang/p/10514873.html

  • 链表节点倒置:

  • 	def reverse(self):
      		pre = None
      		cur = self.head
      		next_node = cur.next
      
      		while cur:
      			cur.next = pre
      			pre = cur
      			cur = next_node
      			if cur:
      				next_node = next_node.next
      		self.head = pre
    

二叉树

  • 全局根节点:树状结构中最上层的那一个节点
  • 左右叶子节点:左右分支对应的节点
  • 子树:
    • 完整的子树
      • 根节点和左右叶子节点组成的
    • 非完整的子树
      • 根节点+左叶子节点
      • 根节点+右叶子节点
      • 根节点
    • 如何区分不同的子树?
      • 根据根节点可以区分不同的子树
        • 一个子树的子节点可以作为另一颗子树的根节点
class Node():
	def __init__(self,item):
		self.item = item
		self.left = None
		self.right = None

class Tree():
	def __init__(self):
		self.root = None
	def insertNode(self,item):
		node = Node(item)
		#树为空的情况
		if self.root == None:
			self.root = node
			return
		#树为非空的情况
		cur = self.root
		queue = [cur]
		while queue:
			pop_node = queue.pop(0)
			if pop_node.left != None:
				queue.append(pop_node.left)
			else:
				pop_node.left = node
				break
			if pop_node.right != None:
				queue.append(pop_node.right)
			else:
				pop_node.right = node
				break

	def travel(self):
		cur = self.root
		queue = [cur]
		while queue:
			pop_node = queue.pop(0)
			print(pop_node.item)
			if pop_node.left != None:
				queue.append(pop_node.left)
			if pop_node.right != None:
				queue.append(pop_node.right)

	def findNode(self,item):
		find = False
		cur = self.root
		queue = [cur]
		while queue:
			pop_node = queue.pop(0)
			if pop_node.item == item:
				find = True
				break
			else:
				if pop_node.left != None:
					queue.append(pop_node.left)
				if pop_node.right != None:
					queue.append(pop_node.right)
		return find

	def changeValueOfNode(self,item,new_value):
		cur = self.root
		queue = [cur]
		while queue:
			pop_node = queue.pop(0)
			if pop_node.item == item:
				pop_node.item = new_value
				break
			else:
				if pop_node.left != None:
					queue.append(pop_node.left)
				if pop_node.right != None:
					queue.append(pop_node.right)
  • 二叉树的遍历方式:

    • 广度遍历:逐层遍历

    • 深度遍历:竖直方向的遍历。让前中后序作用在每一颗子树中返回的遍历结果。

      • 前序:根左右
      • 中序:左根右
      • 后序:左右根
    • 	def forward(self,root): #root就是不同子树的根节点
        		if root == None:
        			return
        		print(root.item)
        		self.forward(root.left)
        		self.forward(root.right)
        	def middle(self,root):
        		if root == None:
        			return
        		self.middle(root.left)
        		print(root.item)
        		self.middle(root.right)
        	def back(self,root):
        		if root == None:
        			return
        		self.back(root.left)
        		self.back(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 addNode(self,item):
        		node = Node(item)
        		if self.root == None:
        			self.root = node
        			return
        		cur = self.root
        
        		while True:
        			if cur.item < item: #插入节点大于根节点,往根节点右侧插入
        				#判断根节点的右侧子节点位置是否为空
        				if cur.right == None:
        					cur.right = node
        					break
        				else:
        					cur = cur.right
        			else:#插入节点的值小于等于根节点,往根节点左侧插入
        				if cur.left == None:
        					cur.left = node
        					break
        				else:
        					cur = cur.left
        
        	def middle(self,root):
        		if root == None:
        			return
        		self.middle(root.left)
        		print(root.item)
        		self.middle(root.right)
        
        tree = SortTree()
        alist = [3,8,5,7,6,2,1]
        for item in alist:
        	tree.addNode(item)
        
        tree.middle(tree.root)
        
        
        

      二分查找

      • 前提:进行查找的序列必须是有序。

      • def b_find(alist,item):#在alist列表中查找item是否存在
        	find = False
        	left = 0 #序列第一个元素下标
        	right = len(alist)-1 #序列最后一个元素下标
        
        	while left <= right:
        		mid = (left+right)//2 #序列中间元素下标
        		if alist[mid] > item: #中间元素大于要查找的元素,则要查找的元素可能在中间元素左侧
        			right = mid - 1
        		elif alist[mid] < item:#查找的元素大于中间元素,则查找的元素在中间元素右侧
        			left = mid + 1
        		else:#查找的元素等于中间元素
        			find = True
        			break
        	return find
        
        alist = [1,2,3,4,5,6,7]
        print(b_find(alist,7))
        

    排序算法

    • 冒泡排序

      • 将乱序序列两两比较,逐步将最大值移动到序列最后位置

        • def sort(alist):
          	for i in range(len(alist)-1):#控制两两元素比较的次数
          		if alist[i] > alist[i+1]:
          			alist[i],alist[i+1] = alist[i+1],alist[i]
          	return alist
          
      • 将上述操作重复作用在其他乱序的部分,直到整个序列变为有序序列

        • def sort(alist):
          	for j in range(len(alist)-1):
          		for i in range(len(alist)-1-j):#控制两两元素比较的次数
          			if alist[i] > alist[i+1]:
          				alist[i],alist[i+1] = alist[i+1],alist[i]
          	return alist
          
    • 选择排序

      • 直接将乱序序列的最大值找出,和序列中最后一个元素交换位置

        • def sort(alist):
          	max_index = 0 #保存序列中最大元素的下标
          	for i in range(len(alist)-1):
          		if alist[i+1] > alist[max_index]:
          			max_index = i+1
          	alist[max_index],alist[len(alist)-1] = alist[len(alist)-1],alist[max_index]
          	return alist
          
          
          alist = [3,8,5,7,6,2,1]
          print(sort(alist))
          
      • 将上述操作重复作用在剩下乱序序列中

        • def sort(alist):
          	for j in range(len(alist)-1):
          		max_index = 0 #保存序列中最大元素的下标
          		for i in range(len(alist)-1-j):
          			if alist[i+1] > alist[max_index]:
          				max_index = i+1
          		alist[max_index],alist[len(alist)-1-j] = alist[len(alist)-1-j],alist[max_index]
          	return alist
          
    • 插入排序

      • 将乱序序列分为两部分:

        • 有序部分:默认情况序列中的第一个元素
        • 无序部分:默认情况下序列中最后n-1个元素
        • 核心:将无序部分的每一个元素以此插入到有序部分合适的位置
      • [3,   8,5,7,6]
        [3,8,   5,7,6]
        [3,5,8,   7,6]
        [3,5,7,8,   6]
        [3,5,6,7,8   ]
        
      • 设计一个变量i,该变量表示有序部分元素的个数,i的初始值一定是1.

      • i = 1时,对应的插入情况的实现:

        • #i = 1的情况
          #alist[i-1]:有序部分最后一个元素
          #alist[i]:无序部分的第一个元素
          if alist[i] < alist[i-1]:
          	alist[i],alist[i-1] = alist[i-1],alist[i]
          
      • i = 2或者i>=2的情况

        • #i = 2的情况
          #alist[i-1]:有序部分最后一个元素
          #alist[i]:无序部分的第一个元素
          while i >= 1:
          	if alist[i] < alist[i-1]:
          		alist[i],alist[i-1] = alist[i-1],alist[i]
          		i -= 1
          	else:
          		break
          
      • 完整代码:

      • def sort(alist):
        	for i in range(1,len(alist)):
        		while i >= 1:
        			if alist[i] < alist[i - 1]:
        				alist[i], alist[i - 1] = alist[i - 1], alist[i]
        				i -= 1
        			else:
        				break
        	return alist
        alist = [3,8,5,7,6,2,1]
        print(sort(alist))
        
    • 希尔排序

      • 增量(gap)
        • 初始值:乱序序列元素个数整除2
        • 作用:
          • gap的值表示的是分组后的组数
          • gap可以表示每组数据之间的间隔
      • 理解:插入排序就是增量为1的希尔排序
    • #在插入排序的基础之上加入增量的概念
      def sort(alist):
      	gap = len(alist) // 2
      	for i in range(gap,len(alist)):
      		while i >= gap:
      			if alist[i] < alist[i - gap]:
      				alist[i], alist[i - gap] = alist[i - gap], alist[i]
      				i -= gap
      			else:
      				break
      	return alist
      
    • 完整:加入增量缩减的操作

    • def sort(alist):
      	gap = len(alist) // 2
      	while gap >= 1:
      		for i in range(gap,len(alist)):
      			while i >= gap:
      				if alist[i] < alist[i - gap]:
      					alist[i], alist[i - gap] = alist[i - gap], alist[i]
      					i -= gap
      				else:
      					break
      		gap //= 2
      	return alist
      alist = [3,8,5,7,6,2,1]
      print(sort(alist))
      
  • 快速排序

    • 定义概念:基数

    • 作用:

      • 初始值:序列中第一个元素

      • 最终实现的效果:不断的移动元素,实现将序列中比基数小的元素移动到基数左侧,否则移动到基数右侧

      • def sort(alist):
        	low = 0
        	high = len(alist)-1
        
        	mid = alist[low]
        	while low < high:
        		#让high向左移动
        		while low < high:
        			if mid < alist[high]:
        				high -= 1
        			else:
        				alist[low] = alist[high]
        				break
        		#让low向右移动
        		while low < high:
        			if alist[low] < mid:
        				low += 1
        			else:
        				alist[high] = alist[low]
        				break
        	if low == high:
        		alist[low] = mid
        		return alist
        
      • 完整操作:

      • def sort(alist,start,end):
        	low = start
        	high = end
        	#结束递归的条件
        	if low > high:
        		return
        
        	mid = alist[low]
        	while low < high:
        		#让high向左移动
        		while low < high:
        			if mid < alist[high]:
        				high -= 1
        			else:
        				alist[low] = alist[high]
        				break
        		#让low向右移动
        		while low < high:
        			if alist[low] < mid:
        				low += 1
        			else:
        				alist[high] = alist[low]
        				break
        	if low == high:
        		alist[low] = mid
        
        	#递归将上述核心操作作用到基数左侧的子序列中
        	sort(alist,start,low-1)
        	#递归将上述核心操作作用到基数右侧子序列中
        	sort(alist,high+1,end)
        	return alist
        
posted @   vetra  阅读(74)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示