69.X的平方根:实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
解题思路:本题是一道常见的面试题,面试官一般会要求面试者在不使用
x
\sqrt{x}
x函数的情况下,得到x的平方根的整数部分。一般的思路会有以下几种:
- 通过其它的数学函数代替平方根函数得到精确结果,取整数部分作为答案;
- 通过数学方法得到近似结果,直接作为答案。
「袖珍计算器算法」是一种用指数函数
e
x
p
exp
exp和对数函数
l
n
ln
ln代替平方根函数的方法。我们通过有限的可以使用的数学函数,得到我们想要计算的结果。我们将
x
\sqrt{x}
x 写成幂的形式
x
1
/
2
x^{1/2}
x1/2 ,再使用自然对数 e进行换底,即可得到:
x
=
x
1
/
2
=
(
e
ln
x
)
1
/
2
=
e
1
2
ln
x
\sqrt{x} = x^{1/2} = (e ^ {\ln x})^{1/2} = e^{\frac{1}{2} \ln x}
x=x1/2=(elnx)1/2=e21lnx
这样我们就可以得到
x
\sqrt{x}
x 的值了。
注意: 由于计算机无法存储浮点数的精确值(浮点数的存储方法可以参考 IEEE 754,这里不再赘述),而指数函数和对数函数的参数和返回值均为浮点数,因此运算过程中会存在误差。例如当 x = 2147395600时, e 1 2 ln x e^{\frac{1}{2}\ln x} e21lnx的计算结果与正确值 4634046340 相差 1 0 − 11 10^{-11} 10−11,这样在对结果取整数部分时,会得到 4633946339 这个错误的结果。
因此在得到结果的整数部分 ans \textit{ans} ans 后,我们应当找出 ans \textit{ans} ans与 ans + 1 \textit{ans} + 1 ans+1中哪一个是真正的答案。
class Solution:
def mySqrt(self, x: int) -> int:
if x == 0:
return 0
ans = int(math.exp(0.5*math.log(x)))
return ans+1 if (ans+1)**2<= x else ans
70.爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
解题思路:如果观察数学规律,可知本题是斐波那契数列,那么用斐波那契数列的公式即可解决问题。
class Solution:
def climbStairs(self, n: int) -> int:
import math
sqrt5 = 5**0.5
fibin = math.pow((1+sqrt5)/2,n+1)-math.pow((1-sqrt5)/2,n+1)
return int(fibin/sqrt5)
83.删除排序链表中的重复元素。
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
解题思路:这是一个简单的问题,仅测试你操作列表的结点指针的能力。由于输入的列表已排序,因此我们可以通过将结点的值与它之后的结点进行比较来确定它是否为重复结点。如果它是重复的,我们更改当前结点的 next 指针,以便它跳过下一个结点并直接指向下一个结点之后的结点。
26 题,有序数组删除重复元素,可使用「双指针」:不真正删除,找到后面不重复元素,依次覆盖前面元素,最后只返回不重复元素的长度。因为数组有下标,所以「双指针」实现比较简单。
此题需要返回一个没有重复元素的新链表,因为链表没有下标,如果使用「双指针」(双引用),后面覆盖前面,可行。但返回新链表,实现起来比较复杂。
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向上一个/或下一个节点的位置的链接(“links”)。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的访问往往要在不同的排列顺序中转换。而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
dummyHead,dummyHead.next = ListNode(0),head
while head != None and head.next != None:
if head.val == head.next.val:
head.next = head.next.next
else:
head = head.next
return dummyHead.next
169.给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于
n
/
2
n/2
n/2的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
解题思路:我们使用哈希映射(HashMap)来存储每个元素以及出现的次数。对于哈希映射中的每个键值对,键表示一个元素,值表示该元素出现的次数。
我们用一个循环遍历数组 nums 并将数组中的每个元素加入哈希映射中。在这之后,我们遍历哈希映射中的所有键值对,返回值最大的键。我们同样也可以在遍历数组 nums 时候使用打擂台的方法,维护最大的值,这样省去了最后对哈希映射的遍历。
Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。
import collections
most_common = collections.Counter(['a', 'a', 'b' ]).most_common()
print(most_common)
[('a', 2), ('b', 1)]
class Solution:
def majorityElement(self, nums: List[int]) -> int:
import collections
counts = collections.Counter(nums)
return max(counts.keys(),key = counts.get)
很好玩啊! 必须是list!不能是其他维度的,如下:
a = [
[1, 2, 3],
[4, 5, 6]
]
most_common = collections.Counter(a).most_common()
print(most_common)
TypeError: unhashable type: 'list'