两数之和,梦开始的地方
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。 示例 1:
输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6 输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6 输出:[0,1]
提示:
2 <= nums.length <= 104 -109 <= nums[i] <= 109 -109 <= target <= 109 只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
暴力枚举,当我们使用遍历整个数组的方式寻找target - x
时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x
。
1 2 3 4 5 6 7 8 9 10 11 class Solution : def twoSum (self, nums: List [int ], target: int ) -> List [int ]: n = len (nums) for i in range (n): for j in range (i + 1 , n): if nums[i] + nums[j] == target: return [i, j] return []
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution { public int [] twoSum(int [] nums, int target) { int n = nums.length; for (int i = 0 ; i < n; ++i) { for (int j = i + 1 ; j < n; ++j) { if (nums[i] + nums[j] == target) { return new int []{i, j}; } } } return new int [0 ]; } }
我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
1 2 3 4 5 6 7 class Solution : def twoSum (self, nums: List [int ], target: int ) -> List [int ]: point: dict [int , int ] = {} for index, value in enumerate (nums): if (target - value) in point: return [index, point[target - value]] point[value] = index
1 2 3 4 5 6 7 8 9 10 11 12 class Solution { public int [] twoSum(int [] nums, int target) { HashMap<Integer, Integer> point = new HashMap <>(); for (int i = 0 ; i < nums.length; i++) { if (point.containsKey(target - nums[i])) { return new int []{point.get(target - nums[i]), i}; } point.put(nums[i], i); } return null ; } }
回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
例如,121 是回文,而 123 不是。
示例 1:
输入:x = 121 输出:true 示例 2:
输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是>一个回文数。 示例 3:
输入:x = 10 输出:false 解释:从右向左读, 为 01 。因此它不是一个回文数。
提示:
-231 <= x <= 231 - 1
进阶:你能不将整数转为字符串来解决这个问题吗?
转化为字符串的时候进行题解就很方便,这也是我为什么选择使用python进行算法解题的原因,这样真的很简单
1 2 3 4 class Solution : def isPalindrome (self, x: int ) -> bool : return str (x) == str (x)[::-1 ]
1 2 3 4 5 6 7 8 class Solution { public boolean isPalindrome (int x) { String string = String.valueOf(x); String reverse = new StringBuffer (string).reverse().toString(); return string.equals(reverse); } }
映入脑海的第一个想法是将数字转换为字符串,并检查字符串是否为回文。但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。 第二个想法是将数字本身反转,然后将反转后的数字与原始数字进行比较,如果它们是相同的,那么这个数字就是回文。但是,如果反转后的数字大于 int.MAX
,我们将遇到整数溢出问题。 按照第二个想法,为了避免数字反转可能导致的溢出问题,为什么不考虑只反转 int 数字的一半?毕竟,如果该数字是回文,其后半部分反转后应该与原始数字的前半部分相同。 例如,输入 1221,我们可以将数字 “1221” 的后半部分从 “21” 反转为 “12”,并将其与前半部分 “12” 进行比较,因为二者相同,我们得知数字 1221 是回文。 首先,我们应该处理一些临界情况。所有负数都不可能是回文,除了0以外,所有个位是0的数字不可能是回文,因为最高位不等于0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution : def isPalindrome (self, x: int ) -> bool : if x < 0 or (x % 10 == 0 and x != 0 ): return False reversed_number: int = 0 while x > reversed_number: reversed_number = reversed_number * 10 + x % 10 x = x // 10 return x == reversed_number or x == reversed_number // 10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Solution { public boolean isPalindrome (int x) { if (x < 0 || (x % 10 == 0 && x != 0 )) { return false ; } int reversedNumber = 0 ; while (x > reversedNumber) { reversedNumber = reversedNumber * 10 + x % 10 ; x = x / 10 ; } return x == reversedNumber || x == reversedNumber / 10 ; } }
罗马数字转整数
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 >1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。 给定一个罗马数字,将其转换成整数。
示例 1:
输入: s = “III” 输出: 3 示例 2:
输入: s = “IV” 输出: 4 示例 3:
输入: s = “IX” 输出: 9 示例 4:
输入: s = “LVIII” 输出: 58 解释: L = 50, V= 5, III = 3. 示例 5:
输入: s = “MCMXCIV” 输出: 1994 解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15 s 仅含字符 (‘I’, ‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’) 题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。 IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
通常情况下,罗马数字中小的数字在大的数字的右边。若输入的字符串满足该情况,那么可以将每个字符视作一个单独的值,累加每个字符对应的数值即可。 如 XXVII 可视作 X+X+V+I+I=10+10+5+1+1=27。 若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字右侧的数字比它大,则将该数字的符号取反。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def romanToInt (self, s: str ) -> int : c2n = { "I" : 1 , "V" : 5 , "X" : 10 , "L" : 50 , "C" : 100 , "D" : 500 , "M" : 1000 , } sum = 0 for index, c in enumerate (s): if index < len (s) - 1 and c2n[c] < c2n[s[index+1 ]]: sum -= -c2n[c] continue sum += c2n[c] return sum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public int romanToInt (String s) { Map<Character, Integer> pairs = Map.of('I' , 1 , 'V' , 5 , 'X' , 10 , 'L' , 50 , 'C' , 100 , 'D' , 500 , 'M' , 1000 ); int result = 0 ; for (int i = 0 ; i < s.length(); i++) { char ch = s.charAt(i); if (i < s.length() - 1 && pairs.get(ch) < pairs.get(s.charAt(i + 1 ))) { result -= pairs.get(ch); continue ; } result += pairs.get(ch); } return result; } }
最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入:strs = [“flower”,”flow”,”flight”] 输出:”fl” 示例 2:
输入:strs = [“dog”,”racecar”,”car”] 输出:”” 解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200 0 <= strs[i].length <= 200 strs[i] 如果非空,则仅由小写英文字母组成
用 LCP(S1…Sn)表示字符串S1…Sn的最长公共前缀。可以得到以下结论LCP(S1…Sn)=LCP(LCP(LCP(S1,S2),S3),…Sn)
基于该结论,可以得到一种查找字符串数组中的最长公共前缀的简单方法。依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。如果在尚未遍历完所有的字符串时,最长公共前缀已经是空串,则最长公共前缀一定是空串,因此不需要继续遍历剩下的字符串,直接返回空串即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution : def longestCommonPrefix (self, strs: List [str ] ) -> str : if not strs: return "" if len (strs) == 1 : return strs[0 ] prefix = strs[0 ] for s in strs[1 :]: prefix = self.lcp(prefix, s) if prefix == "" : return "" return prefix def lcp (self, s1: str , s2: str ) -> str : s = "" for i in range (min (len (s1), len (s2))): if s1[i] == s2[i]: s += s1[i] else : break return s
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public String longestCommonPrefix (String[] strs) { if (strs == null || strs.length == 0 ) { return "" ; } String prefix = strs[0 ]; int count = strs.length; for (int i = 1 ; i < count; i++) { prefix = longestCommonPrefix(prefix, strs[i]); if (prefix.length() == 0 ) { break ; } } return prefix; } public String longestCommonPrefix (String str1, String str2) { int length = Math.min(str1.length(), str2.length()); int index = 0 ; while (index < length && str1.charAt(index) == str2.charAt(index)) { index++; } return str1.substring(0 , index); } }
方法二: 方法一是横向扫描,依次遍历每个字符串,更新最长公共前缀。另一种方法是纵向扫描。纵向扫描时,从前往后遍历所有字符串的每一列,比较相同列上的字符是否相同,如果相同则继续对下一列进行比较,如果不相同则当前列不再属于公共前缀,当前列之前的部分为最长公共前缀。
1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def longestCommonPrefix (self, strs: List [str ] ) -> str : if not strs: return "" length, count = len (strs[0 ]), len (strs) for i in range (length): c = strs[0 ][i] if any (i == len (strs[j]) or strs[j][i] != c for j in range (1 , count)): return strs[0 ][:i] return strs[0 ]
有效的括号
给定一个只包括 ‘(‘,’)’,’{‘,’}’,’[‘,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “()[]{}”
输出:true
示例 3:
输入:s = “(]”
输出:false
示例 4:
输入:s = “([])”
输出:true
提示:
1 <= s.length <= 104 s 仅由括号 ‘()[]{}’ 组成
判断括号的有效性可以使用「栈」这一数据结构来解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution : def isValid (self, s: str ) -> bool : if not s or len (s) % 2 == 1 : return False list1: list [str ] = [] for c in s: if c in ["{" , "[" , "(" ]: list1.append(c) if c == "}" : if list1 and list1[-1 ] == "{" : list1.pop() else : return False if c == "]" : if list1 and list1[-1 ] == "[" : list1.pop() else : return False if c == ")" : if list1 and list1[-1 ] == "(" : list1.pop() else : return False return not list1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public boolean isValid (String s) { if (s.length() % 2 == 1 ) { return false ; } Deque<Character> stack = new LinkedList <>(); Map<Character, Character> pairs = Map.of('}' , '{' , ']' , '[' , ')' , '(' ); for (int i = 0 ; i < s.length(); i++) { char ch = s.charAt(i); if (pairs.containsKey(ch)) { if (stack.isEmpty()) { return false ; } if (stack.peek() != pairs.get(ch)) { return false ; } stack.pop(); continue ; } stack.push(ch); } return stack.isEmpty(); } }
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给>定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 示例 2:
输入:l1 = [], l2 = [] 输出:[] 示例 3:
输入:l1 = [], l2 = [0] 输出:[0]
提示:
两个链表的节点数目范围是 [0, 50] -100 <= Node.val <= 100 l1 和 l2 均按 非递减顺序 排列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution : def mergeTwoLists (self, l1: ListNode, l2: ListNode ) -> ListNode: prev = prehead = ListNode(-1 ) while l1 and l2: if l1.val <= l2.val: prev.next = l1 l1 = l1.next else : prev.next = l2 l2 = l2.next prev = prev.next prev.next = l1 if l1 is not None else l2 return prehead.next
删除有序数组中的重复项
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元>素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺>序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可>以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最>初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重>要。 返回 k 。 判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = […]; // 输入数组 int[] expectedNums = […]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length; for (int i = 0; i < k; i++) { assert nums[i] == expectedNums[i]; } 如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修>改为 1, 2 。不需要考虑数组中超出新长度后面的元素。 示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4] 解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修>改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
提示:
1 <= nums.length <= 3 * 104 -104 <= nums[i] <= 104 nums 已按 非严格递增 排列
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution : def removeDuplicates (self, nums: List [int ] ) -> int : if not nums: return 0 length = len (nums) fast = slow = 1 while fast < length: if nums[fast] != nums[fast-1 ]: nums[slow] = nums[fast] slow+=1 fast+=1 return slow
移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 >val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同>的元素的数量。
假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以>下操作:
更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。>nums 的其余元素和 nums 的大小并不重要。 返回 k。 用户评测:
评测机将使用以下代码测试您的解决方案:
int[] nums = […]; // 输入数组 int val = …; // 要移除的值 int[] expectedNums = […]; // 长度正确的预期答案。 // 它以不等于 val 的值排序。
int k = removeElement(nums, val); // 调用你的实现
assert k == expectedNums.length; sort(nums, 0, k); // 排序 nums 的前 k 个元素 for (int i = 0; i < actualLength; i++) { assert nums[i] == expectedNums[i]; } 如果所有的断言都通过,你的解决方案将会 通过。
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2,, ] 解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 >2。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评>测)。 示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3,, ,_] 解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,>1,3,4。 注意这五个元素可以任意顺序返回。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评>测)。
提示:
0 <= nums.length <= 100 0 <= nums[i] <= 50 0 <= val <= 100
1 2 3 4 5 6 7 8 class Solution : def removeElement (self, nums: List [int ], val: int ) -> int : left = 0 for i in range (len (nums)): if nums[i] != val: nums[left] = nums[i] left += 1 return left