除了算法题 可能没有让人更开心的了
从今天起,每天两个算法题呀
两数之和
题目链接:https://leetcode-cn.com/problems/two-sum
我的解法:
因为没有什么哈希表的知识,刚开始重拾算法题,已经回到铁憨憨的状态了。所以写了一个平时的工程的解法,
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
for i in range(0, len(nums) ):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return[i,j]
这个程序的运行的时间全凭在数组中的位置决定了。
也想过如何简化,如排序一下,如果和比较小,那些大于他的可以被丢掉了。
大佬们的解法都是哈希表。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashmap = {}
for idx, num in enumerate(nums):
if target - num in hashmap:
return [hashmap[target - num],idx]
else:
hashmap[num] = idx
只出现一次的数字
题目链接:https://leetcode-cn.com/problems/single-number/
要求是非空整数数组,且某个元素只出现一次,并找出他。
要求 线性时间复杂度,且不使用额外空间。
最近都用的python,选择的python2 的编辑器(python3的没太看懂)
我的解法:
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in nums:
if nums.count(i)==1:
return i
利用了列表的自带函数计数功能,自己计算为一次的,
算是个投机取巧吧。
在解题区看到了很多的大佬解法:
- 使用了异或
大佬用遍历数组,并使用异或相加,和自己的异或相当于0,最后剩下的数字就是唯一的数字。
for i in nums:
a ^= i
return a
- 数学解法
2∗(a+b+c)−(a+a+b+b+c)=c
把列表编程集合,然后✖️2,减去列表的值 的和
return 2 * sum(set(nums)) - sum(nums)
- 哈希表
我们用哈希表避免每次查找元素是否存在需要的 O(n)O(n) 时间。
遍历 nums 中的每一个元素
查找 hash_table 中是否有当前元素的键
如果没有,将当前元素作为键插入 hash_table
最后, hash_table 中仅有一个元素,用 popitem 获得它
多数元素
题目链接:https://leetcode-cn.com/problems/majority-element/
此题目 超过了70% 的代码。还可以。
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
value = int(len(nums)/2)
for i in set(nums):
if nums.count(i)>value:
return i
这种计数问题,可以确定的是,超过半数的数字一定有且只有一个。所以在计数过程中如果出现了超过半数的数字,我们可以立刻返回。
我的想法是,将列表转化为字典,然后对字典里的值依次求次数,发现超过半数的值,就返回。
- 摩尔投票法:
也是一种数学的想法,读了很多遍才想明白。
2.api方法
使用 排序的方法,将列表排序,然后返回列表中中间位置的数字。
Arrays.sort(nums);
return nums[nums.length / 2];
合并两个有序数组
class Solution(object):
def merge(self, nums1, m, nums2, n):
"""
:type nums1: List[int]
:type m: int
:type nums2: List[int]
:type n: int
:rtype: None Do not return anything, modify nums1 in-place instead.
"""
item = 0
length = m
if m==0:
for i in range(0,len(nums2)):
nums1[i] = nums2[i]
else:
for i in nums2:
for j in range(item,length):
if i > nums1[length-1]:
nums1.insert(length, i)
nums1.pop(-1)
length = length + 1
item = j + 1
break
if i <=nums1[j]:
nums1.insert(j , i)
nums1.pop(-1)
length = length + 1
item = j + 1
break
elif i > nums1[j]:
if i <= nums1[j+1]:
nums1.insert(j+1,i)
nums1.pop(-1)
length = length +1
item = j+1
#print(item)
break
这个代码居然打败了99%的人的算法运行速度。但是其实并不好。因为占的空间多。我的想法就是,遍历第二个数组将其和第一个数组的元素挨个比较,插入并删除最后面的那个0。
- 小于某个值,就插入在当前位置
- 大于值a,小于值a的下一个,就插入在a的后面,
- 如果大于列表1最大值,直接插入列表一最后一个值得后面。
过程中发现,列表1 会有为空的情况,还额外写了一个判断来避免这种情况。
看题解的话,就是惊呆了。
方法一 : 合并后排序
最朴素的解法就是将两个数组合并之后,再排序。
将数组2替换数组1的0的值,
这种方法的时间复杂度较差,为O((n + m)\log(n + m))O((n+m)log(n+m))。这是由于这种方法没有利用两个数组本身已经有序这一点。
方法二: 双指针 / 从后往前
确实比我的方法高明了不少
搜索二维矩阵 II
链接:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/
因为昨天学习了指针 今天也想学高端解法 最终也没发现什么数学解法。
写的解题方案也不是特别的高明。就不放上来。
因为矩阵的行和列是排序的(分别从左到右和从上到下),所以在查看任何特定值时,我们可以修剪O(m)或O(n)元素。
算法:
首先,我们初始化一个指向矩阵左下角的 (row,col) 指针。然后,直到找到目标并返回 true(或者指针指向矩阵维度之外的 (row,col)为止,我们执行以下操作:如果当前指向的值大于目标值,则可以 “向上” 移动一行。 否则,如果当前指向的值小于目标值,则可以移动一列。不难理解为什么这样做永远不会删减正确的答案;因为行是从左到右排序的,所以我们知道当前值右侧的每个值都较大。 因此,如果当前值已经大于目标值,我们知道它右边的每个值会比较大。也可以对列进行非常类似的论证,因此这种搜索方式将始终在矩阵中找到目标(如果存在)。
验证回文串
class Solution(object):
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
import re
new = re.findall("[0-9A-Za-z]",s)
length = int(len(new)/2)
flag = True
for i in range(0,length):
if new[i].upper() == new[len(new)-1-i].upper():
pass
else:
flag=False
return flag
今天这道题,本来看起来不是很难,但是自己真心想了半天 不会做。知道要用递归来解答,但是有种无从下手的感觉。
因为之前没有接触过这种问题,这次好好学习。
希望下一次就可以解答出来了吧!
网上给出的解答方式为:回溯法
回溯法的核心思想就是递归,虽然其过程逻辑很清楚,而且执行效率很高。但缺点也是与之对应的,逻辑清楚,必然其抽象性很高,所以有时候就是这样的情况:看它的解题过程很容易看懂,但要是让你自己动手写这个递归过程,发现很难下笔。
描述:
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
其实总结起来就3点:
1 出口。
一个递归算法一定要有出口,否则就是一个死循环了。出口语句一般都挺好写的,但 是出口语句该放在哪儿了,这个就是关键了。
这个语句其实挺好写的,一般也就2-3行代码,大多数人都能想出来。但大多数人苦恼的就是不知道该把它放在哪儿,把出口语句放在递归函数的第一行就行。
2 递归函数的参数。
一般情况下,递归函数是要带参数的,因为递归操作都是用来处理下一次的过程,如果没有参数的话,那么就很难从下一次的操作回溯到当前操作了。
这个递归函数的参数的设置也是有很大门道的,设置的好就很容易得到答案,否则弄大半天可能还是没有一点反应。大家一定要记住一点:这个参数是随着每一次的递归操作而发生改变的。而回溯法很关键的一点就是:如果当前操作行不通,如何回溯到上一步操作。大家继续看上面贴的两个递归函数的参数,会发现其参数都是要改变的,既然参数会发生改变,那么我们要如何保存其上一步操作的值呢?
结果一定是要有一个全局参数来保存,这个全局参数不会随着每一次的递归操作而随时改变,它只是用来保存每一次递归操作成功时的结果,其它的不关它的事。在一开始就定义了一个List空列表。大家也可以照搬的,凡是结果需要保存的题目90%以上就是要预先定义一个List空列表
3 递归函数的处理过程。
这个自不必多说,重中之重,需要好好理解其过程
如果当前递归过程的处理参数符合要求,则执行相关赋值或其它操作,然后转入下一次递归,如果下一次递归不能找到出口,则把之前相关赋值或其它操作重置为初始状态。
加一
题目链接:https://leetcode-cn.com/problems/plus-one/
没什么难度
看评论很多人没看懂题目。然后需要考虑很大的数字的情况,内存溢出问题。我没想过*10去算,毕竟加1就行了。
我的解法:
class Solution(object):
def plusOne(self, digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
value = ''
for i in digits:
value = value+str(i)
aList= []
for i in str(int(value)+1):
aList.append(int(i))
return aList
就是先把列表转化为数字,在把数字转化为列表。
存在重复元素
网页链接:https://leetcode-cn.com/problems/contains-duplicate/
我的解法使用了字典,把他变成了字典,然后比较长度即可。
class Solution(object):
def containsDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
set_=set(nums)
list_ = list(set_)
if len(nums) == len(list_):
return False
else :
return True
字符串中的第一个唯一字符
题目链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string/
看了大神的解法表示 再简单的题,大神也能写出花来。
整数反转
外观数列
一遍过 太不容易了
看评论区一堆人抖机灵的回答~
我的想法比较 普通,就是有一个count 一个num,比较下一位和当前值的区别,如果相同的话就加count,不同的话就变num和str。
def countAndSay(n):
"""
:type n: int
:rtype: str
"""
n_list = ['1']
for i in range(0,n):
if len(n_list)<i+1:
print(i+1,n_list[-1])
flag = False
while flag!=True:
#print(n_list[-1][:])
num = n_list[-1][0]
count = 1
strn = ''
for item in range(1,len(n_list[-1])):
print(n_list[-1][item])
if num==n_list[-1][item]:
count +=1
else:
strn = strn+str(count)+str(num)
num = n_list[-1][item]
count = 1
strn = strn + str(count) + str(num)
n_list.append(strn)
break
return n_list[-1]
print(countAndSay(5))
买卖股票的最佳时期
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
def maxProfit( prices):
"""
:type prices: List[int]
:rtype: int
"""
num =prices[0]
profit = 0
for i in range(1,len(prices)):
#print(prices[i],num)
if prices[i] > num:
profit = prices[i] - num +profit
num = prices[i]
else:
num = prices[i]
return profit
print(maxProfit([1,2,3,4,5]))
题目比较简单 懒得思考 没有优化代码
反转字符串
链接:https://leetcode-cn.com/problems/reverse-string/
没什么难度,就是不可以使用return。
必须真正修改原先的列表
所以为了减少时间复杂度,遍历字符串长度一半,进行头尾的数字交换即可。
注意,需要用一个临时变量来存储其中的值。
字符串转换整数 (atoi)
题目链接:https://leetcode-cn.com/problems/string-to-integer-atoi/
这道题的难度居然算是中等,我认为这个是真的烦
没啥意思
他有很多隐藏的内容,得测试的时候才发现有问题。
比如 ±2 是错误的,返回0
比如 +2 0,会返回2…… 需要实际试错
我的想法非常的复杂。。也懒得简化了
class Solution(object):
def digit(self, i):
num = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']
if i in num:
return True
return False
def myAtoi(self, str):
"""
:type str: str
:rtype: int
"""
value = ''
# str = str.replace(" ", "")
flag = True
if len(str.replace(" ", ""))==0 or len(str) == 0:
return 0
elif len(str) == 1 and self.digit(str[0]) == True:
return int(str) #就一个数字
elif len(str) == 1 and self.digit(str[0]) == False:
return 0
newStr = ""
space = False
for i in range(0,len(str)):
if str[i] == " ":
if space == True:
break
else:
newStr = newStr + str[i]
space = True
str = newStr
if self.digit(str[0]) == False and str[0] != "-" and str[0] != "+":
return 0
elif str[0] == '-':
value = value + '-'
for i in range(1, len(str)):
if flag:
if self.digit(str[i]):
value = value + str[i]
else:
flag = False
elif str[0] == '+':
for i in range(1, len(str)):
if flag:
if self.digit(str[i]):
value = value + str[i]
else:
flag = False
else:
for i in range(0, len(str)):
if flag:
if self.digit(str[i]):
value = value + str[i]
else:
flag = False
print(value)
if len(value) <= 1 and self.digit(value)==False:
return 0
else:
num = int(value)
if num > 2147483648:
return 2147483648
elif num < -2147483648:
return -2147483648
else:
return num
a = Solution()
print(a.myAtoi(" "))
我的想法确实比较复杂,看了一下大佬的解法。确实简单了一点点。相差无几。
但是确实这种题正则表达式就是一行的事情。
class Solution:
def myAtoi(self, s: str) -> int:
return max(min(int(*re.findall('^[\+\-]?\d+', s.lstrip())), 2**31 - 1), -2**31)
两个数的交集
题目链接:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/