题目来自牛客网
文章目录
NC78 反转单向链表
日期:2021.8.29
方法:定义pre存储新链表的头,cur用于反转方向,nex用于遍历旧链表,然后不断遍历
nex = cur.next 旧链表向前移动
cur.next = pre 改变当前节点的方向
pre = cur 新链表的头向前移动
cur = nex 当前节点向前移动
失误: list.reverse()返回None,直接将list反向
自己的解法:遍历原链表,并用列表暂存链表的值,反转列表后,生成新的链表
时间复杂度:O(n),
空间复杂度:O(n)
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if pHead is None:#判断是否为空
return None
tem_list = [] #存储链表的所有值
tem_pHead = pHead # 保存输入链表的头
while(True):# 遍历输入链表
tem_list.append(tem_pHead.val)
if tem_pHead.next is None:
break
else:
tem_pHead = tem_pHead.next
tem_list.reverse() # 反转链表的值
new_list = ListNode(tem_list[0]) # 新链表的头
tem_list2 = new_list # 用于生成反转链表
for i in range(len(tem_list))[1:]: # 生成反转链表
tem_list2.next = ListNode(tem_list[i])
tem_list2 = tem_list2.next
return new_list
标准解法: 直接改变原链表方向,把头变成尾,然后把下个节点指向头,直至遇到None
时间复杂度:O(n),
空间复杂度:O(1)
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if pHead is None or pHead.next is None:#判断是否为空 或只有一个值
return pHead
pre = ListNode(None) # 存储新链表的头
cur = pHead
nex = ListNode(None) # 与cur一起遍历整个链表
while cur is not None:
nex = cur.next
if cur == pHead:#判断是否是头
cur.next = None # 生成新链表的尾
else:
cur.next = pre # 反转节点的方向,指向新链表
pre = cur #将新链表的头指向新节点
cur = nex #将cur指向下个节点
# 当遍历到最后一个节点时,nex会变成None,从而cur会变成None,退出循环
return pre
NC93 设计LRU缓存结构
日期:2021.8.30
思路:把一个字典当作双向链表,来存储键值对,当每次读取一个值时,将该值移动到字典最右端,并删除原值
缺点:再内置dict中,是没有顺序的,可能出现错误
#
# lru design
# @param operators int整型二维数组 the ops
# @param k int整型 the k
# @return int整型一维数组
#
class Solution:
def __init__(self):
self.lru = {} #双向链表作为缓存,左侧作为最不常用的,右侧作为最常用的
def set(self,dic,k):
'''
将dic(key,value)插入缓存
'''
if len(self.lru) == k: #当满的时候,删除最不常用的
key = list(self.lru.keys())[0]
self.lru.pop(key)
#若没满,则添加当前元素
key = dic[0]
value = dic[1]
self.lru[key] = value
def get(self,key):
'''
从缓存中读取并更新缓存
'''
if key in self.lru:
value = self.lru[key]
# 更新顺序
self.lru.pop(key) # 删除
self.lru[key] = value #移动到最右段
return value
else:
return -1
def LRU(self , operators , k ):
# write code here
l_return = []
for opt in operators:
if opt[0] == 1 :
self.set([opt[1],opt[2]],k)
if opt[0] == 2:
key = opt[1]
l_return.append(self.get(key))
return l_return
改进:将dict更换为OrderedDict(底层直接使用了哈希表和双链表)
from collections import OrderedDict
class Solution:
def __init__(self):
self.cache = OrderedDict()
def get(self, key):
if key not in self.cache:
return -1
self.cache.move_to_end(key)# 移动到末尾
return self.cache[key]
def set(self, key, value, k):
if key in self.cache: #若已存在key,更新当前值
self.cache.move_to_end(key)
self.cache[key] = value
return None
if len(self.cache) == k:# 若达到容量,则弹出头部节点
self.cache.popitem(last=False)
self.cache[key] = value
def LRU(self, operators, k):
res = []
for opt in operators:
if opt[0] == 1:
self.set(opt[1], opt[2], k)
elif opt[0] == 2:
res.append(self.get(opt[1]))
return res
大佬解法:双向链表+哈希表
class DListNode:
def __init__(self, x=0, y=0):
self.key = x
self.value = y
self.pre = None
self.next = None
class Solution:
def __init__(self):
self.head = DListNode()
self.tail = DListNode()
self.head.next = self.tail
self.tail.pre = self.head
self.cache = dict()
def get(self, key):
if key not in self.cache:
return -1
node = self.cache[key]
self.move_to_end(node)
return node.value
def set(self, key, value, k):
if key in self.cache:
node = self.cache[key]
node.value = value
self.move_to_end(node)
else:
node = DListNode(key, value)
self.cache[key] = node
self.add_to_end(node)
if len(self.cache) > k:
removed = self.head.next
self.remove(removed)
del self.cache[removed.key]
def remove(self, node):
node.pre.next = node.next
node.next.pre = node.pre
def add_to_end(self, node):
node.pre = self.tail.pre
node.next = self.tail
self.tail.pre.next = node
self.tail.pre = node
def move_to_end(self, node):
self.remove(node)
self.add_to_end(node)
def LRU(self, operators, k):
res = []
for opt in operators:
if opt[0] == 1:
self.set(opt[1], opt[2], k)
elif opt[0] == 2:
res.append(self.get(opt[1]))
return res
NC4 判断链表中是否有环
日期:2021.8.31
难点:如何让空间复杂度为O(1)
思路:用一个哈希表复制链表中所有的节点,当出现重复节点时,证明有环
时间复杂度:O(N),空间复杂度O(N)
缺点:占用空间太大
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#
#
# @param head ListNode类
# @return bool布尔型
#
class Solution:
def hasCycle(self , head ):
# write code here
tem = [] #存储链表中所有的节点
if head is None or head.next is None:#判断链表是否为空或长度只有1
return False
while (head.next is not None):
tem.append(head)# 将当前节点添加到列表中
head = head.next # 向前移动一步
if head in tem: # 如果该节点在以前已经出现过,则证明有环出现
return True
return False
改进,使用快指针(每次走两步),慢指针(每次走一步),如果存在环,两者最终将相遇,空间复杂度降低为O(1)
class Solution:
def hasCycle(self , head ):
# write code here
if head is None or head.next is None:
return False
low = head
fast = head
while((low is not None) and (fast is not None)):#两者任何一个走到尽头,则停止循环
low = low.next
if fast.next is not None:
fast = fast.next.next
else: #当链表长度是奇数时,直接返回False
return False
if low == fast: # 判断两者是否相遇
return True
return False
NC76 用两个栈实现队列
日期:2021.9.1
知识点:python中的List就相当于一个栈,只使用append和pop的时候
初步解法:
不足:使用复制后反转的方式,来将stack1中的数据复制到stack2中,不太妥当
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack1 = [] # 压入栈(右侧压入)
self.stack2 = [] # 弹出栈(右侧弹出)
def push(self, node):
# write code here
self.stack1.append(node) #将数据压入栈
def pop(self):
# return xx
if len(self.stack2) > 0 : #当弹出栈不为空时,弹出最右侧值
return self.stack2.pop()
elif len(self.stack1) == 0: # 当弹出栈和压入栈都为空时,返回None
return None
else: # 当弹出栈为空,压入栈不为空时,将压入栈中的值反向后复制到弹出栈中,并删除原来的值
self.stack2 = self.stack1.copy()
self.stack2.reverse()
self.stack1 = []
return self.stack2.pop()
改进:逐个将stack1中的数据复制到stack2中
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack1 = [] # 压入栈(右侧压入)
self.stack2 = [] # 弹出栈(右侧弹出)
def push(self, node):
# write code here
self.stack1.append(node)
def pop(self):
# return xx
if len(self.stack2) > 0 :
return self.stack2.pop()
elif len(self.stack1) == 0:
return None
else:
while self.stack1:
self.stack2.append(self.stack1.pop()) #逐个弹出并添加到stack2z
return self.stack2.pop()
NC105 二分查找-II
日期:2021.9.1
难点:返回从左往右第一个相等的值的索引
初步思路:首先判断数组为空或数组长度为一的情况,然后用start和end,middle进行二分查找,若middle处的值=target,需要继续向前寻找,最终会出现start=end-1的情况,判断首尾后,跳出循环
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 如果目标值存在返回下标,否则返回 -1
# @param nums int整型一维数组
# @param target int整型
# @return int整型
#
class Solution:
def search(self , nums , target ):
# write code here
length = len(nums) #数组的长度
if length == 0: #数组为空
return -1
elif length ==1: #数组长度为1
return -1 if nums[0] != target else 0
start = 0 #首
end = length-1 #尾
index = -1 #存储返回的索引
while (True):
middle = int((start+end)/2)
value = nums[middle]
if value == target: #若middle处遇到target,则赋值给index,并继续向前查找
index = middle
end = middle
elif value>target:
end = middle
else:
start = middle
if start == end -1: # 当start=end-1的情况,判断首尾后,跳出循环
if nums[start] == target:
index = start
break
if nums[end] == target:
index = end
break
break
return index
NC15 求二叉树的层序遍历
日期:2021.9.2
二叉树的三种遍历方法:python 二叉树 前序中序后序层序 递归与非递归遍历_Mario的博客-CSDN博客
前序遍历:中左右(先读当前节点的值,然后跳到左边,和右边)
中序遍历:左中右
后序遍历:左右中
层序遍历:一层一层读取
初步思路:使用一个队列来存储每一层的所有树节点,然后遍历队列中节点的所有值,并用一个临时队列,遍历原队列中节点的所有子节点,将原队列更新为临时队列
时间复杂度:O (N) 空间复杂度O(N)
缺点:使用了一个临时队列,可以优化后将其去掉
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
#
#
# @param root TreeNode类
# @return int整型二维数组
#
class Solution:
def levelOrder(self , root ):
# write code here
if root is None:
return []
queue = [root] #遍历存储某一层的所有节点
res = [] #返回结果
while len(queue)>0:#当某一层节点数量为零时,跳出循环
res.append([node.val for node in queue])#遍历所有值
temp = [] #临时列表存储下一层节点
for node in queue: #注意从左向右添加节点
if node.left is not None:
temp.append(node.left)
if node.right is not None:
temp.append(node.right)
queue = temp
return res
参考:递归解法:
class Solution:
def levelOrder(self , root ):
# write code here
if root is None:
return []
res = [] # 返回值
level = 0 #保存层数
def get_level(level,node):
if level == len(res):#当遍历到下一层时,添加一层
res.append([])
res[level].append(node.val) #将值添加到对应的层
if node.left:
get_level(level+1,node.left) #进入下一层的左侧
if node.right:
get_level(level+1,node.right)#进入下一层的右侧
get_level(level,root)
return res
WC142 排序算法
日期:2021.9.3
方法:快速排序
参考:快速排序
- 解法:
- 选择左侧第一个值作为基准point
- 从右边开始,找到第一个小于point的索引
right
- 从左边开始,找到第一个大于point的索引
left
- 交换两者
- 重复1-3,知道
left=right
- 交换point 和right的值
- 此时,right左侧的值都小于right,右侧的值都大于right,然后递归即可
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
# 将给定数组排序
# @param arr int整型一维数组 待排序的数组
# @return int整型一维数组
#
class Solution:
def MySort(self , arr ):
# write code here
# write code here
return self.QuickSort(arr,0,len(arr)-1)
def QuickSort(self,arr,left,right):
'''
快速排序
'''
if left>=right:
return
point = arr[left]
low = left
high = right
while(left < right):
while left <right and point <= arr[right]:
right -= 1
while left < right and point >= arr[left]:
left += 1
self.swap(arr,left,right)
self.swap(arr,low,right)
self.QuickSort(arr, low, right-1)
self.QuickSort(arr, right+1, high)
return arr
def swap(self,arr,i,j):
temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
NC45 实现二叉树先序,中序和后序遍历
日期:2021.9.4
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
#
#
# @param root TreeNode类 the root of binary tree
# @return int整型二维数组
#
class Solution:
def threeOrders(self , root ):
# write code here
if root is None:
return [None,None,None]
def first(root):
'''
前序遍历,先读中节点,然后读左子树,最后右子树
'''
stack = [] #存储扫过的节点
res = [] #存储值
while root or stack:
while root is not None: #一直读到当前子树的最底层的最左侧
stack.append(root)
res.append(root.val)#添加父节点的值
root = root.left # 走向左子树
if stack:
node = stack.pop() #读取最后加入的节点
if node.right is not None:
root = node.right #走向右子树
return res
def middle(root):
'''
中序遍历,先读左子树,然后读父节点,最后右子树
'''
stack = []
res = []
while root or stack:
while root is not None:
stack.append(root)
root = root.left # 一直走到左子树的尽头
if stack:
node = stack.pop()
res.append(node.val) #读取最后一个加入节点的值(左子树为none的父节点的值)
if node.right is not None:
root = node.right #走向右子树
return res
def last(root):
'''
后序遍历,先读左子树,然后读右子树,最后父节点
【父节点的值、右节点、左节点】
【父节点的值、右节点、左节点的值、左—右节点、左-左节点】
【父节点的值、右节点、左节点的值、左—右节点、左-左节点的值】
实现后序遍历
'''
stack = []
res = []
stack.append(root) #压入父节点
while stack:
node = stack.pop()
if type(node) is TreeNode: #如果当前节点是父节点,将当前节点替换成节点值,并压入右节点、左节点
stack.append(node.val)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
else:
res.append(node) #当栈的头部是数字时,说明左侧走到了尽头,将父节点的值添加到列表中
return res
return [first(root),middle(root),last(root)]
递归解法
class Solution:
def threeOrders(self , root ):
# write code here
if root is None:
return [None,None,None]
def first(root,res = []):
if not root:
return
res.append(root.val)
first(root.left,res)
first(root.right,res)
return res
def middle(root,res):
if not root:
return
middle(root.left, res)
res.append(root.val)
middle(root.right, res)
return res
def last(root,res=[]):
if not root:
return
last(root.left,res)
last(root.right,res)
res.append(root.val)
return res
return [first(root,[]),middle(root,[]),last(root,[])]