目录
- 未参考剑指offer上面的思路
- 3. 数组中的重复数字
- 4.二维数组中的查找
- 5.替换空格
- 6 从尾到头打印链表
- 7 重建二叉树
- 8 二叉树的下一个节点
- 9 用两个栈实现队列
- 10 斐波那契数列
- 11 旋转数组的最小数字
- 15 二进制中1的个数
- 16 数值的整数次方
- 18.2 删除链表中重复的节点
- 19.正则表达式匹配
- 20.表示数值的字符串
- 21 调整数组顺序使奇数位于偶数前面
- 22 链表中的倒数第k个节点
- 23.链表中环的入口节点
- 24 反转链表
- 25 合并两个排序的链表
- 26 树的子结构
- 27 二叉树的镜像
- 28 判断对称二叉树
- 29 顺时针打印矩阵
- 30 包含min函数的栈
- 31 栈的压入、弹出序列
- 32 从上往下打印二叉树--广度遍历
- 33 二叉搜索树的后序遍历序列
- 34 二叉树中和为某一值的路径
- 35 复杂链表的复制
- 36 二叉搜索树与双向链表
- 37 序列化二叉树
- 39 数组中出现次数超过一半的数字
- 40 最小的K个数
- 41 数据流中的中位数
- 66.构建乘积数组
- 已参考多种思路
- 未归类--来自牛客剑指offer
未参考剑指offer上面的思路
3. 数组中的重复数字
# -*- coding:utf-8 -*-
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
li = []
for i in numbers:
if i not in li:
li.append(i)
else:
duplication[0] = i
return True
return False
4.二维数组中的查找
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
rows = len(array)
cols = len(array[0])
#此时应该用or不能用and
if rows == 0 or cols == 0:
return False
for i in range(rows):
if target >= array[i][0] and target <= array[i][-1]:
for j in range(cols):
if target == array[i][j]:
return True
return False
笔记
获取二维列表的维度:
#获取行
rows = len(array)
#获取列
cols = len(array[0])
获取二维数组的维度
#获取行
rows = array.shape(0)
#获取列
cols = array.shape(1)
5.替换空格
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
ss = ""
for i in s:
if i == " ":
ss = ss + "%20"
else:
ss = ss + i
return ss
6 从尾到头打印链表
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
res=[]
while listNode:
res.append(listNode.val)
listNode=listNode.next
return res[::-1] #逆序打印
笔记
python中[-1]、[:-1]、[::-1]、[2::-1]的使用方法
a=[1,2,3.4,5]
print(a)
[ 1 2 3 4 5 ]
print(a[-1]) ###取最后一个元素
[5]
print(a[:-1]) ### 除了最后一个取全部
[ 1 2 3 4 ]
print(a[::-1]) ### 取从后向前(相反)的元素
[ 5 4 3 2 1 ]
print(a[2::-1]) ### 取从下标为2的元素翻转读取
[ 3 2 1 ]
7 重建二叉树
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
if len(pre) == 0:
return None
if len(pre) == 1:
return TreeNode(pre[0])
root = TreeNode(pre[0])
# i = tin.index(pre[0])
#
# tin_lchild = tin[:i]
# len_lchild = len(tin_lchild)
# pre_lchild = pre[1:1+len_lchild]
#
# tin_rchild = tin[i+1:]
# len_rchild = len(tin_rchild)
# pre_rchild = pre[1+len_lchild:]
root.left = self.reConstructBinaryTree(pre[1:1+len(tin[:tin.index(pre[0])])], tin[:tin.index(pre[0])])
root.right = self.reConstructBinaryTree(pre[1+len(tin[:tin.index(pre[0])]):], tin[tin.index(pre[0])+1:])
return root
笔记
递归★★★★★
class soultion:
def nove(self,n,a,b,c):
print(n,a,b,c)
if n == 1:
print(a,'------------>',c)
else:
self.nove(n-1,a,c,b)
self.nove(1,a,b,c)
self.nove(n-1,b,a,c)
if __name__ == "__main__":
a = soultion()
b = a.nove(3, 'A', 'B', 'C')
print(b)
上述程序的执行结构图如下所示:
四个字母分别代表传入的(n,a,b,c)
传入顺序:由左到右,由上到下。
8 二叉树的下一个节点
题目分析:
# -*- coding:utf-8 -*-
# class TreeLinkNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# self.next = None
class Solution:
def GetNext(self, pNode):
# write code here
#有右子树
if pNode.right != None:
r = pNode.right
while r.left:
r = r.left
return r
#无右子树
else:
#如果是根节点(无父节点)
if pNode.next == None:
return None
#如果不是根节点(有父节点)
else:
#如果是父节点的左孩子
if pNode == pNode.next.left:
return pNode.next
#如果是父节点的右孩子
else:
while pNode.next.next.left != pNode.next:
pNode = pNode.next
#没有符合条件的即是尾节点
if pNode.next.next == None:
return None
return pNode.next.next
9 用两个栈实现队列
题目分析
栈:先进后出
队列:先进先出
完成队列的push和pop操作,即实现头删和尾插。
- 首先建立两个栈,s1、s2
- 将队列中的元素“abcd”压入s1中,此时s2为空
- 将s1中的元素pop进s2中,此时pop一下s2中的元素,就可以达到和队列删除数据一样的顺序了
- 当s2只pop了一个元素a时(未pop完所有元素),s1中可能还会插入元素e,这时如果将s1中的元素e插入s2中,在a之后出栈的元素就是e了,显然,这样想是不对的,我们必须规定当s2中的元素pop完之后,也就是s2为空时,再插入s1中的元素。
题目分析笔记借鉴于下面的链接:
https://blog.csdn.net/cherrydreamsover/article/details/80466781
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
#append操作为尾插,为了遵循栈的原则,在pop的时候必须是pop()
self.stack1.append(node)
def pop(self):
# return xx
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
#append操作为尾插,为了遵循栈的原则,在pop的时候必须是pop()
return self.stack2.pop()
10 斐波那契数列
斐波那契数列定义:从第3项开始,每一项都等于前两项之和。
F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
标准斐波那契数列(f(1)=1、f(2)=1))
# -*- coding:utf-8 -*-
class Solution:
def Fibonacci(self, n):
# write code here
count = 0
a , b = 0 ,1
while count < n:
a , b = b , a+b
count+=1
return a
"""
#下面的代码是用递归实现
#经过测试,在n>=32时运行时间已经超过2s
if n == 0:
return 0
if n == 1:
return 1
return self.Fibonacci(n-1) + self.Fibonacci(n-2)
"""
11 旋转数组的最小数字
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if not rotateArray:
return None
if len(rotateArray) == 1:
return rotateArray[0]
i = 0
while rotateArray[i] and rotateArray[i+1]:
if rotateArray[i] > rotateArray[i+1]:
return rotateArray[i+1]
i+=1
15 二进制中1的个数
下面代码中有两个比较神奇的操作:
操作一 if n < 0:
n = n & 0xffffffff
在Python中,数的大小是可以无限扩大的,不像c++中,数超过32位会溢出。
所以对于一个负数而言,进行了这个操作,实际上是提取了负数(在计算机中以补码形式存在)的后32位,前面的任意位则变成了0。
比如说 -1,
一开始是 111…(n个1)…11111111,
进行111…(n个1)…11111111 & 0x000…(n个0)…ffffffff,
得到,00…(n个0)…111111111(32个1),变成了含32个1的正数.
可能会有人对上面的例子有疑问,为什么经过上面的操作之后-1就变成了含32个1的正数?????
解释:
在操作之前,-1的补码是 111…(n个1)…1111111,但是黄色的1表示是负数,在经过操作之后,只保留了最后的32位(为什么保留32位,下面有解释,先继续往下看)此时00…(n个0)…111111111(32个1)中最左边的1不在是符号位,符号位而是最左边的0,因此变成了含32个1的正数.
为什么只保留最后32位,为什么是与0xffffffff相与????
解释:
为什么是与0xffffffff相与,只保留32位???而不是与0xffffffffffffffff(16个)相与保留64位呢,因为牛客网考虑了其他语言,在其他语言中,数超过32位会溢出。
操作二 n = n & (n-1)
如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,会发生下面三点变化:
以最右边的1为分界线,例如二进制数1100
- 最右边的1变为0
- 原来在1后面的所有的0都会变成1
- 原来在1前面数不受影响
因次,可以得到结果是1011
我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
# write code here
count = 0
if n < 0:
n = n & 0xffffffff
while n:
n = n & (n-1)
count+=1
return count
16 数值的整数次方
快速幂法:
快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N)
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
# write code here
"""
法一:快速幂法
"""
#用来记录exponent的正负
flag = False
if base == 0:
if exponent > 0:
return 0
elif exponent < 0:
return None
else:
if exponent == 0:
return 1
else:
if exponent < 0:
flag = True
exponent = -exponent
result = base
#右移一位
exponent >>= 1
while exponent:
base *= base
#判断最右边的一位是否为1
if (exponent & 1) == 1:
result *= base
exponent >>= 1
if flag:
return 1/result
else:
return result
"""
法二
return base**exponent
"""
18.2 删除链表中重复的节点
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplication(self, pHead):
# write code here
#由于链表可能从第一个节点就开始重复,所以在pHead的头节点之前再增加一个节点
first = ListNode(-1)
first.next = pHead
cur = pHead
per = first
while cur and cur.next:
if cur.val != cur.next.val:
per = cur
cur = cur.next
else:
#加上下面这句话之后,会保留重复的节点
#比如链表1->2->3->3->4->4->5
#现在的输出为:1->2->5
#加上下面这句话的输出为:1->2->3->4->5
#per = cur
val = cur.val
while cur and cur.val == val:
cur = cur.next
per.next = cur
return first.next
上述代码的完整调试程序
# -*- coding:utf-8 -*-
class Node(object):
""""节点"""
def __init__(self,elem):
self.elem = elem
self.next = None
class Singlelinklist(object):
"""单链表"""
# 不传入节点时为空列表,因此node的默认值为None
def __init__(self, node=None):
# _表示私有属性
# _head指向列表中的第一个节点
self._head = node
def is_empty(self):
return self._head == None
def lenth(self):
# cur表示游标,用来遍历节点
cur = self._head
# count用来计数
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历(打印)整个列表"""
cur = self._head
while cur != None:
print(cur.elem, end=" ")
cur = cur.next
print("")
# 复杂度O(1)
def add(self, item):
"""列表头部添加元素"""
node = Node(item)
# 下面两句话顺序不能改表,改变顺序之后,原有的列表会丢失
node.next = self._head
self._head = node
# 上面个两句话可以用下面这一句话代替
# self._head,node.next = node,self._head
# 复杂度O(n)
def append(self, item):
"""列表尾部添加元素"""
# 传入的数据,先需要把数据转化为节点
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
while cur.next != None:
cur = cur.next
cur.next = node
# 复杂度O(n)
def insert(self, pos, item):
# pos从0开始索引
# per表示pos最终指向pos-1的那个位置
node = Node(item)
per = self._head
count = 0
if pos <= 0:
self.add(item)
elif pos > (self.lenth() - 1):
self.append(item)
else:
while count < (pos - 1):
count += 1
per = per.next
# 此时per指向pos-1
node.next = per.next
per.next = node
def remove(self, item):
# 只删除与之相匹配的第一个,第二个之后出现的不删除
if self.is_empty():
return
cur = self._head
per = None
while cur != None:
# 判断item与节点是否相等
if cur.elem == item:
# 如果删除的是第一个结点的话
if per == None:
self._head = cur.next
else:
per.next = cur.next
break
else:
per = cur
cur = cur.next
# 复杂度O(n)
def search(self, item):
cur = self._head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
# class ListNode:
# def __init__(self, x):
# self.elem = x
# self.next = None
class Solution:
def deleteDuplication(self, pHead):
# write code here
#由于链表可能从第一个节点就开始重复,所以在pHead的头节点之前再增加一个节点
# first = Node(-1)
# first.next = pHead
cur = pHead._head.next
per = pHead._head
while cur and cur.next:
if cur.elem != cur.next.elem:
per = cur
cur = cur.next
else:
per = cur
elem = cur.elem
while cur and cur.elem == elem:
cur = cur.next
per.next = cur
return pHead._head.next
if __name__ == '__main__':
s = [-1,1,2,3,3,4,4,5]
li = Singlelinklist()
for i in range(len(s)):
li.append(s[i])
# print(li)
answer = Solution()
a = answer.deleteDuplication(li)
print(a)
19.正则表达式匹配
# 可以分为四种情况
#1 s、pattern都为空 ,此时为真
#2 s不为空,pattern为空,此时为假
#3 s为空,pattern不为空,此时可能为真也看了能为假
#此时只有一种情形为真,len(pattern)为偶数,且每一个字母或者.后面都是*,比如:a*b*.*
#4 s、pattern都不为空
#此时可以分为两种情形
#4.1 pattern的第二个字符为*
#此时分为三种情况
#4.1.1 *表示前面的字符出现0次,比如:aa,ab*ac*
#此时self.match(s,pattern[2:])
#4.1.1 *表示前面的字符出现n次(用出现1次进行递归),比如:aaaab,a*b
#此时self.match(s[1:],pattern[2:])
#4.1.3 .表示多个字符时,比如:abc,.*,此时.表示abc,*表示出现一次
#此时self.match(s[1:],pattern)
#4.2 pattern的第二个字符不为*
#此时比较简单,直接比较s、pattern的第一位即可
# -*- coding:utf-8 -*-
class Solution:
def match(self, s, pattern):
# write code here
if s == "" and pattern == "":
return True
elif s != "" and pattern == "":
return False
elif s == "" and pattern != "":
if len(pattern) % 2 == 0:
for i in range(len(pattern)):
if i // 2 != 0 and pattern[i] == "*":
return False
return True
else:
return False
else:
# pattern的第二个字符为*的情况
if len(pattern) >= 2 and pattern[1] == "*":
if s[0] == pattern[0] or pattern[0] == "." :
return self.match(s,pattern[2:]) or self.match(s[1:],pattern[2:]) or self.match(s[1:],pattern)
elif (s[0] != pattern[0] and pattern[1] != ".") and pattern[1] == "*":
return self.match(s,pattern[2:])
#pattern的第二个字符不为*的情况
else:
if s[0] == pattern[0] or pattern[0] == ".":
return self.match(s[1:],pattern[1:])
else:
return False
20.表示数值的字符串
题目分析
- ±出现在第一位或者是e和E的下一位
- E、e后面必须有数(不能是小数的形式),形式如下:2 、-2
- 不能出现e之外的其他字母
- 只能出现一次小数点
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
class Solution:
# s字符串
#def __init__(self, s):
#self.s = s
def isNumeric(self,s):
# write code here
point_count = 0
#E/e的坐标不可能为负值,初始化一个小于0的数即可,但是-1不行,
#初始化为-1时,E_coordinate+1=0,与e/E出现在第一位时矛盾
E_coordinate = -5
has_other = False
has_more_point = False
E_behind_false = False
add_coordinate_false = False
for i in range(len(s)):
#记录小数点的个数
if s[i] == ".":
point_count += 1
#判断E/e出现的位置
elif s[i] == "e" or s[i] == "E":
E_coordinate = i
if i == len(s)-1:
return False
elif (s[i+1] >= "0" and s[i+1] <= "9") or s[i+1] == "+" or s[i+1] == "-":
for j in range(i+2,len(s)-1):
if s[j] < "0" or s[j] >"9":
E_behind_false = True
else:
E_behind_false = True
#判断+/-号出现的位置是否正确
elif s[i] == "+" or s[i] == "-":
if i != 0 and i != E_coordinate+1:
add_coordinate_false = True
#判断是否有其他异常字符
elif s[i] < "0" or s[i] >"9":
has_other = True
if point_count > 1:
has_more_point = True
if has_other ==True or has_more_point == True or E_behind_false == True or add_coordinate_false == True:
return False
else:
return True
21 调整数组顺序使奇数位于偶数前面
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
# write code here
single = []
double = []
for i in array:
if i % 2 ==0:
double.append(i)
else:
single.append(i)
return single + double
22 链表中的倒数第k个节点
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
# write code here
#方法一:快慢指针
#先让快指针向前走k个节点(每一步都需要判断是否越界)
#当快指针到达尾节点时,慢指针到达倒数第k个节点
slow = head
fast = head
for i in range(k):
if fast:
fast = fast.next
else:
return None
while fast:
fast = fast.next
slow = slow.next
return slow
"""
#方法二:
#先遍历一遍计算链表长度,再输出该链表中倒数第k个结点。
#用于记录链表长度
count = 0
cur = head
while cur:
cur = cur.next
count+=1
if k > count:
return None
#经过上面的遍历,per已经指向最后,因此需要在初始化一遍
cur = head
for i in range(count-k):
cur = cur.next
return cur
"""
23.链表中环的入口节点
解题思路中结论二的证明
设:
链表头到环入口长度为–a
环入口到相遇点长度为–b
相遇点到环入口长度为–c
则:相遇时
快指针路程=a+(b+c)k+b ,k>=1 其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。
慢指针路程=a+b
快指针走的路程是慢指针的两倍,所以:
*(a+b)2=a+(b+c)k+b
化简可得:
a=(k-1)(b+c)+c 这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。
"""
解题思路--快慢指针法
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。
接着让两个指针分别从相遇点(meet_point)和链表头(head_point)出发,两者都改为每次走一步,最终相遇于环入口(结论2)。
"""
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
slow = pHead
fast = pHead
meet_point = pHead
head_point = pHead
while fast != None and fast.next != None:
slow = slow.next
fast = fast.next.next
if slow == fast:
meet_point = slow
while head_point != meet_point:
head_point = head_point.next
meet_point = meet_point.next
return head_point
24 反转链表
# -*- 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
#方法一:
#相遇于方法二可以节省空间
tem = None
rev_head = None
#pHead相当于cur,表示当前节点
while pHead:
#tem临时记录pHead的下一个节点
tem = pHead.next
#反转
pHead.next = rev_head
#反转之后的链表的首节点的更新
rev_head =pHead
#当前节点继续往下走
pHead = tem
return rev_head
"""
#方法二:
#新创建一个链表,从头部添加节点
new_list = None
while pHead:
#将数的形式转化为节点的形式
node = ListNode(pHead.val)
#在头部添加节点
node.next = new_list
#每次都需要让new_list指向新节点的头部
new_list = node
#旧链表遍历
pHead = pHead.next
return new_list
"""
25 合并两个排序的链表
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
new_head = ListNode(-1)
root = new_head
while pHead1 != None and pHead2 != None:
#下面的判断必须是if pHead1.val > pHead2.val:
#不能是if pHead1 > pHead2:
#不能对节点进行比较大小
if pHead1.val > pHead2.val:
new_head.next = pHead2
new_head = new_head.next
pHead2 = pHead2.next
else:
new_head.next = pHead1
new_head = new_head.next
pHead1 = pHead1.next
if pHead1:
new_head.next = pHead1
if pHead2:
new_head.next = pHead2
return root.next
26 树的子结构
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# write code here
result = False
if pRoot1 != None and pRoot2 != None:
if pRoot1.val == pRoot2.val:
#此方法中下面的语句可以用下面的这一句话代替
# return self.Perorder(pRoot1,pRoot2) or self.HasSubtree(pRoot1.left,pRoot2) or self.HasSubtree(pRoot1.right,pRoot2)
result = self.Perorder(pRoot1,pRoot2)
if not result:
result = self.HasSubtree(pRoot1.left,pRoot2)
if not result:
result = self.HasSubtree(pRoot1.right,pRoot2)
return result
def Perorder(self, pRoot1, pRoot2):
if not pRoot2:
return True
if not pRoot1:
return False
#下面的四句话可以写成
#if pRoot1.val != pRoot2.val:
# return False
#return self.Perorder(pRoot1.left,pRoot2.left) and self.Perorder(pRoot1.right,pRoot2.right)
if pRoot1.val == pRoot2.val:
return self.Perorder(pRoot1.left,pRoot2.left) and self.Perorder(pRoot1.right,pRoot2.right)
else:
return False
27 二叉树的镜像
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
# write code here
if not root:
return
if root.left or root.right:
root.left,root.right = root.right,root.left
self.Mirror(root.left)
self.Mirror(root.right)
return root
28 判断对称二叉树
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetrical(self, pRoot):
# write code here
if pRoot is None:
return True
return self.check(pRoot.left,pRoot.right)
def check(self,l,r):
if l == None and r == None:
return True
elif (l != None and r == None) or (l == None and r != None) or (l.val != r.val):
return False
return self.check(l.left,r.right) and self.check(l.right,r.left)
29 顺时针打印矩阵
对于下面代码的两点解释
1.pop对于矩阵而言能否pop出某一行?
res += matrix.pop(0)
经过资料查找,所有的解释都是同一种说法:pop只是对于list进行操作,每次只能pop出一个元素。但是经过代码测试,pop函数可以pop出矩阵/二维数组的某一行(pop出某一列需要借助for循环)
测试代码如下:
a = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
a.pop(0)
print(a)
结果:
[[5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
2.对列进行操作的时候,判断语句为什么是if matrix and matrix[0]而不是if matrix?
举个例子:
a = [[1],[2],[3],[4],[5]]
经过res += matrix.pop(0)
a = [[2],[3],[4],[5]]
再经过
if matrix and matrix[0]:
for row in matrix:
res.append(row.pop())
a = [[],[],[],[]]
对于此时的a,matrix为真,matrix[0]为假。
因为[[],[],[],[]]相当于[False,False,False,False]
因此,在对列进行操作时,可能在对首列或者尾列操作之后形成[[],[],[],[]]的情形,一次需要使用if matrix and matrix[0]
进行判断。
# -*- coding:utf-8 -*-
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
# write code here
res = []
while matrix :
# 下面不能用append,append只接受一个元素
#pop可以移除矩阵中的某一行
#a = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
#a.pop(0)--->a = [[5,6,7,8],[9,10,11,12],[13,14,15,16]]
res += matrix.pop(0)
if matrix and matrix[0]:
for row in matrix:
res.append(row.pop())
if matrix:
#先pop出最后一行,在进行取逆操作
res += matrix.pop()[::-1]
if matrix and matrix[0]:
#逆序取矩阵中的每一行
for row in matrix[::-1]:
res.append(row.pop(0))
return res
30 包含min函数的栈
"""
思路:
得到最小元素min函数的复杂度为O(1)
建立一个辅助栈stack2,每当压入stack1的元素小于辅助栈stack2时,同时将元素压入stack2
在出栈时,当在stack1的栈顶元素于辅助栈stack2的栈顶元素相等时,辅助栈也需要进行pop操作
"""
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
self.stack1.append(node)
if not self.stack2:
self.stack2.append(node)
elif node <= self.stack2[-1]:
self.stack2.append(node)
def pop(self):
# write code here
if self.stack2[-1] == self.stack1[-1]:
self.stack2.pop()
#pop方法,需要返回元素
return self.stack1.pop()
def top(self):
# write code here
return self.stack1[-1]
def min(self):
# write code here
return self.stack2[-1]
31 栈的压入、弹出序列
方法一–借助辅助空间
思路
借助一个辅助栈,把压入序列按顺序存储到辅助栈中,当辅助栈不为空且栈顶元素等于弹出序列第一个元素时,弹出辅助栈的栈顶元素,在判断输出序列的下一个元素
#方法一
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
assist_stack = []
j = 0
for i in pushV:
assist_stack.append(i)
while assist_stack and assist_stack[-1] == popV[j]:
assist_stack.pop()
j+=1
return len(assist_stack) == 0
方法一参考的以下博客:
https://blog.csdn.net/weixin_40941966/article/details/80798880
方法二–不借助辅助空间
思路:
如果popV是压入栈序列pushV的弹出序列应满足下列的规律:
该规律用白话叙述不容易理解,通过下面的例子进行解释
pushV = [1,2,3,4,5]
popV1 = [4,5,3,2,1]
popV2 = [4,3,5,1,2]
popV1是pushV的弹出序列,popV2不可能是pushV的弹出序列。
首先,我们看弹出序列的的首元素,popV1和popV2都为4,然后再压入栈序列中进行搜索,pushV[3] = 4
然后,对上面的序列进行pop操作,pop出元素4,可以得到
pushV = [1,2,3,5]
popV1 = [5,3,2,1]
popV2 = [3,5,1,2]
最关键的地方就在下面,弹出序列的下一个元素(popV1中的5,popV2中的3)应该处于pushV的什么位置????
经过分析,弹出序列的下一个元素必须处于上一步pop出的元素(即4)左边的一个元素或者右边所有元素中
此题的关键在于这一句话,不明白的细细品
代码有两个版本,思路一样,版本二相比于版本一节省时间,
建议先看版本一再看版本二,版本一更容易理解
# 版本二
#相比于版本一节省时间
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
temp = 1
error_flag = True
while popV:
#不再从头开始遍历,而是直接看正确位置范围内有没有该元素
for i in range(temp-1,len(pushV)):
if popV[0] == pushV[i]:
temp = i
popV.pop(0)
pushV.pop(i)
#如果在正确范围内有该元素,则error_flag = False
error_flag = False
break
#上面的for循环执行完之后,如果没有在正确范围内找到(即error_flag没修改),
# 则说明该序列不是pushV的弹出序列
if error_flag:
return False
#重置错误标志
error_flag = True
return True
"""
下面的代码实现上面的描述,但是下面的for循环每次都遍历整个序列,时间比较长,在牛客网上无法通过。
最上面的代码是对下面代码的修改,节省了时间,可以在牛客网通过。
# 版本一
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
#为什么初始化为1,以为下面有temp-1
temp = 1
while popV:
for i in range(len(pushV)):
#寻找本次弹出序列元素在popV的位置
if popV[0] == pushV[i]:
#判断位置是否正确
if i < temp-1:
return False
#temp用于记录i的位置,方便判断弹出序列的下一元素是否处于正确位置
temp = i
popV.pop(0)
pushV.pop(i)
#终止for循环
break
#弹出序列所有元素位置都正确,则 return True
return True
"""
32 从上往下打印二叉树–广度遍历
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回从上到下每个节点值列表,例:[1,2,3]
def PrintFromTopToBottom(self, root):
# write code here
answer = []
if not root:
return answer
li = [root]
while li:
cur = li.pop(0)
answer.append(cur.val)
if cur.left:
li.append(cur.left)
if cur.right:
li.append(cur.right)
return answer
33 二叉搜索树的后序遍历序列
二叉搜索树
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树。
后续遍历结果:1 5 3 9 12 10 7
二叉搜索树的后续遍历具有以下特点:
- 最后一个元素为数的根节点
- 存个一个分界线,分界线左边的所有元素都小于根节点,分界线右边的所有元素都大于根节点(上图的分界线就在3 9 之间)
- 经过第二步的分割,形成两个切片[1 5 3]、[9,12,10],切片同样符合上述规律
根据上面的特点,在考虑两种特殊情况,1.无左孩子,2.无右孩子
# -*- coding:utf-8 -*-
class Solution:
def VerifySquenceOfBST(self, sequence):
# write code here
if not sequence:
return False
if len(sequence) == 1:
return True
count1 = 0
count2 = 0
for i in range(len(sequence)):
#左右孩子节点都不为空
if sequence[i] < sequence[-1] and sequence[i+1] > sequence[-1]:
#左子树的所有节点必须小于根节点
for j in range(i+1):
if sequence[j] > sequence[-1]:
return False
#右子树所有节点必须大于根节点
for k in range(i+1,len(sequence)-1):
if sequence[k] < sequence[-1]:
return False
return self.VerifySquenceOfBST(sequence[:i+1]) and self.VerifySquenceOfBST(sequence[i+1:-1])
#仅有左孩子
if sequence[i] < sequence[-1]:
count1 += 1
#仅有右孩子
if sequence[i] > sequence[-1]:
count2 += 1
if count1 == len(sequence)-1 or count2 == len(sequence)-1:
return self.VerifySquenceOfBST(sequence[:-1])
return False
34 二叉树中和为某一值的路径
叶节点:没有子节点的节点
思路:
用一临时列表记入当前先序遍历的整条路径,若此路径节点值的和等于expectNumber,则将路径加入最终返回的列表之中。
注意:当一父节点的两个子节点所在的路径都遍历完时,需要将父节点从临时列表中删除。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def __init__(self):
self.li = []
#用于保存遍历的路径
self.tem = []
def FindPath(self, root, expectNumber):
# write code here
#当root为空时,直接返回空列表
if not root:
return self.li
#当此节点为叶节点,开始判断sum(self.tem)是否等于expectNumber
if not root.left and not root.right:
self.tem.append(root.val)
if sum(self.tem) == expectNumber:
self.li.append(self.tem)
#数组长度大于1时,才能进行下面的切片操作
#当self.tem = self.tem[:-1]用self.tem.pop()代替时,会出现问题
if len(self.tem) > 1:
self.tem = self.tem[:-1]
else:
self.tem.pop()
return self.li
#下面三句话相当前序遍历
self.tem.append(root.val)
if root.left:
self.FindPath(root.left, expectNumber)
if root.right:
self.FindPath(root.right, expectNumber)
#遍历完一次,返回上一层时需要将父节弹出
self.tem.pop()
return self.li
35 复杂链表的复制
思路
# -*- coding:utf-8 -*-
# class RandomListNode:
# def __init__(self, x):
# self.label = x
# self.next = None
# self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
if not pHead:
return None
#1.在原有链表上进行链表的复制,不处理self.random
cur = pHead
while cur:
clone_node = RandomListNode(cur.label)
clone_node.next = cur.next
cur.next = clone_node
cur = cur.next.next
#2.处理新增复制节点的self.random
cur = pHead
while cur:
cur.next.random = cur.random
cur = cur.next.next
#进行链表的拆分
cur = pHead
clone_list = pHead.next
while cur:
clone_node = cur.next
cur.next = clone_node.next
if clone_node.next:
clone_node.next = clone_node.next.next
else:
clone_node.next = None
cur = cur.next
return clone_list
36 二叉搜索树与双向链表
思路
二叉搜索树的中序遍历为排序列表
在中序遍历的基础上进行稍微改动即可。
#二叉搜索树的中序遍历为排序列表
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.last_node = None
def Convert(self, pRootOfTree):
# write code here
self.Tinorder(pRootOfTree)
while pRootOfTree and pRootOfTree.left:
pRootOfTree = pRootOfTree.left
return pRootOfTree
def Tinorder(self,cur):
if not cur:
return
self.Tinorder(cur.left)
#当前节点的左节点指向上一节点
cur.left = self.last_node;
#只有当上一个节点不为空时,上一节点的右节点才能指向当前节点
if self.last_node:
self.last_node.right = cur;
self.last_node = cur;
self.Tinorder(cur.right)
37 序列化二叉树
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.count = -1
def Serialize(self, root):
# write code here
# 下面的两种判断方式相同
# if root is None:
if not root:
#必须添加分隔符,否则两位数转化为字符串时会出现错误
return "#,"
#如果当前节点是父节点,则会继续递归
#如果当前节点是叶节点,则会返回“.val , # , #"
return str(root.val)+ ',' + self.Serialize(root.left) + self.Serialize(root.right)
def Deserialize(self, s):
# write code here
self.count += 1
#只是第一次进入函数时对字符串进行分割
#split操作返回的是一个列表,如果每次都进行split操作会报错
#因为第一次往后s变成了列表,列表没有split操作
if self.count == 0:
s = s.split(',')
if self.count >= len(s):
return None
root = None
if s[self.count] != "#":
root = TreeNode(int(s[self.count]))
root.left = self.Deserialize(s)
root.right = self.Deserialize(s)
return root
split示例
39 数组中出现次数超过一半的数字
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
dic = {}
for i in numbers:
if i not in dic:
dic[i] = 1
else:
dic[i] += 1
#values() 方法返回一个迭代器,可以使用 list() 来转换为列表,列表为字典中的所有值
if max(dic.values()) > len(numbers)//2:
#遍历字典(dic),查找所有“值”的最大值(key=dic.get(everykey)),返回该“值”的“键”(dic)
return max(dic,key=dic.get)
else:
return 0
40 最小的K个数
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if k > len(tinput):
return []
self.quick_sort(tinput,0,len(tinput)-1)
return tinput[:k]
def quick_sort(self,li,first,last):
if first >= last:
return
mid_value = li[first]
low = first
high = last
while low < high:
while low < high and li[high] >= mid_value:
high -= 1
li[low] = li[high]
while low < high and li[low] < mid_value:
low += 1
li[high] = li[low]
li[low] = mid_value
self.quick_sort(li,first,low-1)
self.quick_sort(li,low+1,last)
41 数据流中的中位数
方法一–锥排序
思路:
- 建立一个大顶锥,一个小顶锥,小顶锥用来存大的半数数据,大顶锥用来存小半数数据(小顶堆中的元素都大于等于大顶堆中的元素)
- 当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中
- 当数目为奇数的时候,将这个值插入小顶堆中,再将小顶堆中根节点(即最小值)插入到大顶堆中
- 取中位数的时候,如果当前个数为偶数,显然是取小顶堆和大顶堆根结点的平均值;如果当前个数为奇数,显然是取小顶堆的根节点
# -*- coding:utf-8 -*-
#导入优先队列算法
from heapq import *
#在heapq中只有小顶锥,大顶锥则用取反实现
class Solution:
def __init__(self):
#创建一个堆,可以使用list来初始化为 []
self.heaps = [], []
self.count = 0
def Insert(self, num):
# write code here
small, large = self.heaps
if self.count % 2 == 0:
#heapq.heappush(heap, item)
#将 item 的值加入 heap 中,保持堆的不变性。
#heapq.heappushpop(heap, item)
#将 item 放入堆中,然后弹出并返回 heap 的最小元素。
"""
如果是偶数:
先将取反放入大顶锥
再将大顶锥的追锥顶pop出,并取反放入小顶锥
(由于大顶锥用来存小半数数据,且大顶锥是小顶锥取反实现,
因此大顶锥里面的数全为原始数据的相反数)
"""
#-num的负号表示大顶锥里面的数全为原始数据的相反数
#-heappushpop的负号表示小顶锥里面的数全为原始数据(即相反数的相反数)
heappush(small, -heappushpop(large, -num))
else:
"""
如果是奇数:
先将放入小顶锥
再将大顶锥的追锥顶pop出,并取反放入大顶锥
"""
heappush(large, -heappushpop(small, num))
self.count+=1
def GetMedian(self,ss):
# write code here
small,large = self.heaps
if self.count % 2 != 0:
return float(small[0])
return (small[0] - large[0]) / 2.0
方法二
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
#用来记录数据流的个数
self.count = 0
self.li = []
def Insert(self, num):
# write code here
self.li.append(num)
self.li.sort()
self.count+=1
#题目有问题,缺少一个参数。需要自己加上
def GetMedian(self,AA):
# write code here
if self.count % 2 == 0:
#牛客网python版本为2+,被除数需要写成小数形式
#python2 5/2 = 2
#python3 5/2 = 2.5
return (self.li[self.count//2-1]+self.li[self.count//2])/2.0
else:
return self.li[self.count//2]
66.构建乘积数组
class Solution:
def multiply(self, A):
# write code here
if A==None:
B = A
else:
B = [1] * len(A)
for i in range(len(A)):
for j in range(len(A)):
if i != j:
B[i] = B[i] * A[j]
return B
已参考多种思路
42 连续子数组的最大和
# -*- coding:utf-8 -*-
"""
思路1:
’最大连续子序列的和‘的第一个元素可能是原始序列中的任何一个元素
因此,第一次计算首元素为array[0]的最大连续和
第二次计算首元素为array[1]的最大连续和
...
最终得到’最大连续子序列的和‘
"""
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
#最大连续子序列和不能初始化为0,
#因为array的元素可能全小于0
maxsum = array[0]
for i in range(len(array)):
#下列两个参数初始化为array[i]即可,相应的i改为i+1
#当前序列最大和
tin_li_maxsum = array[i]
#当前序列和
tin_li_sum = array[i]
for j in range(i+1,len(array)):
tin_li_sum = tin_li_sum + array[j]
if tin_li_sum > tin_li_maxsum:
tin_li_maxsum = tin_li_sum
if tin_li_maxsum > maxsum:
maxsum = tin_li_maxsum
return maxsum
"""
思路2:
动态规划
"""
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dp = [0 for _ in range(len(nums))]
#初始化
dp[0] = nums[0]
#定义循环
for i in range(1,len(nums)):
#状态转移
if dp[i-1]>0:
dp[i] = dp[i-1] + nums[i]
else:
dp[i] = nums[i]
return max(dp)
45:把数组排成最小的数
"""
思路:
本题的最终目的就是将数组中的元素按照拼接顺序排好序,
因此,可以看成一道排序题,只不过需要自己定义排序规则
两个步骤:
1.排序(我选择的是比较简单的冒泡排序)
2.排序规则定义
先将元素转化为字符串,然后拼接,比较拼接之后的大小。
最后再决定num1,num2是否调换位置。
str1 = str(num1) + str(num2)
str2 = str(num2) + str(num1)
if str1 >= str2:
return str2
else:
return str1
"""
# -*- coding:utf-8 -*-
class Solution:
def PrintMinNumber(self, numbers):
# write code here
"""冒泡排序"""
if not numbers:
return ""
n = len(numbers)
count = 0
while n > 0:
for i in range(n - 1):
if self.compare(numbers[i], numbers[i + 1]):
numbers[i], numbers[i + 1] = numbers[i + 1], numbers[i]
count += 1
n -= 1
#如果count==0,则说明此序列是已经排好序的序列
if count == 0:
break
num_str = ""
for i in numbers:
num_str = num_str + str(i)
return int(num_str)
def compare(self, num1, num2):
str1 = str(num1) + str(num2)
str2 = str(num2) + str(num1)
if str1 >= str2:
return True
else:
return False
49:丑数
"""
思路:
创建一个数组uglyNumber,里面是拍好序的丑数
数组里面最大的丑数为uglyNumber[-1],该丑数肯定是前面某一个丑数乘以2、3或者5的结果,
所以我们首先考虑把已有的每个丑数分别乘以2、3、5,
乘以2、3、5之后,用count2、count3、count5记录首大于uglyNumber[-1]的数的下标
然后取uglyNumber[count2]*2,uglyNumber[count3]*3,uglyNumber[count5]*5这三者之中的最小值
最后一进行uglyNumber.append
"""
# -*- coding:utf-8 -*-
class Solution:
def GetUglyNumber_Solution(self, index):
# write code here
if index <= 0:
return 0
uglyNumber = [1]
count2 = 0
count3 = 0
count5 = 0
while len(uglyNumber) < index:
while uglyNumber[count2]*2 <= uglyNumber[-1]:
count2 += 1
while uglyNumber[count3]*3 <= uglyNumber[-1]:
count3 += 1
while uglyNumber[count5]*5<= uglyNumber[-1]:
count5 += 1
uglyNumber.append(min(uglyNumber[count2]*2,uglyNumber[count3]*3,uglyNumber[count5]*5))
return uglyNumber[index-1]
50:第一个只出现一次的字符
"""
思路1:
从头遍历,每个元素都与后面的所有元素进行对比,
如果在后面没有发现重复的字符,则该字符就是只出现一次的字符。
这种思路的时间复杂度是O(n**2)
思路2:
创建一个哈希表(字典),键为元素,值为元素出现的次数,
第一遍遍历之后得到每个元素出现的次数
第二遍从头开始遍历,看元素在字典中对应的值是否等于1
这种思路的时间复杂度是O(n),但是需要辅助空间
"""
# -*- coding:utf-8 -*-
class Solution:
def FirstNotRepeatingChar(self, s):
# write code here
if not s:
return -1
dic = {}
for i in s:
if i not in dic:
dic[i] = 1
else:
dic[i] += 1
for i in range(len(s)):
if dic[s[i]] == 1:
return i
51:数组中的逆序对
注:本题在牛客网上无法通过,但是思路没有问题
解释一下思路2:
已知数组[7,5,6,4]
归并排序的实现步骤如下图所示:
- 在第一对长度为1的子数组{7}、{5}中,7大于5,因此(7,5)组成一个逆序对。同样,在第二对长度为1的子数组{6}、4}中,也有逆序对(6,4)
- 接下来我们统计两个长度为2的子数组之间的逆序对。
首先比较left[0]和right[0]的大小,并将小的弹出。
如果right[0]<left[0],则说明存在len(left)个逆序对,
如果right[0]>left[0],则说明不存在逆序对
重复上面的过程,直至left、right都为空
"""
思路1:
借用冒泡排序的思想----复杂度O(n**2)
逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成一个逆序对。
若构成一个逆序对,则计数器count+1
由于每个数字都要和O(n)个数字进行比较,因此这种算法的时间复杂度是O(n2)
代码附在最后面
思路2:
借用归并排序的思想----复杂度O(nlog2n)
"""
# -*- coding:utf-8 -*-
class Solution:
#思路二
def __init__(self):
self.count = 0
def InversePairs(self, data):
# write code here
data = self.merge_sort(data)
return self.count%1000000007
def merge_sort(self, alist):
"""归并排序"""
n = len(alist)
if n <= 1:
return alist
mid = n // 2
# left 归并排序后形成的新的有序列表
left = self.merge_sort(alist[:mid])
# right 归并排序后形成的新的有序列表
right = self.merge_sort(alist[mid:])
# 将两个有序的列表合并
result = []
while left and right:
if left[0] < right[0]:
result.append(left.pop(0))
else:
self.count += len(left)
result.append(right.pop(0))
result += left
result += right
return result
"""
#思路一
def InversePairs(self, data):
# write code here
#冒泡排序
n = len(data)
count = 0
while n > 0:
for i in range(0,n-1):
if data[i] > data[i+1]:
data[i],data[i+1] =data[i+1],data[i]
count += 1
n -= 1
if count == 0:
break
return count
"""
52:两个链表的第一个公共节点
"""
思路1:
时间复杂度O(m+n),空间复杂度O(m+nm、n分别为两个链表的长度
1.建立两个辅助栈,
2.同时从两个栈顶弹出元素,如果存在公共节点,则总存在i使得弹出的第i个节点相同,
第i+1个节点不同,
3.那么,第i个节点即为第一个公共节点
思路2:
时间复杂度O(m+n),m、n分别为两个链表的长度
1.先遍历两个链表,计算两个链表的长度。
2.让长的链表先走“两个链表之差”步
3.然后,再让两个链表同时走,第一个相同的节点即为第一个公共节点
"""
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
#思路二:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
len1,len2 = 0,0
#p1,p2 = pHead1,pHead2
p1 = pHead1
p2 = pHead2
while p1:
p1 = p1.next
len1 += 1
while p2:
p2 = p2.next
len2 += 1
if len1 > len2:
for i in range(len1-len2):
pHead1 = pHead1.next
else:
for i in range(len2-len1):
pHead2 = pHead2.next
while pHead1 != pHead2:
pHead1 = pHead1.next
pHead2 = pHead2.next
return pHead1
53:数字在排序数组中出现的次数
"""
思路1-二分法 复杂度O(n)
1.先用二分法找到某一个k出现的位置
2.然后再向两边检测两边的元素时候等于k
思路2-改进的二分法 复杂度O(logn)
1.找出第一个k出现的位置
1.1如果中间数比k小,则第一个k出现在右半段
1.2如果中间数比k大,则第一个k出现在左半段
1.3如果中间数等于k,则向左左搜索,找到第一个k的位置
2.找出最后一个k出现的位置
同上
3.作差
思路3 复杂度O(logn)
1.因为数组中的数为整数,因此引入两个数k-0.5,k+0.5
2.查找k-0.5,k+0.5应该插入的位置,然后作差
"""
# -*- coding:utf-8 -*-
class Solution:
def GetNumberOfK(self, data, k):
# write code here
#思路三
return self.Serch(data,k+0.5)-self.Serch(data,k-0.5)
def Serch(self,data,k):
first = 0
last = len(data)-1
while first <= last:
mid = (first+last) // 2
if data[mid] < k:
first = mid +1
else:
last = mid - 1
return first
55.1:二叉树的深度
"""
思路:
1.如果根节点只有左子树,那么树的深度应该是左子树的深度+1
2.如果根节点只有右子树,那么树的深度应该是右子树的深度+1
3.如果根节点既有右子树又有左子树,那么树的深度应该是深者的深度+1
因此可以利用递归实现
"""
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def TreeDepth(self, pRoot):
# write code here
if not pRoot:
return 0
left = self.TreeDepth(pRoot.left)
right = self.TreeDepth(pRoot.right)
if left > right:
return left + 1
else:
return right + 1
55.2:平衡二叉树
"""
思路一
1.利用”二叉树的深度“的程序计算所有节点左、右子树的深度
2.比较同一节点左右子树的深度差
缺点:
每遍历一个节点都会对其左右子树进行遍历,导致同一节点遍历多次
(节点的深度越深,遍历次数越多)
思路二
每个节点只遍历一次,并记录当前节点的深度(某一节点的深度等于它到叶节点的路径的长度)
从下往上遍历,
如果子树是平衡二叉树,则返回子树的高度;
如果发现子树不是平衡二叉树,一直返回-1
"""
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, pRoot):
# write code here
if self.TreeDepth(pRoot) != -1:
return True
return False
def TreeDepth(self,root):
if not root:
return 0
left = self.TreeDepth(root.left)
right = self.TreeDepth(root.right)
if abs(left-right)<=1 and left!=-1 and right!=-1:
return max(left,right)+1
else:
return -1
56:数组中只出现一次的两个数字
"""
思路一 时间复杂度O(n)空间复杂度O(n)
这也是最容易想到的思路,新建一个字典,用于存储数组中出现的元素,以及对应的出现次数。
找出值为1的元素对应的键
思路二 异或法 时间复杂度O(n)空间复杂度O(1)
两个相同数字异或=0,两个不同数字异或=1。一个数和0异或还是它本身。
假设:一个数组中只有一个数出现一个,其他的数都出现两次。
那么对改组的数进行异或,最终结果是出现次数为一次的数
依照这个思路,我们来看仅有两个数(我们假设是A、B)出现一次的数组。
我们首先还是先异或,最终肯定是A、B异或的结果,
这个结果的二进制中的1,表现的是A和B的不同的位。
我们就取第一个1所在的位数,假设是第n位,接着把原数组分成两类,分类标准是第n位是否为1。
如此,相同的数肯定在一个类,因为相同数字所有位都相同,
而不同的数,肯定不在一类。
然后把这两个类按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
在下面的程序中并没有真正的把原数组分为两组,
而是用a去异或数组中第n位等于1的数
用b去异或数组中第n位不等于1的数
a、b即为所求
"""
# -*- coding:utf-8 -*-
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# write code here
if not array:
return []
s1 = self.Xor(array)
index = 0
while not s1&1:
s1 >>= 1
index += 1
a,b = 0,0
for i in array:
if self.isBit(i, index):
a ^= i
else:
b ^= i
return [a, b]
def Xor(self,array):
res = 0
for i in array:
res ^= i
return res
#判断数i的从低到高的第index位是不是1
def isBit(self,i,index):
i = i >> index
return i & 1
56扩展-数组中仅有一个数出现一次,其他数都出现三次
57.1 和为s的两个数字
"""
思路:
假设:b>a且 a + b = s 、(a - m ) + (b + m) = s
则:(a - m )(b + m)=ab - (b-a)m - m*m < ab;
说明外层的乘积更小
两个指针
1.small指向数组开头,big指向数组结尾
2.如果和小于sum,说明太小了,small右移寻找更大的数
3.如果和大于sum,说明太大了,big左移寻找更小的数
4.和相等,返回small、big
"""
# -*- coding:utf-8 -*-
class Solution:
def FindNumbersWithSum(self, array, tsum):
# write code here
if len(array) < 2:
return []
small = 0
big = len(array)-1
while small < big:
if array[small]+array[big] == tsum:
return array[small],array[big]
elif array[small]+array[big] < tsum:
small += 1
else:
big -= 1
return []
57.2 和为s的连续整数序列
"""
思路
用两个数small和big分别表示序列的最小值和最大值。
首先把small初始化为1,big初始化为2。
如果从small到big的序列的和大于s,则可以从序列中去掉较小的值,也就是增大small的值。
如果从small到big的序列的和小于s,则可以增大big,让这个序列包含更多的数字。
因为这个序列至少要有两个数字,我们一直增加small到(1+s)/2为止。
"""
# -*- coding:utf-8 -*-
class Solution:
def FindContinuousSequence(self, tsum):
# write code here
if tsum < 3:
return []
st = []
small = 1
big = 2
while small < (tsum+1)//2:
if self.Sum(small,big) == tsum:
st.append([i for i in range(small,big+1)])
small += 1
elif self.Sum(small,big) < tsum:
big += 1
else:
small += 1
return st
def Sum(self,small,big):
return (small+big)*(big-small+1)//2
58.1 反转单词顺序列
"""
思路:
以“ ”将s分隔开,并且对分开的每一部分进行逆序操作
然后再以“ ”连接
"""
# -*- coding:utf-8 -*-
class Solution:
def ReverseSentence(self, s):
return " ".join(s.split(" ")[::-1])
58.2 左旋转字符串
"""
思路一:三次Reverse
设计一个Reverse(s,m,n),该函数可以对字符串s中的第m-n个数进行反转。
三次Reverse即可s=”abcXYZdef” k=3
例:
第一次Reverse(s,0,k-1)-->s=”cbaXYZdef”
第二次Reverse(s,k,len(s)-1)-->s=”cbafedZYX”
第三次Reverse(s,0,len(s)-1)-->s=”XYZdefabc”
思路二:先补后切
s=”abcXYZdef” k=3
l=len(s)
1.补
s+=s --> s=”abcXYZdefabcXYZdef”
2.切
s = s[k:l+k]
思路三:切片组合
s=”abcXYZdef” k=3
return s[k:]+s[:k]
"""
# -*- coding:utf-8 -*-
class Solution:
def LeftRotateString(self, s, k):
# write code here
return s[k:]+s[:k]
61 打扑克中的顺子
"""
思路:
1.先对numbers进行排序,并reverse
2.在对numbers中非零的数计算两个数之间的差值(差值-1=允许插入的整数数量=”间隔“)
3.比较零的数量,与第二部计算出来的的所有间隔之和
比如;
numbers=[5,7,0,4,8]
排序之后:numbers=[8,7,5,4,0]
间隔分别为(去掉numbers中的0)8-7-1=0,7-5-1=1,5-4-1=0
sum(间隔)=零的数量
return True
"""
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
# write code here
if not numbers:
return False
numbers.sort(reverse = True)
#记录间隔之和
index = 0
#记录0的数量
zero_index = 0
for i in range(0,4):
#之后两个数都不为0,且差值不为1的时候才计算”间隔“
if numbers[i] and numbers[i+1] and numbers[i]-numbers[i+1]!=1:
#出现相同的数字直接返回False
if numbers[i]-numbers[i+1] == 0:
return False
#间隔=numbers[i]-numbers[i+1])-1
index+=((numbers[i]-numbers[i+1])-1)
#一共四个王(0),numbers五个数,因此numbers[0]!=0,
#因此,从numbers[1]开始判断0的数量即可
elif numbers[i+1]==0:
zero_index += 1
if zero_index >= index:
return True
return False
62 圈中剩下的数字
"""
思路一
将n个编号转换为一个循环链表
思路二
每次删除数的下标是cur = (cur + m -1) % len(li)
"""
# -*- coding:utf-8 -*-
class Solution:
def LastRemaining_Solution(self, n, m):
# write code here
if not m or not n:
return -1
cur = 0
li=[i for i in range(n)]
while len(li)>1:
cur = (cur + m -1) % len(li)
li.pop(cur)
return li[0]
未归类–来自牛客剑指offer
把二叉树打印成多行
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表[[1,2],[4,5]]
def Print(self, pRoot):
# write code here
#用两个列表分别保存当前行节点和下一行节点
current = [pRoot]
next_layer = []
result = []
if pRoot is None:
return result
while current:
for i in current:
if i.left:
next_layer.append(i.left)
if i.right:
next_layer.append(i.right)
#每一层输出一行,重点在于下面的append([]),[]就表示了每层一行
result.append([i.val for i in current])
current,next_layer = next_layer,[]
return result
按之字形顺序打印二叉树
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Print(self, pRoot):
# write code here
current = [pRoot]
next_layer = []
result = []
deep = 0
if pRoot is None:
return result
while current:
deep += 1
for i in current:
if i.left:
next_layer.append(i.left)
if i.right:
next_layer.append(i.right)
if deep % 2 != 0:
result.append([i.val for i in current])
else:
#下面这两句话的含义是一样的,都是逆序遍历current[]
#result.append([current[len(current)-i-1].val for i in range(len(current))])
result.append([i.val for i in current[::-1]])
current,next_layer = next_layer,[]
return result
二叉搜索树的第k个结点
二叉搜索树
二叉搜索树也叫二叉排序树,它可以是一棵空树。它具有一下特点
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
- 没有键值(key)相等的节点
- 二叉搜索树的中序遍历结果是排好序的
二叉搜索树如下所示:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回对应节点TreeNode
def __init__(self):
self.li = []
self.serch_flag = False
self.node = None
def KthNode(self, pRoot, k):
# write code here
if k <= 0 or not pRoot:
return None
self.li = self.Tinorder(pRoot)
if len(self.li) < k:
return None
self.node = self.Serch(pRoot, self.li[k - 1])
return self.node
#二叉搜索树的中序遍历结果是排序好的
def Tinorder(self, pRoot):
"""中序遍历"""
if not pRoot:
return
self.Tinorder(pRoot.left)
self.li.append(pRoot.val)
self.Tinorder(pRoot.right)
return self.li
def Serch(self, pRoot, num):
"""数值搜索"""
if not pRoot:
return
if pRoot.val == num:
self.node = pRoot
self.Serch(pRoot.left,num)
self.Serch(pRoot.right,num)
return self.node
本题扩展–任意二叉树的第K个节点
思路:
- 首先利用前序遍历将二叉树存入列表
- 再对列表进行快速排序
- 然后在寻找节点
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回对应节点TreeNode
def __init__(self):
self.li = []
#self.serch_flag = False
self.node = None
def KthNode(self, pRoot, k):
# write code here
if k <= 0 or not pRoot:
return None
self.li = self.Perorder(pRoot)
if len(self.li) < k:
return None
self.li = self.Quick_sort(self.li, 0, len(self.li) - 1)
self.node = self.Serch(pRoot, self.li[k - 1])
return self.node
def Perorder(self, pRoot):
"""先序遍历"""
if not pRoot:
return
self.li.append(pRoot.val)
self.Perorder(pRoot.left)
self.Perorder(pRoot.right)
return self.li
def Quick_sort(self, alist, first, last):
"""快速排序"""
# 终止条件
if first >= last:
return alist
mid_value = alist[first]
low = first
high = last
while low < high:
while low < high and alist[high] >= mid_value:
high -= 1
alist[low] = alist[high]
while low < high and alist[low] < mid_value:
low += 1
alist[high] = alist[low]
alist[low] = mid_value
self.Quick_sort(alist, first, low - 1)
self.Quick_sort(alist, low + 1, last)
return alist
def Serch(self, pRoot, num):
"""数值搜索"""
if not pRoot:
return
if pRoot.val == num:
#self.serch_flag = True
self.node = pRoot
self.Serch(pRoot.left,num)
self.Serch(pRoot.right,num)
return self.node
滑动窗口的最大值
# -*- coding:utf-8 -*-
class Solution:
def maxInWindows(self, num, size):
# write code here
maxli = []
#有三种情况可以直接返回:
#num = none
#size = 0
#size > len(num)
if not num or not size or size > len(num):
return maxli
for i in range(len(num)-size+1):
maxli.append(max(num[i:size+i]))
return maxli
跳台阶
题目分析
题目实际为一个斐波那契数列(只不过f(1)=1、f(2)=2))
- 假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1)
- 假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
- 由1).2)假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)
# -*- coding:utf-8 -*-
class Solution:
def jumpFloor(self, number):
# write code here
if number == 0:
return
a , b = 1 ,1
while number > 0:
a , b = b , a+b
number-=1
return a
"""
if number == 0:
return 0
if number == 1:
return 1
if number == 2:
return 2
return self.jumpFloor(number-1) + self.jumpFloor(number-2)
"""
变态跳台阶
题目分析
由上一题“跳台阶”可以联想到:
- 假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1)
- 假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
- 。。。。。。
- 假定第一次跳的是n-1阶,那么剩下的是1个台阶,跳法是f(1) = 1
- 假定第一次跳的是n阶,那么剩下的是0个台阶,跳法是f(0) = 0
因此可以得到:
f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)+ f(n-1)
又因为:
f(n-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)
联立上面的两个式子可以得到:
f(n) = 2* f(n-1)、 f(1) = 1 、f(0) = 0
# -*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self, number):
# write code here
if number == 0:
return
if number == 1:
return 1
return 2*self.jumpFloorII(number-1)
矩形覆盖
题目分析
题目实际为一个斐波那契数列(只不过f(1)=1、f(2)=2))
# -*- coding:utf-8 -*-
class Solution:
def rectCover(self, number):
# write code here
if number == 0:
return 0
a,b = 1,1
while number > 0:
a,b = b,a+b
number -= 1
return a
"""
if number == 0:
return 0
if number == 1:
return 1
if number == 2:
return 2
return self.rectCover(number-1) + self.rectCover(number-2)
"""