一. 算法概念
- 含义: 算法就是对问题进行处理且求解的一种实现思路或者思想.
评判程序优劣的方法
- 消耗计算机资源和执行效率(无法直观)
计算算法执行的耗时(不推荐, 因为会受机器和执行环境的影响)
时间复杂度(推荐)
时间复杂度
- 评判规则: 量化算法执行的操作 / 执行步骤的数量,
- 如下列
- def sumOfN(n):
- # 执行一步:
- theSum = 0
- for i in range(1,n+1):
- # 下式一共执行 n 步, for 循环不算一步, 因为它是控制循环次数的
- theSum = theSum + i
- # return 又执行一步
- return theSum
- # 调用函数, 计算执行步骤为: n+2
- sumOfN(10)
- 最重要的项: 时间复杂度表达式中最有意义的项
- 使用大 O 记法来表示时间复杂度
- O(最重要的项)
- # 如上式: 最有意义的项就是 n, 所以 n+2 ==>O(n)
常见的时间复杂度:
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)) 的组织方式就被称作为数据结构. 数据结构解决的就是一组数据如何进行保存, 保存形式是怎样的.
- 使用不同的形式组织数据, 在基于查询时的时间复杂度是不一样的. 因此认为算法是为了解决实际问题而设计的, 数据结构是算法需要处理问题的载体.
平均耗时
- timeit 模块: 该模块可以用来测试一段 python 代码的执行速度 / 时长.
- Timer 类: 该类是 timeit 模块中专门用于测量 python 代码的执行速度 / 时长的. 原型为: class -
- timeit.Timer(stmt='pass',setup='pass').
stmt 参数: 表示即将进行测试的代码块语句.
setup: 运行代码块语句时所需要的设置.
timeit 函数: timeit.Timer.timeit(number=100000), 该函数返回代码块语句执行 number 次的平均耗时.
例子:
- from timeit import Timer
- def func():
- a = []
- for i in range(10000):
- a.append(i)
- return a
- def func1():
- a = [i for i in range(10000)]
- return a
- if __name__ == '__main__':
- # 计算 func1 的平均耗时
- time_func = Timer("func1()","from __main__ import func1")
- t1 = time_func.timeit(1000)
- print(t1)
二. 栈与队列
1. 栈
定义与方法
- 特性: 先进后出的数据结构, 分栈顶和栈尾
- 相关的方法(待使用列表实现)
Stack() 创建一个空的新栈. 它不需要参数, 并返回一个空栈.
push(item)将一个新项添加到栈的顶部. 它需要 item 做参数并不返回任何内容.
pop() 从栈中删除顶部项. 它不需要参数并返回 item . 栈被修改.
peek() 从栈返回顶部项, 但不会删除它. 不需要参数. 不修改栈.
isEmpty() 测试栈是否为空. 不需要参数, 并返回布尔值.
size() 返回栈中的 item 数量. 不需要参数, 并返回一个整数.
方法的实现(列表)
- class Stack(object):
- def __init__(self):
- self.items = []
- # 栈顶添加
- def push(self,item):
- self.items.append(item)
- # 栈顶删除并返回元素
- def pop(self):
- return self.items.pop()
- # 返回栈顶元素索引
- def peek(self):
- return len(self.items) - 1
- # 判断栈是否为空
- def isEmpty(self):
- return self.items == []
- # 返回栈的总长度
- def size(self):
- return len(self.items)
2. 队列
单端队列
定义与方法
- 特性: 先进先出
- 方法:
Queue() 创建一个空的新队列. 它不需要参数, 并返回一个空队列.
enqueue(item) 将新项添加到队尾. 它需要 item 作为参数, 并不返回任何内容.
dequeue() 从队首移除项. 它不需要参数并返回 item. 队列被修改.
isEmpty() 查看队列是否为空. 它不需要参数, 并返回布尔值.
size() 返回队列中的项数. 它不需要参数, 并返回一个整数.
方法的实现
- 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 size(self):
- return len(self.items)
应用案例: 烫手山芋
- '''
- 案例: 烫手的山芋
- 烫手山芋游戏介绍: 6 个孩子围城一个圈, 排列顺序孩子们自己指定. 第一个孩子手里有一个烫手的山芋, 需要在计时器计时 1 秒后将山芋传递给下一个孩子, 依次类推. 规则是, 在计时器每计时 7 秒时, 手里有山芋的孩子退出游戏. 该游戏直到剩下一个孩子时结束, 最后剩下的孩子获胜. 请使用队列实现该游戏策略, 排在第几个位置最终会获胜.
- 准则: 手里有山芋的孩子永远排在队列的头部
- '''
- # 代码实现
- kids = ['A','B','C','D','E','F']
- queue = Queue()
- for kid in kids:
- queue.enqueue(kid) #A 对头 F 队尾
- while queue.size()> 1:
- for i in range(6): #每循环一次, 山芋传递一次, 手里有山芋的孩子永远在对头位置
- kid = queue.dequeue()
- queue.enqueue(kid)
- queue.dequeue()
- print('获胜的选手是:',queue.dequeue())
双端队列
- 同同列相比, 有两个头部和尾部. 可以在双端进行数据的插入和删除, 提供了单数据结构中栈和队列的特性
- 方法:
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):
- self.items.insert(0,item)
- def addRear(self,item):
- self.items.append(item)
- def removeFront(self):
- return self.items.pop()
- def removeRear(self):
- return self.items.pop(0)
- def isEmpty(self):
- return self.items == []
- def size(self):
- return len(self.items)
应用案列: 回文检查
- def isHuiWen(s):
- ex = True
- # 实例化
- q = Deque()
- # 循环添加元素至双端队列中
- for ch in s:
- q.addFront(ch)
- while q.size()> 1:
- # 判断从后面删除的的元素是否等于从前方删除的元素
- if q.removeFront() != q.removeRear():
- ex = False
- break
- return ex
三. 内存相关概念
计算机的作用
用来存储和运算二进制的数据
计算机如何计算 1+2?
将 1 和 2 的二进制类型的数据加载到计算机的内存中, 然后使用寄存器进行数值的预算.
变量的概念
变量就是某一块内存
引用: 变量 ==》内存空间的地址
指向: 如果变量或者引用表示的是某一块内存空间地址的话, 则该变量或者该引用指向了该块内存
内存空间是有两个默认的属性:
内存空间的大小
bit(位): 一个 bit 大小的内存空间只能存放一位二进制的数
- byte(字节):8bit
- kb:1024byte
内存空间的地址
使用一个十六进制的数值表示
作用: 让 cup 寻址
四. 顺序表和链表
1. 顺序表
- 集合中存储的元素是有顺序的, 顺序表的结构可以分为两种形式: 单数据类型和多数据类型
- python 中的列表和元组就属于多数据类型的顺序表(多数据类型顺序表的内存图(内存非连续开辟))
- 缺点 ***:
顺序表的结构需要预先知道数据大小来申请连续的存储空间, 而在进行扩充时又需要进行数据的搬迁.
2. 链表
定义与方法:
- 链表是一种线性表, 但是不像顺序表一样连续存储数据, 而是每一个结点 (数据存储单元) 里存放下一个结点的信息(即地址)
- 方法:
. is_empty(): 链表是否为空
. length(): 链表长度
. travel(): 遍历整个链表
. add(item): 链表头部添加元素
. append(item): 链表尾部添加元素
. insert(pos, item): 指定位置添加元素
. remove(item): 删除节点
. search(item): 查找节点是否存在
实现方法:
- # 定义节点类
- class Node():
- def __init__(self,item):
- self.item = item # item 用来存值
- self.next = None # next 用来指向下一个节点内存地址
- # 定义一个链表类
- class Link():
- # 构造出一个空链表
- def __init__(self):
- #_head 存储的只能是空或者第一个节点的地址
- self._head = None
- # 向链表的头部插入一个节点
- def add(self,item):
- #创建一个新的节点
- node = Node(item)
- # 新节点的 next 指向原头部节点指向的内存地址
- node.next = self._head
- # 头部节点指向新创建的 node 的内存地址
- self._head = node
- # 遍历整个链表
- def travel(self):
- #_head 在链表创建好之后一定是不可变
- cur = self._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._head == None:
- self._head = node
- return
- cur = self._head
- pre = None# pre 指向的是 cur 前一个节点
- 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
- break
- cur = cur.next
- return find
- # 向某个位置插入
- def insert(self,pos,item):
- node = Node(item)
- 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):
- cur = self._head
- pre = None
- #删除的是第一个节点
- if cur.item == item:
- self._head = cur.next
- return
- while cur:
- '''
- 如果当前节点的值与所搜寻的值相等, 则前一节点的 next 指向当前节点的 next 属性指向的内存地址
- '''
- pre = cur
- cur = cur.next
- if cur.item == item:
- pre.next = cur.next
- return
- if cur.next == None:
- return
五. 排序方法和查找方法
1. 查找方法
顺序查找
- 定义:
当数据存储在诸如列表的集合中时, 我们说这些数据具有线性或顺序关系. 每个数据元素都存储在相对于其他数据元素的位置. 由于这些索引值是有序的, 我们可以按顺序访问它们. 这个过程产实现的搜索即为顺序查找.
- 顺序查找原理剖析:
从列表中的第一个元素开始, 我们按照基本的顺序排序, 简单地从一个元素移动到另一个元素, 直到找到我们正在寻找的元素或遍历完整个列表. 如果我们遍历完整个列表, 则说明正在搜索的元素不存在.
- 代码实现:
该函数需要一个列表和我们正在寻找的元素作为参数, 并返回一个是否存在的布尔值. found 布尔变量初始化为 False, 如果我们发现列表中的元素, 则赋值为 True.
二分查找
前提:
- 一定是基于有序集合的查找!!!
- 有序列表对于我们的实现搜索是很有用的. 在顺序查找中, 当我们与第一个元素进行比较时, 如果第一个元素不是我们要查找的, 则最多还有 n-1 个元素需要进行比较. 二分查找则是从中间元素开始, 而不是按顺序查找列表. 如果该元素是我们正在寻找的元素, 我们就完成了查找. 如果它不是, 我们可以使用列表的有序性质来消除剩余元素的一半. 如果我们正在查找的元素大于中间元素, 就可以消除中间元素以及比中间元素小的一半元素. 如果该元素在列表中, 肯定在大的那半部分. 然后我们可以用大的半部分重复该过程, 继续从中间元素开始, 将其与我们正在寻找的内容进行比较.
代码实现:
- # 二分查找 [1,2,3,4,5,6,7,8,9,10,27,36,46,58,69] - 有序列表
- l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 27, 36, 46, 58, 69, 100]
- def func(lis, n):
- count = 0 # 定义查找次数
- left = 0 # 定义左边界
- right = len(lis) - 1 # 定义右边界
- # 循环查找, 循环条件 左边界 <= 右边界
- while left <= right:
- # 二分: 中间元素位置
- num = (left + right) // 2
- # 如果查找的元素大于左面的最后一个元素, 左边界变为 二分位置 + 1
- if n> lis[num]:
- left = num + 1
- # 如果查找的元素大于左面的最后一个元素, 右边界变为 二分位置 - 1
- elif n <lis[num]:
- right = num - 1
- # 直到找到为止
- else:
- msg = f'位置为 {num}, 查找了{count} 次'
- return msg
- count += 1
- print(func(l1, 5))
2. 七种排序方法
1. 有序链表:
原理:
- 实现链表的有序插入
实现方法:
- # 定义节点类
- class Node():
- def __init__(self,item):
- self.item = item # item 用来存值
- self.next = None # next 用来指向下一个节点内存地址
- # 定义有序链表
- class SortLink():
- def __init__(self):
- self._head = None
- '''基础方法见 4. 顺序表和链表'''
- # 链表的有序插入
- def sortInsert(self,item):
- node = Node(item)
- #插入的是第一个节点
- if self._head == None:
- self._head = node
- return
- cur = self._head
- pre = None
- # 当 item 比第一个节点的值还小时, 插入到第一个节点位置
- if item < cur.item:
- self._head = node
- node.next = cur
- return
- # 循环比较当前节点的值与 item 的大小关系
- while cur:
- pre = cur
- cur = cur.next
- # 如果 cur 后没有节点了, 则把 node 放在最后的位置
- if cur == None:
- pre.next = node
- return
- # 找到当前节点值比 item 大的节点, 把 node 插在当前节点前
- if cur.item> item:
- pre.next = node
- node.next = cur
- return
2. 二叉树排序
二叉树相关概念
- 根节点
- 叶子节点: 左叶子节点, 右叶子节点
- 树的层级, 树的高度
- 二叉树的遍历
1. 广度优先遍历
一层一层对节点进行遍历
2. 深度优先遍历
前序: 根左右
中序: 左根右
后序: 左右根
二叉树实现方法:
类似于链表.
- # 定义节点类
- class Node():
- def __init__(self,item):
- self.item = item
- self.left = None # 左叶子节点
- self.right = None # 右叶子节点
- # 定义二叉树类
- class Tree():
- def __init__(self):
- self.root = None
- def addNode(self,item):
- node = Node(item)
- #如果插入第一个节点的情况
- if self.root == None:
- self.root = node
- return
- cur = self.root
- q = [cur] #列表元素是我们进行遍历判断的节点
- while q:
- nd = q.pop(0)
- if nd.left == None:
- nd.left = node
- return
- else:
- q.append(nd.left)
- if nd.right == None:
- nd.right = node
- return
- else:
- q.append(nd.right)
- def travel(self):
- cur = self.root
- q = [cur]
- while q:
- nd = q.pop(0)
- print(nd.item)
- if nd.left:
- q.append(nd.left)
- if nd.right:
- q.append(nd.right)
- def forwoar(self,root):# 前序: 根左右
- if root == None:
- return
- print(root.item)
- self.forwoar(root.left)
- self.forwoar(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)
- # 使用:
- tree = Tree()
- tree.addNode(1)
- tree.addNode(2)
- tree.addNode(3)
- tree.addNode(4)
- tree.addNode(5)
- tree.addNode(6)
- tree.addNode(7)
- # tree.travel()
- # tree.forwoar(tree.root)
- # tree.back(tree.root)
排序二叉树实现方法:
- # 定义节点类
- class Node():
- def __init__(self,item):
- self.item = item
- self.left = None
- self.right = None
- # 定义排序二叉树类
- class SortTree():
- def __init__(self):
- self.root = None
- # 重点: 添加新节点
- def add(self,item):
- node = Node(item)
- cur = self.root
- # 二叉树还没有节点的情况
- if self.root == None:
- self.root = node
- return
- '''
- 二叉树有节点时循环比较当前节点的值与需要插入的节点的值的大小:
- 1. 比当前节点值大, 向右侧插入, 并判断当前右侧是否为空, 不是则 cur = cur.right
- 2. 比当前节点值小, 向左侧插入, 并判断当前左侧是否为空, 不是则 cur = cur.left
- 3. 循环直到找到左侧或右侧满足条件的某个空位置结束, 调出循环
- '''
- while cur:
- #向右侧插入
- if item> cur.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)
3. 冒泡排序
原理:
- 相邻元素两两比较, 大的元素往后放, 第一完毕后, 最大值就在最大索引处. 然后再继续第二大......, 这样就可得到一个排好序的数组了.
实现方法:
- # 逐渐将乱序序列的最大值找出放置在乱序序列的尾部
- def sort(alist):
- # 外层循环根据列表长度控制循环次数
- for j in range(len(alist)-1):
- # 内层循环根据当前外层循环次数来控制需要比较的次数, 即第 j 大的元素
- 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
- alist = [3,8,5,7,6]
- print(sort(alist))
4. 选择排序
原理:
- 选择排序改进了冒泡排序, 每次遍历列表只做一次交换. 为了做到这一点, 一个选择排序在他遍历时寻找最大的值, 并在完成遍历后, 将其放置在正确的位置.
- 对于冒泡排序来讲选择排序由于交换数量的减少, 选择排序通常在基准研究中执行得更快.
代码实现:
- # 例如:
- alist = [1,3,8,5,2]
- def maopao(alist):
- # 外层循环根据列表长度控制循环冒泡进行次数
- for j in range(0,len(alist)-1):
- max_index = 0 # 当前循环最大元素的索引
- # 里层循环找出第 j 大的元素的索引, 并赋值给 max_index
- for i in range(1,len(alist)-j):
- # 俩俩比较
- if alist[i-1] <alist[i]:
- max_index = i
- # 互换当前循环最大值元素与列表 len(alist)-1-j 元素位置
- alist[max_index],alist[len(alist)-1-j] = alist[len(alist)-1-j],alist[max_index]
- return alist
5. 插入排序
原理:
- 一种特殊的希尔排序
- 插入排序的主要思想是每次取一个列表元素与列表中已经排序好的列表段进行比较, 然后插入从而得到新的排序好的列表段, 最终获得排序好的列表. 比如, 待排序列表为[49,38,65,97,76,13,27,49], 则比较的步骤和得到的新列表如下:(带有背景颜色的列表段是已经排序好的, 红色背景标记的是执行插入并且进行过交换的元素)
实现方法:
- alist = [1,3,8,5,2]
- def insert_func(alist):
- # 外层循环控制 alist 的索引
- for i in range(1,len(alist)):
- # 循环判断当前索引对应的元素与前面已排好序的元素的大小关系
- while i> 0:
- # 如果小于前一位元素, 则互换位置, 索引 - 1, 继续比较, 直至索引为 1
- if alist[i] <alist[i-1]:
- alist[i],alist[i-1] = alist[i-1],alist[i]
- i -= 1
- else:
- # 如果大于前一位元素, 说明不用动地方. 跳出循环, 判断下一个
- break
- return alist
6. 希尔排序
原理:
- 希尔排序 (Shell Sort) 也称缩小增量排序, 是直接插入排序算法的一种更高效的改进版本, 该方法的基本思想是: 先将整个待排元素序列分割成若干个子序列 (由相隔某个 "增量(gap)" 的元素组成的) 分别进行直接插入排序, 然后依次缩减增量再进行排序, 待整个序列中的元素基本有序 (增量足够小) 时, 再对全体元素进行一次直接插入排序. 因为直接插入排序在元素基本有序的情况下(接近最好情况), 效率是很高的, 因此希尔排序在时间效率比直接插入排序有较大提高
- gap: 增量值, 拆分出来各个组数
- 插入排序就是增量为 1 的希尔排序
实现方法:
- def sort(alist):
- gap = len(alist)//2 # gap 第一次设置为总长度为 2
- # 当 gap 的值大于等于 1 的时候
- # 根据插入排序的原理给各个 gap 下分出的数组排序
- while gap>= 1:
- for i in range(gap,len(alist)):
- while i> 0:
- if alist[i] <alist[i-gap]:
- alist[i],alist[i-gap] = alist[i-gap],alist[i]
- i -= gap
- else:
- break
- # 排序完当前下根据 gap 的分组, gap = gap // 2
- gap //= 2
- return alist
7. 快速排序
原理:
- 将列表中第一个元素设定为基准数字, 赋值给 mid 变量, 然后将整个列表中比基准小的数值放在基准的左侧, 比基准到的数字放在基准右侧. 然后将基准数字左右两侧的序列在根据此方法进行排放.
- 定义两个指针, low 指向最左侧, high 指向最右侧
- 然后对最右侧指针进行向左移动, 移动法则是, 如果指针指向的数值比基准小, 则将指针指向的数字移动到基准数字原始的位置, 否则继续移动指针.
- 如果最右侧指针指向的数值移动到基准位置时, 开始移动最左侧指针, 将其向右移动, 如果该指针指向的数值大于基准则将该数值移动到最右侧指针指向的位置, 然后停止移动.
- 如果左右侧指针重复则, 将基准放入左右指针重复的位置, 则基准左侧为比其小的数值, 右侧为比其大的数值.
实现方法:
- def sort(alist,start,end):
- low = start # 左侧指针
- high = end # 右侧指针
- #递归结束的条件
- if low> high:
- return
- # 基准: 最左侧的数值
- mid = alist[low]
- # low 和 high 的关系只能是小于, 当等于的时候就要填充 mid 了
- while low <high:
- '''
- 从右侧开始判断:
- 如果当前 high 指针指向的元素值大于 mid 基准值, 则 high 指针向左偏移 1 位, 即 high-1, 继续循环;
- 如果当前 high 指针指向的元素值小于 mid 基准值, 则将 high 指针指向的元素赋予至 low 指针指向的位置, 结束当前循环, 退出右侧循环
- 从左侧开始判断:
- 如果当前 low 指针指向的元素值小于 mid 基准值, 则 high 指针向右偏移 1 位, 即 high+1, 继续循环;
- 如果当前 low 指针指向的元素值大于 mid 基准值, 则将 low 指针指向的元素赋予到 high 指针指向的位置, 结束当前循环, 退出左侧循环
- 判断当前的 low 指针是否与 high 指针重合, 如果重合, 将 mid 的基值放在 low 或者 high 的位置, 如果不等, 继续下一次循环.
- '''
- while low < high:
- if alist[high]> mid:
- high -= 1
- else:
- alist[low] = alist[high]
- break
- while low <high:
- if alist[low] < mid:
- low += 1
- else:
- alist[high] = alist[low]
- break
- #当 low 和 high 重复的时候, 将 mid 填充
- if low == high:
- alist[low] = mid #or alist[high] = mid
- break
- '''
- 按照 low 指针与 high 指针重合的位置切分列表, 递归调用本体函数对切分后的列表进行快排!!
- '''
- # 快排切分后的左侧序列
- sort(alist,start,high-1)
- # 快排切分后的右侧序列
- sort(alist,low+1,end)
- return alist
- alist = [6,1 , 2, 7, 9, 3, 4, 5, 10, 8]
六. 常见问题(面试题)
如何使用两个队列实现一个栈?
实现方法:
- # 定义一个队列类
- 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)
- # 数据准备
- q1 = Queue()
- q2 = Queue()
- for i in [1,2,3,4,5]: q1.append(i)
- '''
- 思路:
- 1. 首先了解: 栈先进后出, 队列先进先出
- 2. 取值时, 将 q1 前 n-1 项取出存在 q2 中, 将最后一个值打印出来
- '''
- # 按 q1 的长度来控制循环次数
- while q1.size()> 0:
- # 取出前 q1 的前 n-1 项, 放置在 q2 中
- while q1.size()> 1:
- item = q1.dequeue()
- q2.append(item)
- print(q1.dequeue())
- # 此时 q1 位空, q2 为原 q1 的前 n-1 项, 将 q1,q2 互换, 然后进行下次循环
- q1,q2 = q2,q1
如何实现将单链表倒置?
实现方法:
- class Node():
- def __init__(self,item):
- self.item = item
- self.next = None
- class Link():
- def __init__(self):
- self._head = None
- def append(self,item):
- node = Node(item)
- if self._head == None:
- self._head = node
- return
- cur = self._head
- pre = None
- while cur:
- pre = cur
- cur = cur.next
- pre.next = node
- def travel(self):
- cur = self._head
- while cur:
- print(cur.item)
- cur = cur.next
- def remove(self,item):
- cur = self._head
- pre = None
- #删除的是第一个节点
- if cur.item == item:
- self._head = cur.next
- return
- while cur:
- pre = cur
- cur = cur.next
- if item == cur.item:
- pre.next = cur.next
- return
- '''
- 实现倒置单链表思路:
- 1. 主要就是改变节点的指向, 当前节点指向前一节点, 当前节点的后一节点指向当前节点.
- 2.self._head 的指向原链表的最后一个节点
- 3. 所以实现此方法需要三个变量: pre 前一节点, cur 当前节点, next_node 下一节点
- '''
- def reverse(self):
- cur = self._head # 未循环当前节点为 self._head
- pre = None # 前一节点为 None
- next_node = cur.next # 下一节点为 cru.next
- # 开始循环倒置链表
- while cur:
- # 改变当前节点的指向, 使其指向前一节点 pre
- cur.next = pre
- # 节点往后偏移, pre = cur, cur = next_node,
- pre = cur
- cur = next_node
- # 如果 cur 不为 None 时, next_node = cur.next
- if cur:
- next_node = cur.next
- # 当 cur 为空时, 结束循环, 所有的节点指向已倒置完毕, 改变 self._head 的指向
- # 注意此时应该指向 pre 而不是 cur!
- self._head = pre
来源: http://www.bubuko.com/infodetail-3262786.html