学习内容:
每日三题
1、 旋转数组输出最小值
2、 斐波那契数列的第
n
n
n项
3、 链表的反序
一、旋转数组的最小值
问题:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
这道题主要考察了我们对于二分查找知识点的掌握
作为一个小白,我最初的想法很简单:这就是一个简单的排序问题,直接用冒泡排序对数组进行排序,然后输出最小值即可。毫无疑问,
O
(
n
2
)
O(n^{2})
O(n2)的时间复杂度是难以满足要求的。在重新阅读题目之后,我发现旋转数组的条件是没有用到的,简单来说一个非递减排序额的旋转数组是有两部分:非递减的
l
i
s
t
[
R
o
t
a
t
e
_
P
o
s
:
]
list[Rotate\_Pos:]
list[Rotate_Pos:]以及
l
i
s
t
[
0
:
R
o
t
a
t
e
_
P
o
s
]
list[0:Rotate\_Pos]
list[0:Rotate_Pos],结合已知信息我们怎样利用二分搜索的方法来实现需求呢?
分析过程:
二分查找的思路我们是很清楚的,每次查找后,确定目标值所在的位置即在
m
i
d
mid
mid的哪一侧,从而减少搜索的长度。这道题二分查找难就难在
l
i
s
t
[
m
i
d
]
list[mid]
list[mid]跟谁比.
一般的比较原则有:
- 如果有目标值target,那么直接让 l i s t [ m i d ] list[mid] list[mid] 和 t a r g e t target target 比较即可。;
- 如果没有目标值,一般可以考虑端点
这里我们把target 看作是右端点,来进行分析,那就要分析以下三种情况,看是否可以达到上述的目标。
c
a
s
e
1
case1
case1 :[4,5,6,1,2,3]
l
i
s
t
[
m
i
d
]
=
6
>
t
a
r
g
e
t
=
1
list[mid]=6> target=1
list[mid]=6>target=1
根据数组的非递减性质,说明
[
f
i
r
s
t
.
.
.
m
i
d
]
[first ... mid]
[first...mid]都是
≥
t
a
r
g
e
t
\geq target
≥target 的所以可以确定答案为
[
m
i
d
+
1...
l
a
s
t
]
[mid+1...last]
[mid+1...last]区间,所以
f
i
r
s
t
=
m
i
d
+
1
first = mid + 1
first=mid+1。
c
a
s
e
2
case2
case2 : [5,6,1,2,3,4]
l
i
s
t
[
m
i
d
]
=
1
<
t
a
r
g
e
t
=
4
list[mid]=1< target=4
list[mid]=1<target=4
说明答案肯定不在[mid+1…last],但是
l
i
s
t
[
m
i
d
]
list[mid]
list[mid]有可能是答案,所以答案在
[
f
i
r
s
t
,
m
i
d
]
[first, mid]
[first,mid]区间,所以
l
a
s
t
=
m
i
d
last = mid
last=mid
c
a
s
e
3
case3
case3 :
l
i
s
t
[
m
i
d
]
=
t
a
r
g
e
t
list[mid]= target
list[mid]=target
这种情况是比较特殊的,我们需要逐步缩小区间去搜索目标值,也就是每次所
如果是
[
1
,
0
,
1
,
1
,
1
]
[1,0,1,1,1]
[1,0,1,1,1],
l
i
s
t
[
m
i
d
]
=
t
a
r
g
e
t
=
1
list[mid]=target=1
list[mid]=target=1, 显然答案在左边;
如果是
[
1
,
1
,
1
,
0
,
1
]
[1,1,1,0,1]
[1,1,1,0,1],
l
i
s
t
[
m
i
d
]
=
t
a
r
g
e
t
=
1
list[mid]=target=1
list[mid]=target=1, 显然答案在右边;
所以这种情况,不能确定答案在左边还是右边,那么就让
l
a
s
t
=
l
a
s
t
−
1
last = last - 1
last=last−1;慢慢缩少区间,同时也不会错过答案。
代码
class Solution:
def minNumberInRotateArray(self, rotateArray):
if len(rotateArrary)==0 :
return 0
#长度为0 返回0
elif len(rotateArrary)==1 :
return rotateArrary[0]
else:
left = 0
right = len(rotaArray)-1
start = 0
while left<right:
mid = (left+right)//2
if rotaArray[start] == rotaArray[mid]:
#中间点大于左端点 first = mid+1 处于递增序列中
start = strat+1
left = strat
elif rotaArray[start] < roraArray[mid]:
left = mid+1
else:
right = mid
return rotaArray[left]
class Solution1:#递归写法
def minNumberInRotateArray(self, rotateArray):
if len(rotaArray)==1:
return rotaArray[0]
elif len(rotaArray)==2:
if rotaArray[0]>rotaArray[1]
return rotaArray[1]
else:
return rotaArray[0]
else:
mid = len(rotaArray)//2
target = rotaArray[-1] #右端点
if rotaArray[mid]>target:
return self.minNumberInRotateArray(rotateArray[mid+1:])
elif rotaArray[mid]==target:
return self.minNumberInRotateArray(rotateArray[:-1])
else:
return self.minNumberInRotateArray(rotateArray[:mid+1])
二、斐波那契数列
问题:现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
这个问题废话不多说直接上代码
class Solution:
#递归写法 简介明了 缺点慢 真的慢
def Fibonacci(self, n):
if n==0:
return 0
elif n ==1 or n==2:
return 1
else:
return self.Fibonacci(n-1)+self.Fibonacci(n-2)
class Solution:
#递归实际上做了多次重复计算 所以我们设计自底向上的计算 依次计算每项
def Fibonacci(self, n):
if n==0:
return 0
elif n ==1 or n==2:
return 1
else:
a,b = 0 ,1
sums = 0
i = 1
while i <n:
sums = a+b
a,b = b,sums
i = i+1
return sums
#矩阵方法 https://blog.csdn.net/lamusique/article/details/89161831
表达式方法:
class Solution:
def Fibonacci(self, n):
a = math.sqrt(5)/5
b = pow((1+math.sqrt(5))/2,n)
c = pow((1-math.sqrt(5))/2,n)
result = int(a*(b-c))
return result
三、链表的反序
输入一个链表,反转链表后,输出新链表的表头。
关于这道题的分析参见博客:https://blog.csdn.net/gongliming_/article/details/88712221
分析的很好
原来的链表
反转后的链表
反转过程
我们需要三个节点来描述每一步的情况,
p
r
e
v
i
o
u
s
−
p
r
e
,
c
u
r
r
e
n
t
−
c
u
r
,
n
e
x
t
−
n
e
x
t
s
previous-pre,current-cur,next-nexts
previous−pre,current−cur,next−nexts,每次的反转过程都有4个子程序。在开始之前,我们先看一下现在节点的状态:
p
r
e
=
N
o
n
e
,
c
u
r
=
L
i
s
t
.
h
e
a
d
,
n
e
x
t
s
=
L
i
s
t
.
h
e
a
d
.
n
e
x
t
pre=None,cur=List.head,nexts=List.head.next
pre=None,cur=List.head,nexts=List.head.next。接下来进行第一次反转,第一步:指向下一个节点
n
e
x
t
s
=
h
e
a
d
.
n
e
x
t
nexts=head.next
nexts=head.next,第二步:将当前头部的下一个节点设置为前一个
h
e
a
d
.
n
e
x
t
=
p
r
e
head.next=pre
head.next=pre这一步是实现反向的重要步骤,除了第一步外其他步都是将当前的节点指向前一个节点(反向过程),第三步:当前节点设置为前一个节点
p
r
e
=
h
e
a
d
pre=head
pre=head,第四步:将下一个节点设置为头部节点
h
e
a
d
=
h
e
a
d
.
n
e
x
t
head=head.next
head=head.next
代码
class ListNode:
def __init__(self,item):
self.val = item
self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
if pHead ==None or pHead.next==None:
return pHead
else:
pre= None
cur=pHead
nexts = None
while cur!=None:
nexts = pHead.next
pHead.next = pre
pre = pHead
pHead = nexts
return pre#头部节点