目录
一、链表
(一)基本概念
1、链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,有一系列结点(地址)组成,结点可动态的生成。
2、结点包括两个部分:(1)存储数据元素的数据域(内存空间),(2)存储指向下一个结点地址的指针域。
3.链表分为 (1)单链表(leetcode刷题使用最多) (2)双链表 (3)单向循环链表 (4)双向循环链表
特点:适合写、不适合读
适用场景:读少写多
(二)链表的的四种操作
1、访问 Access O(n) (没有索引了,要从头开始遍历)
2、搜索 Search O(n)
3、插入 Insert O(1) (仅仅指删除这个操作,不包含遍历找到这个元素的过程)
4、删除 Delete O(1) (同上)
(三)链表常用操作
1、创建链表
使用deque包来创建
2、添加元素
两种方法:append(末尾)、insert(特定索引位置)
3、访问元素
4、查找元素
找到特定元素的索引
5、更新元素
6、删除元素
两种方法:del linkedlist[i]是删除索引位置的元素,linkedlist.remove(i)括号中是元素,不是索引
7、链表的长度
(四)leetcode练习题
203移除链表元素:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
解法1:不设头结点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
cur=pre=head
if (head!=None):# 考虑了[]
while (head!=None and pre==head and cur==head ):# head!=None条件考虑了[7,7,7,7]这种情况到最后head是None的情况
if (head.val!=val):
cur=head.next
pre=head
else:
head=head.next
pre=cur=head
while cur:
if (cur.val==val):
pre.next=cur.next
cur=cur.next
else:
cur=cur.next
pre=pre.next
return head
else:
return head
解法2(简单解法):在头结点前新建一个结点。next指向头结点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
prehead = ListNode()
prehead.next = head
cur=head
pre=prehead
while cur:
if cur.val==val:
pre.next = cur.next
cur=cur.next
else:
pre=pre.next
cur=cur.next
return prehead.next
206反转链表:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur=head
pre=None
while cur:
nnext = cur.next
cur.next = pre
pre = cur
cur = nnext
return pre
二、队列
(一)基本概念
特点:先入先出。
分类:
1、单端队列:只有一个扣可以进,一个口可以出
2、双端队列(用的不多):两个口都可以进,两个口都可以出
(二)队列的四种操作
1、访问 Access O(n) (从队列头开始遍历)
2、搜索 Search O(n)
3、插入 Insert O(1) (只能在末尾插入)
4、删除 Delete O(1) (只能删除第一个)
(三)队列常用操作
左边为队头,右边为队尾
1、创建队列
默认用deque创建的是一个双端队列,但大部分时候只用单端就可以
2、添加元素
3、获取即将出队的元素
4、删除即将出队的元素
popleft不仅可以删除元素,还同时可以返还元素
5、判断队列是否为空、队列长度
6、遍历队列(边删除边遍历队列)
(五)python的deque与list的区别
- 根据index读list,时间复杂度为O(1),deque是O(n)
- 在两头插入数据,deque的时间复杂度为O(1), list为O(n)
- deque是一个双向链表,所以操作头尾非常简单。
- 随机往中间插入数据,deque与list的时间复杂度都是O(n)
(六)leetcode练习题
933最近的请求次数
写一个 RecentCounter
类来计算特定时间范围内最近的请求。
请你实现 RecentCounter
类:
RecentCounter()
初始化计数器,请求数为 0 。int ping(int t)
在时间t
添加一个新请求,其中t
表示以毫秒为单位的某个时间,并返回过去3000
毫秒内发生的所有请求数(包括新请求)。确切地说,返回在[t-3000, t]
内发生的请求数。
保证 每次对 ping
的调用都使用比之前更大的 t
值。
class RecentCounter:
def __init__(self):
self.q = deque()
def ping(self, t: int) -> int:
self.q.append(t)
while len(self.q)!=0 and (t-self.q[0])>3000:
self.q.popleft()
return len(self.q)
# Your RecentCounter object will be instantiated and called as such:
# obj = RecentCounter()
# param_1 = obj.ping(t)
三、栈
(一)基本概念
特点:先进后出
应用:浏览器的后退功能
(二)栈的四种操作
1、访问 Access O(1) (栈顶元素)
2、搜索 Search O(n)
3、插入 Insert O(1) (只能在栈顶插入)
4、删除 Delete O(1) (只能删除栈顶元素)
(三)栈的常用操作
1、创建栈
直接用列表实现栈常用的操作
2、添加元素
3、查看栈顶元素
4、删除栈顶元素
5、栈的长度 O(1)
列表里其实有一个变量,进出元素随时加减,所以len的时间复杂度是O(1)
6、栈是否为空
7、遍历栈(便删除栈顶边遍历)
(四)leetcode练习题
20有效的括号
解法一:自己写的
class Solution:
def isValid(self, s: str) -> bool:
stack=[0]
for char in s:
if stack[-1]=='(' and char==')':
stack.pop()
elif stack[-1]=='[' and char==']':
stack.pop()
elif stack[-1]=='{' and char=='}':
stack.pop()
else:
stack.append(char)
if len(stack)==1:
return True
else:
return False
解法二:大佬写的(和上面思路一致,但写法非常简洁)
class Solution:
def isValid(self, s: str) -> bool:
dic = {')':'(',']':'[','}':'{'}
stack = []
for i in s:
if stack and i in dic: # stack不为空 且 是dic中的key(遇到右括号才进入这个if)
if stack[-1] == dic[i]: stack.pop()
else: return False # 已经遇到右括号了,但是栈顶不是和右括号对应的左括号,无效
else: stack.append(i) # 栈空 或 左括号进栈
return not stack
496下一个更大元素
nums1
中数字 x
的 下一个更大元素 是指 x
在 nums2
中对应位置 右侧 的 第一个 比 x
大的元素。
给你两个 没有重复元素 的数组 nums1
和 nums2
,下标从 0 开始计数,其中nums1
是 nums2
的子集。
对于每个 0 <= i < nums1.length
,找出满足 nums1[i] == nums2[j]
的下标 j
,并且在 nums2
确定 nums2[j]
的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1
解法一:自己写的
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
stack = []
for n1 in nums1:
for i,n2 in enumerate(nums2):
if n2 == n1:
max = n2
stack.append(-1)
for j in range(i+1,len(nums2)):
if nums2[j]>n2:
max=nums2[j]
stack[-1] = max
break
else:
pass
else:
pass
return stack
break:结束本循环,也就是说,即使循环次数没有结束,只要碰到break,这个循环就结束了。如果是循环嵌套,内循环里边有break,则结束内循环,外循环不受影响;如果是外循环有break,则结束外循环。
解法二:大佬写的
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
m, n = len(nums1), len(nums2)
res = [0] * m
for i in range(m):
j = nums2.index(nums1[i])
k = j + 1
while k < n and nums2[k] < nums2[j]:
k += 1
res[i] = nums2[k] if k < n else -1
return res
注:
c = a if a >b else b
如果a>b为真,则输出a,否则输出b