前文
整理一下最近学的数据结构与算法的笔记。其实一直都有断断续续的学数据结构,但每次都是零碎的,这次想把之前学习的内容串起来,形成一个整体。
新年希望能把自己的学习路径记录下来,最好能整理成体系(给自己挖坑,不知道什么时候能填:)
适用人群:想学习数据结构与算法的coder们~
正文:
这篇文章整理的是下图标红的部分,本文会按照代码实现数据结构和实践两部分,分别对数组,线性表,栈,队列,链表,字符串进行介绍,文中会包括python代码的实现。
1. 数组
1.1 数组的定义:
数组是具有一定顺序关系的若干对象组成的集合,组成数组的对象称为数组元素
1.2 静态数组和动态数组
静态数组,在程序编译时分配空间的数组(声明之后数组长度不可改变)。
动态数组,在程序运行时分配空间的数组(声明之后数组长度可根据问题而调整)
1.3 代码实现动态数组:
# 继承object和不继承object的区别,继承object会有更多的可操作对象(__XX__),这些都是类中的高级特性
class Arr:
def __init__(self, capacity=10):
"""
构造函数
:param capacity:数组最大容量,不指定的话默认为10
"""
self._capacity = capacity
self._size = 0
self._data = [None]*self._capacity
def __getattr__(self, item):
"""让Arr类支持索引操作"""
return self._data[item]
def getSize(self):
"""返回数组有效元素个数"""
return self._size
def getCapacity(self):
"""返回当前容量"""
return self._capacity
def isEmpty(self):
"""判断当前数组是否为空"""
return self._size ==0
def add(self,index,elem):
"""
向数组中添加元素
:return:
"""
if index < 0 or index > self._size: #输入的位置无效
raise Exception('Add Filed. Require 0<= index <= self._size')
if self._size==self._capacity: #容量满了
# 默认扩容当前容量的二倍。容量翻倍要比容量加上一个固定值要好,这样做均摊复杂度为O(1)。具体请百度
self._resize(self._capacity*2)
#从尾部开始挪动元素,在index处腾出一个空间来
for i in range(self._size-1,index-1,-1):
self._data[i+1] = self._data=[i]
self._data[index] = elem #将该位置赋值为elem
self._size +=1 #数组有效元素+1
def addLast(self,elem):
"""
向数组末尾添加元素
:param elem: 所要添加的元素
:return:
"""
self.add(self._size,elem) # 直接调用add方法,注意不用再次判定合法性了,因为add函数中已经判断过了
# private
def _resize(self,new_capacity):
"""
数组容量放缩至new_capacity,私有成员函数
:param new_capacity:新的容量
:return:
"""
new_arr = Arr(new_capacity)#新建一个新的数组new_arr,容量为new_capacity
for i in range(self._size):
new_arr.addList(self._data[i]) #将当前数组的元素按当前顺序全部移动到new_arr中
self._capacity = new_capacity # 数组容量变为new_capacity
self._data = new_arr._data #将new_arr._data赋值给self._data,从而完成数组的容量放大操作
def addFirst(self,elem):
"""
想数组头部添加元素
时间复杂度:O(1)
:param elem: 所有添加的元素
:return:
"""
self.add(0,elem)
def get(self, index):
"""
获得索引index处的元素
时间复杂度:O(1)
:param index: 数组索引
:return: 索引处的值
"""
if index<0 or index>=self._size:
raise Exception("Get failed, Index is illegal.")
return self._data[index]
def getFirst(self):
"""
获取数组首位元素的值
:return: 首位置元素的值
"""
return self.get(0)
def getLat(self):
"""
获取数组末尾元素的值
:return:末尾元素的值
"""
return self.get(self._size-1)
def set(self,index,elem):
"""
将索引为index的元素的值设为elem
时间复杂度:O(1)、
:param index:索引
:param elem:新的值
:return:
"""
if index<0 or index>=self._size:
raise Exception('Set Failed. Index is illegal.')
self._data[index] = elem
def contains(self,elem):
"""
查看数组中是否存在元素elem,最好不要传入一个浮点数...
时间复杂度:O(n)
:param elem:目标元素
:return:bool值,存在为真
"""
for i in range(self._size): # 遍历
if self._data[i] == elem:
return True # 找到了就返回True
return False # 遍历完了还没找到,就返回False
def find(self,elem):
"""
在数组中查找元素,并返回元素所在的索引。(如果数组中存在有多个elem,只返回最左边elem的索引
时间复杂度: O(n)
:param elem:目标元素
:return: 元素所在的索引,没找到则返回-1
"""
for i in range(self._size):
if self._data[i]==elem:
return i
return -1
def findAll(self,elem):
"""
找到值为elem全部元素索引
:param elem: 目标元素
:return: 一个列表,值全部elem的索引
"""
ret_list = Arr()
for i in range(self._size):
if self._data[i]==elem:
ret_list.addLast(i)
return ret_list
def remove(self,index):
"""
删除索引为index的元素,index后面的元素都要向前移动一个位置
时间复杂度O(n)
:param index:目标索引
:return:位于该索引的元素的值
"""
if index < 0 or index >= self._size:
raise Exception('Remove failed.Require 0<= index <self._size')
ret = self._data[index] # 拷贝一下index的位置,便于返回
for i in range(index+1, self._size):
self._data[i-1] = self._data[i]
self._size -=1 # 维护self._size
self._data[self._size] = None # 最后一个元素的垃圾回收
return ret
def removeFirst(self):
"""
删除数组首位置的元素
时间复杂度:O(n)
:return:数组首位置的元素
"""
return self.remove(0)
def removeLast(self):
"""
删除数组首位置的元素
时间复杂度:O(n)
:return:数组首位置的元素
"""
return self.remove(self._size-1) # 调用remove函数
def removeElement(self,elem):
"""
删除数组中卫elem的元素,如果数组中不存在elem,那么什么都不做,如果存在多个,只删除最左边的那个
:param elem:要删除的值
:return:
"""
index = self.find(elem)
if index!=-1:
self.remove(index)
def removeALLElement(self,elem):
"""
删除数组内所有值为elem的元素,可以用递归来写,这里用迭代的方法,elem不存在就什么都不做
:param elem: 要删除的目标元素
:return:
"""
while True:
index = self.find(elem)
if index != -1:
self.remove(index)
else:
break
def get_Max_index(self):
"""
获取数组中的最大元素的索引,返回最大的索引值,如果有多个最大值,返回最左边的
:return:
"""
if self.isEmpty():
raise Exception("Error, array is Empty!")
max_elem_index = 0 #记录最大的索引,初始化为0
for i in range(self.getSize()):
if self._data[i]>self._data[max_elem_index]:
max_elem_index= i
return max_elem_index
def get_Min_index(self):
"""
获取数组中的最小元素的索引,返回最小元素的索引值,如果有多个最小值,默认返回最左边那个的索引
时间复杂度:O(n)
:return: 最小元素的索引
"""
if self.isEmpty():
raise Exception('Error, array isEmpty!')
min_elem_index = 0 # 记录最小值得索引,初始化为0
for i in range(1,self.getSize()):
if self._data[i]<self._data[min_elem_index]:
min_elem_index=i
return min_elem_index
def swap(self, index1, index2):
"""
交换分别位于索引index1和索引index2处的元素
:param index1: 索引1
:param index2: 索引2
"""
if index1 < 0 or index2 < 0 or index1 >= self._size or index2 >= self._size: # 合法性检查
raise Exception('Index is illegal')
self._data[index1], self._data[index2] = self._data[index2], self._data[index1]
1.4 实践
- task1:
利用动态数组解决数据存放的问题,编写一段代码,要求输入一个整数N,用动态数组A来存放2-N之间所有5或者7的倍数,输出该数组
利用上面实现的数组class Arr
def put_five_seven(N):
"""
输入一个整数N,返回被5或者7整除的数组
:param self:
:param N:
:return: list
"""
alist = Arr()
for i in range(1,N+1):
if i % 5 == 0 or i % 7 == 0:
alist.addLast(i)
return alist
if __name__=='__main__':
N = input("请输入一个整数N=")
target_list = put_five_seven(int(N))
for i in range(target_list._size):
print(target_list._data[i], " ")
- task2:
托普利茨矩阵问题,如果一个矩阵的每一方向由左上到右下的对角线上具有相同的元素,那么这个矩阵是拓普利茨矩阵
1,2,3,4
5,1,2,3
9,5,1,2
输入是一个包含整数的二维数组,行数和列数均在[1,20]范围内,整数包含在[0,99]范围内
输出为true or false
def isToeplitzMatrix(matrix):
"""
输入二维数组,判断是否为托普利茨矩阵(对角线元素一样)
思路:如果位于(x,y)上的元素,只需要检查x==0 or c==0 or matrix[x][y] = matrix[x-1][y-1]
:param matrix:
:return:
"""
# matrix[-1][-1]不会溢出,取的是最后一位数
# all()表示所有都为true才返回true
return all(r == 0 or c == 0 or matrix[r - 1][c - 1] == val for r, row in enumerate(matrix) for c, val in enumerate(row))
if __name__=='__main__':
matrix = [[1, 2, 3, 4], [5, 1, 2, 3], [9, 5, 1, 2]]
print(isToeplitzMatrix(matrix))
- task3:
三数之和:给定一个包含n个整数的数组nums,判断nums中是否存在三个元素a,b。c使得a+b+c=0?,找出所有满足条件且不重复的三元组
def sumOfThree(nums):
nums.sort() #排序
res = []
for i in range(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue #去重
j = i+1 #指针1
k = len(nums)-1 #指针2
while j < k:
sums = nums[i]+nums[j]+nums[k]
if sums == 0:
res.append([nums[i], nums[j], nums[k]])
while j < k and nums[j] == nums[j+1]: #去重
j += 1
while j < k and nums[k] == nums[k-1]: #去重
k -= 1
j += 1
k -= 1
elif sums < 0:
j += 1
else:
k -= 1
return res
if __name__=='__main__':
nums=[-1,0,1,2,-1,-4]
print(sumOfThree(nums))
2. 线性表
2.1 定义
线性表(Linear List)是由n(n >= 0)
个相同类型的数据元素a1,a2,...,an
组成的有序序列。即表中除首尾元素外,其它元素有且仅有一个直接前驱和直接后继。首元素仅有一个直接后继,尾元素仅有一个直接前驱。表中数据元素的个数称为表的长度,记为:(a1,a2,...,an)
。
2.2 线性表的存储和实现
顺序存储(顺序表):利用顺序存储结构(即利用数组)实现的线性表。
链式存储(链表):利用指针方式实现的线性表称为链表(单链表、循环链表、双链表)。它不要求逻辑上相邻的数据元素在物理位置上也相邻,即:逻辑结构与物理结构可以相同也可以不相同。
2.3 代码实现
顺序存储结构实现线性表
# 顺序存储结构实现线性表
class SeqList(object):
"""
线性表
基本操作:
"""
def __init__(self, maxsize):
self.data = range(maxsize)
self.maxsize = maxsize
self.last = -1
def __getitem__(self, key):
"""
索引器
:param item:
:return:
"""
if self.is_empty():
print('SeqList is empty!')
return
elif key<0 or key>self.last:
print("the given key is Error!")
else:
return self.data[key]
def __setitem__(self, key, value):
"""
索引器set
:param key:
:param value:
:return:
"""
if self.is_empty():
print("SeqList is empty!")
return
if key <0 or key>self.last:
print("the given key is Error!")
return
else:
self.data[key] = value
def __len__(self):
"""
长度
:return:
"""
length = self.last + 1
return length
def getlengthe(self):
"""
顺序表长度
:return:
"""
return self.last+1
def clear(self):
self.last=-1
def is_empty(self):
if self.last == -1:
return True
else: return False
def is_full(self):
"""
顺序表是否满了
:return:
"""
if self.last==self.maxsize-1:
return True
else:
return False
def insert(self,index,elem):
"""
插入元素
:param index:
:param elem:
:return:
"""
if self.is_full():
print('SeqList is full!')
return
elif index < 0 or index > self.last+1: # 当要插入的index没超过数组的最大容量时,index不能比最后一个index对应的值大1
print("Position is error!")
return
elif index==self.last+1:
self.last+=1
self.data[self.last] = elem
else:
for i in range(self.last, index-1,-1): # 原来的数据从index到last全部往后移一位
self.data[i+1] = self.data[i]
self.data[i] = elem # 此时的i的值等于index
self.last += 1
def remove(self, index):
"""
删除元素
:param index:
:return:
"""
if self.is_empty():
print('SeqList is empty!')
return
if index < 0 or index > self.last:
print("Position is error!")
return
else:
for i in range(index, self.last):
self.data[i] = self.data[i+1]
self.data[self.last] = None
self.last-=1
def find(self, data):
"""
:param data:
:return:存在返回该元素在线性表中的位置,否则返回-1
"""
for i in range(self.last+1):
if self.data[i] == data:
return i
链式存储结构实现线性表
单向链表实现
# 定义节点类
class ListNode(object):
def __init__(self, data):
self.data = data
self.next = None # 指针
def has_value(self,value):
if self.data ==value:
return True
else:
return False
# 定义单链表类
class SingleLinkedList():
"""
__init__():初始化对象
list_length():返回节点数量
output_list():输出节点值
add_list_item():在列表末尾增加一个新的节点
unordered_search():根据一个特殊值去查询列表
remove_list_item_by_id():根据节点id移除节点
"""
def __init__(self):
self.head = None #头部
self.tail = None
def list_length(self):
"return the number of list items"
count = 0
current_node = self.head
while current_node is not None:
count += 1
current_node = current_node.next
return count
def output_list(self):
"""outputs the List"""
current_node = self.head
while current_node is not None:
print(current_node.data)
current_node = current_node.next
def add_list_item(self, item):
if not isinstance(item, ListNode):
item = ListNode(item)
if self.head is None:
self.head = item
else:
self.tail.next = item
self.tail = item
def unordered_search(self, value):
"""根据一个特殊值去查询列表"""
current_node = self.head
node_id = 1
results = []
while current_node is not None:
if current_node.has_value(value):
results.append(node_id)
current_node = current_node.next
node_id = node_id + 1
return results
def remove_list_item_by_id(self, item_id):
"""根据结点索引删除"""
current_id = 1
current_node = self.head
previous_node = None
while current_node is not None:
if current_id == item_id:
if previous_node is not None:
previous_node.next = current_node.next
else:
self.head = current_node.next
previous_node = current_node
current_node = current_node.next
current_id = current_id + 1
双向链表实现
# 定义节点类
class ListNode1():
def __init__(self,data):
self.data = data
self.next = None
self.previous = None
def has_value(self,value):
if self.data ==value:
return True
else:
return False
# 双向链表
class DoubleLinkedList():
def __init__(self):
"""
constructor to initiate this object
"""
self.head = None
self.tail = None
def List_length(self):
"""return the number of list length"""
count = 0
current_node = self.head
while current_node is not None:
count +=1
current_node = current_node.next
return count
def output_list(self):
current_node = self.head
while current_node is not None:
print(current_node.data)
current_node = current_node.next
def unordered_search(self, value):
"""search the Linked List for the node that has this value"""
current_node = self.head
results = []
node_id =1
while current_node is not None:
if current_node.has_value(value):
results.append(node_id)
current_node = current_node.next
node_id = node_id + 1
return results
def add_list_item(self,item):
"""add an item at the end of the list"""
if isinstance(item,ListNode):
if self.head is None:
self.head = item
self.previous = None
self.tail = item
else:
self.tail.next = item
item.previous = self.tail
self.tail = item
def remove_list_item_by_id(self,item_id):
current_node = self.head
current_id = 1
while current_node is not None:
previous_node = current_node.previous
next_node = current_node.next
if current_id == item_id:
# 分别判断id是否为头和链表是否只有一个元素
if previous_node is not None:
previous_node.next = next_node
if next_node is not None:
next_node.previous = previous_node
else:
self.head = next_node
if next_node is not None:
next_node.previous = None
current_node = next_node
current_id += 1
2.4 实践
task1:合并两个有序链表
class Solution():
def merge_twoLinked_into_One(self,LinkA,LinkB):
"""
1. 合并两个有序链表
:param LinkA:
:param LinkB:
:return: 合并完后的链表
"""
results = ListNode()
current_nodeA = LinkA.head
current_nodeB = LinkB.head
while current_nodeA and current_nodeB is not None:
if current_nodeA.data < current_nodeB.data:
results.next = current_nodeA
current_nodeA = current_nodeA.next
else:
results.next = current_nodeB
current_nodeB = current_nodeB.next
if current_nodeB is not None:
results.next = current_nodeB
if current_nodeA is not None:
results.next = current_nodeA
return results.next
task2:删除链表的倒数第N个节点
def Delete_Reverse_N(self,track, n_id):
"""
2. 删除链表的倒数第N个节点
:param track: 链表
:param n_id: 删除倒数第N个结点
:return: 删除后的链表
"""
length = track.list_length
res = track.remove_list_item_by_id(length-n_id+1)
return res
task3:反转链表
def Reverse_Link(self,link):
"""
3. 旋转链表
:return:
"""
if not link:
return link
p = link
q = p.next
p.next = None
while q:
r = q.next
q.next = p
p = q
q = r
return p
3. 栈
3.1 栈的定义
栈是线性表的一种,栈是插入(入栈)和删除(出栈)操作只能在一端(栈顶)进行的线性表。即先进后出(First In Last Out)的线性表。
3.2 栈的实现方式
栈有两种实现一种是顺序栈一种是链栈
3.3 代码实现
栈的顺序存储结构
class stack():
"""
栈的顺序存储结构
"""
def __init__(self, size):
self.size = size
self.stack = []
self.top = -1
def push(self,x):
"""
入栈
:param x:
:return:
"""
if self.is_full():
raise Exception("stack is full!!!")
else:
self.stack.append(x)
self.top = self.top + 1
def pop(self):
"""
出栈
:return:
"""
if self.is_empty():
raise Exception("stack is empty!!!")
else:
self.top = self.top-1
self.stack.pop()
def is_full(self):
"""
判断栈是否已满
:return:
"""
return self.top+1 == self.size
def is_empty(self):
"""
检查栈是否为空
:return:
"""
return self.top == -1
def showStack(self):
print(self.stack)
栈的链式存储结构
class Node(object):
"""
栈的链式存储结构
"""
def __init__(self,data=None):
self.data = data
self.next = None
class LinkStack(object):
def __init__(self):
self.top = Node(None)
self.count = 0
def get_legth(self):
return self.count
def get_top(self):
"""返回栈顶元素"""
return self.top.data
def is_empty(self):
return self.count==0
def push(self,elem):
"""进栈"""
tmp = Node(elem)
if self.is_empty():
self.top = tmp
else:
tmp.next = self.top
self.top = tmp
self.count += 1
def pop(self):
"""出栈"""
if self.is_empty():
raise IndexError("Stack is Empty!")
else:
self.count -=1
elem = self.top.data
self.top = self.top.next
return elem
def show_stack(self):
"""从栈顶开始显示各结点值"""
if self.is_empty():
raise IndexError("Stack is empty!")
else:
tmp = self.top
j = self.count
while tmp and j>0:
print(tmp.data)
tmp = tmp.next
j -= 1
if __name__=="__main__":
def test_linkStack():
lks = LinkStack()
for i in range(1, 5):
lks.push(i)
lks.show_stack()
lks.pop()
lks.show_stack()
def test_stack():
s = stack(10)
for i in range(6):
s.push(i)
s.showStack()
for i in range(3):
s.pop()
s.showStack()
4. 队列
4.1 队列的定义
队列和栈一样也是线性表的一种,队列是插入(入队)在一端(队尾)进行而删除(出队)在另一端(队首)进行的线性表。即先进先出(First In First Out)的线性表。
4.2 队列的存储方式
队列的存储方式同样分为顺序存储和链式存储。
4.3 代码实现
队列的顺序存储
class SqQueue():
"""队列的顺序存储"""
def __init__(self, size):
self.data = list(None for i in range(size+1))
self.maxsize = size + 1
self.front = 0
self.rear = 0
self.length = 0
def get_length(self):
return self.length
def is_full(self):
return (self.rear+1) % self.maxsize == self.front
def is_empty(self):
return self.rear == self.front
def enQueue(self, elem):
"""
进队列,从队尾加入
:param elem:
:return:
"""
if self.is_full():
raise IndexError("Queue is full!!!")
else:
self.data[self.rear] = elem
self.rear = (self.rear + 1) % self.maxsize # [0,1,2,None]
self.length += 1
def deQueue(self):
"""出队列,从对首删除"""
if self.is_empty():
raise ValueError("SqQueue is empty!")
else:
del_elem = self.data[self.front]
self.data[self.front] = None
self.front = (self.front + 1) % self.maxsize
self.length -= 1
return del_elem
def show_queue(self):
# 显示队列元素,从队首开始显示
if self.is_empty():
raise ValueError("SqQueue is empty!")
else:
j = self.front
while j != self.rear:
print(self.data[j])
j = (j+1) % self.maxsize
print(' ')
队列的链式存储结构
class Node():
def __init__(self, data=None):
self.data = data
self.next = None
class LinkQueue():
def __init__(self):
self.front = Node()
self.rear = Node()
self.length = 0
def get_length(self):
return self.length
def is_empty(self):
return self.length == 0
def enQueue(self, elem):
tmp = Node(elem)
if self.is_empty():
self.rear = tmp
self.front = tmp
else:
self.rear.next = tmp
self.rear = tmp
self.length += 1
def deQueue(self):
# 出队
if self.is_empty():
raise ValueError("LinkQueue is empty!")
else:
del_elem = self.front.data
self.front = self.front.next
self.length -= 1
return del_elem
def showQueue(self):
if self.is_empty():
raise ValueError("LinkQueue is empty!")
j = self.length
tmp = self.front
while j>0:
print(tmp.data)
tmp = tmp.next
j -= 1
print(" ")
if __name__ == "__main__":
def check_listQueue():
sqq = SqQueue(5)
for i in range(5):
sqq.enQueue(i)
sqq.show_queue()
print("---------------------")
sqq.deQueue()
sqq.show_queue()
def check_LinkQueue():
lkq = LinkQueue()
for i in range(5):
lkq.enQueue(i)
lkq.showQueue()
print(";;;;;;;;;;;;;;;")
lkq.deQueue()
lkq.showQueue()
check_LinkQueue()
4.4 队列的实践
模拟银行大厅叫号服务程序
from DataWhale.Queue_task04 import LinkQueue
# from DataWhale.Queue_task04 import Node
import threading
import time
class LinkBankQueue(LinkQueue):
def __init__(self):
LinkQueue.__init__(self)
self.callnumber = 0
def getcallNumber(self):
if self.is_empty() and self.callnumber == 0:
self.callnumber = 1
else:
self.callnumber += 1
return self.callnumber
def getLength(self):
return self.length
# 服务窗口
class ServiceWindow(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.bankQueue = None
self.lock = threading.Lock()
def service(self):
while True:
self.lock.acquire()
time.sleep(10)
try:
if not self.bankQueue.is_empty():
print("请 %d 号到 %s 号窗口" % (self.bankQueue.front.data, threading.current_thread().name))
self.bankQueue.deQueue()
else:
print("队列为空哦!!!")
finally:
self.lock.release()
# 客户端
if __name__ == "__main__":
bankQueue = LinkBankQueue()
windowcount = 2
serviceWindows = [None] * windowcount
threadList = [None] * windowcount
for i in range(windowcount):
serviceWindows[i] = ServiceWindow()
serviceWindows[i].bankQueue = bankQueue
threadList[i] = threading.Thread(name=(i+1), target=serviceWindows[i].service, args=())
threadList[i].start()
while True:
input("请点击触摸屏获取号码!!!\n")
callnumber = bankQueue.getcallNumber()
if bankQueue != None:
print("您的号码是:%d,您前面有 %d 位" % (callnumber, bankQueue.getLength()))
bankQueue.enQueue(callnumber)
else:
print("您的号码是:%d,您前面有0位" % (callnumber))
5. 字符串
5.1 字符串的定义
- 串(string)是由零个或多个字符组成的有限序列,又名字符串,记为`S=”a0a1…an”
5.2 字符串的实践
求输入字符串中连续不重复的字符串的最长长度。
- 分析:这道题主要用到思路是:滑动窗口
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求! 一直维持这样的队列,找出队列出现最长的长度时候,求出解!
class Solution():
def lenOfLongestSubstr(self, s):
"""
输入:abcabcbb
:param s:
:return:
"""
if not s:
return 0
left = 0 # 窗口的左边
L = len(s) # 字符串总长度
lookup = set()
cur_len = 0
max_len = 0
# 窗口右移
for i in range(L):
cur_len += 1
while s[i] in lookup:
lookup.remove(s[left])
left += 1
cur_len -= 1
if cur_len > max_len:
max_len = cur_len
lookup.add(s[i])
return max_len
参考链接:https://github.com/datawhalechina/team-learning