为了以后的工作,要开始刷题啦!!!!
技术宅拯救世界!!!!!!!!!!!
先从简单题开始,慢慢来~~~~~~~~~~~~
尽量保证每天至少一道题吧!!!!!!
https://www.bilibili.com/video/BV1BC4y187bZ/?spm_id_from=333.788.recommend_more_video.2
简单题的答案就不贴到这里了,一般大家都会,这里仅仅提供我的一些小总结。
方法
- hashMap的get put 实现存储和获取
- hashMap的containsKey 实现查找
- Integer.MAX_VALUE 最大边界
- StringBuilder(x + “”)).reverse() 缓冲字符串的倒转
- string,charAt(int index); 返回字符串中指定索引的的字符
- string,toCharArrayt(); 字符串转换成字符数组。
两数之和
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
提示:
2 <= nums.length <= 103
-109 <= nums[i] <= 109
-109 <= target <= 109
- 只会存在一个有效答案
我的理解 1. 类似于二维数组求和一样的暴力求和算法 2.应用哈希表的的逆向求和算法
- 这里就不说了,小学二年级都会(滑稽)
- 这里说一下,在开始的hashtable中是没有键值对的,在后面循环中,慢慢向里面插入键值对。把后面的要插入的数字当作是num[i],在前面的数字中寻找target-num[i],直到到找到为止,和普通的遍历算法略有区别。
- 小结: hashmap可以减小时间复杂度;在hashmap中用 containsKey(Object key)和containsValue(Object value) 来判断是否存在相应的键或值,同时配合 get(Object key)和put(K key, V value)来实现hashmap中的存取。
整数反转
给你一个 32 位的有符号整数
x
,返回将x
中的数字部分反转后的结果。如果反转后整数超过 32 位的有符号整数的范围
假设环境不允许存储 64 位整数(有符号或无符号)。[−231, 231 − 1]
,就返回 0。
示例 1:
输入:x = 123 输出:321
示例 2:
输入:x = -123 输出:-321
示例 3:
输入:x = 120 输出:21
示例 4:
输入:x = 0 输出:0
提示:
-231 <= x <= 231 - 1
我的理解 1. 遇到有关数字的问题就直接差分成数字去进行处理会比较简单。 2.判断条件的确定
在判断条件这里有两点,一个是正常的不等式关系,另一个是溢出问题,因为都是int类型,当大于Integer.MAX_VALUE时会有溢出的现象发生,如果继续加得到的数会比其小,因此虽然是32位整数,但是比较的是第31位置和前31相同时的最后一位。对于正数为231-1=2147483647,末位为7,对于负数-232=-2147483648,末尾为8 所以多了判断条件。
回文数
给你一个整数
x
,如果x
是一个回文整数,返回true
;否则,返回false
。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,
121
是回文,而123
不是。
示例 1:
输入:x = 121 输出:true
示例 2:
输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入:x = 10 输出:false 解释:从右向左读, 为 01 。因此它不是一个回文数。
示例 4:
输入:x = -101 输出:false
提示:
-231 <= x <= 231 - 1
我的理解 1. 遇到有关数字的问题就直接差分成数字去进行处理会比较简单。但是也要尽量不使用数组,因为数组是开辟一块固定的空间。
// 通过”/div“和来取左边的数字,通过“%10”来取右边的数字。
int div = 1;
while (x / div >= 10) div *= 10;
while (x > 0) {
int left = x / div;
int right = x % 10;
if (left != right) return false;
x = (x % div) / 10;
div /= 100;
}
2 字符串缓冲暴力法 通过使用StringBuilder的reverse实现字符串的调转 以及equals 进行比较
public boolean isPalindrome(int x) {
String reversedStr = (new StringBuilder(x + "")).reverse().toString();
return (x + "").equals(reversedStr);
}
RomanToInteger
罗马数字包含以下七种字符:
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
。这个特殊的规则只适用于以下六种情况:
<li><code>I</code> 可以放在 <code>V</code> (5) 和 <code>X</code> (10) 的左边,来表示 4 和 9。</li>
<li><code>X</code> 可以放在 <code>L</code> (50) 和 <code>C</code> (100) 的左边,来表示 40 和 90。 </li>
<li><code>C</code> 可以放在 <code>D</code> (500) 和 <code>M</code> (1000) 的左边,来表示 400 和 900。</li>
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例 1:
输入: "III" 输出: 3
示例 2:
输入: "IV" 输出: 4
示例 3:
输入: "IX" 输出: 9
示例 4:
输入: "LVIII" 输出: 58 解释: L = 50, V= 5, III = 3.
示例 5:
输入: "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 。
- 关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
我的理解 : 1. 映射的关系,如何将罗马字符映射到整形数字上。有两种映射方式:hashMap 和Switch 2.罗马字符的计算方式:把一个小值放在大值的左边,就是做减法,否则为加法。
最长公共前缀
- 自己瞎写的 感觉还可以
class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs.length == 0){
return "";
}
int minLen = minStrLen(strs);
for (int i = 0; i < minLen; i++) {
if (!sameStr(strs, i)) {
return strs[0].substring(0, i );
}
}
return strs[0].substring(0, minLen);
}
private int minStrLen(String[] strs) {
int minLen = strs[0].length();
for (int i = 1; i < strs.length; i++) {
if (minLen > strs[i].length()) {
minLen = strs[i].length();
}
}
return minLen;
}
private boolean sameStr(String[] strs, int num) {
char prefixChar = strs[0].charAt(num);
for (int i = 1; i < strs.length; i++) {
if (prefixChar != strs[i].charAt(num)) {
return false;
}
}
return true;
}
有效的括号(回看)
给定一个只包括
'('
,')'
,'{'
,'}'
, code>'[',']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
<li>左括号必须以正确的顺序闭合。</li>
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "()[]{}" 输出:true
示例 3:
输入:s = "(]" 输出:false
示例 4:
输入:s = "([)]" 输出:false
示例 5:
输入:s = "{[]}" 输出:true
提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
这个题目需要使用堆栈,我的堆栈之类的还没有系统的学,后面再系统的做这个题。
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 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
均按 非递减顺序 排列
// 本题目中仅仅加入了一个哑节点,所用的所有节点还是原链表中的。
// 当prev的next改变时,原链表节点中的指针就已经改变了。
// 因此可以合并到一个链表中。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
prev.next = l1 == null ? l2:l1;
return prehead.next;
删除有序数组中的重复项
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
// 自己写的 有点啰嗦
// 考虑了在前后数值相同时的情况,比较啰嗦
public int removeDuplicates(int[] nums) {
int i = 0;
int len = nums.length;
for (;i < len - 1; ) {
if (nums[i] == nums[i + 1]) {
for (int j = i + 1; j < nums.length - 1; j++) {
nums[j] = nums[j + 1];
}
len--;
} else i++;
}
return len;
}
// 答案的精简版
// 考虑了在前后数值不同时的情况,比较简单
public int removeDuplicates(int[] nums) {
int i = 0;
for(int j = 0; j < nums.length; j++){
if(nums[j] != nums[i]){
nums[++i] = nums[j];
}
}
return i + 1;
}
移除元素
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2] 解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3] 解释:函数应该返回新的长度5
, 并且 nums 中的前五个元素为0
,1
,3
,0
, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
// 我认为本题的本质和上一道题一样。
// 原地是重点,使用两个下标数字来模拟两个数组。
public int removeElement(int[] nums, int val) {
int i = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] != val) {
System.out.println("i"+nums[i]);
System.out.println("j"+nums[j]);
nums[i]=nums[j];
i++;
}
}
return i;
}
实现strStr()
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll" 输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba" 输出: -1
说明:
当 needle
是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle
是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
// 这个很简单 就不多说了
public int strStr(String haystack, String needle) {
for (int i = 0; i <= (haystack.length() - needle.length()); i++) {
if (needle.equals(haystack.substring(i, i + needle.length())))
return i;
}
return -1;
}
搜索插入位置(二分查找)
二分查找没啥好说的 就一个算法 然后勤加练习
(摘抄自力扣大佬)
- 分析题意,挖掘题目中隐含的 单调性;
- while (left < right) 退出循环的时候有 left == right 成立,因此无需考虑返回 left 还是 right;
- 始终思考下一轮搜索区间是什么,如果是 [mid, right] 就对应 left = mid ,如果是 [left, mid - 1] 就对应 right = mid - 1,是保留 mid 还是 +1+1、-1−1 就在这样的思考中完成;
- 从一个元素什么时候不是解开始考虑下一轮搜索区间是什么 ,把区间分为 22 个部分(一个部分肯定不存在目标元素,另一个部分有可能存在目标元素),问题会变得简单很多,这是一条 非常有用 的经验;
- 每一轮区间被划分成 22 部分,理解 区间划分 决定中间数取法( 无需记忆,需要练习 + 理解 ),在调试的过程中理解 区间和中间数划分的配对关系:
划分 [left, mid] 与 [mid + 1, right] ,mid 被分到左边,对应 int mid = left + (right - left) / 2;;
划分 [left, mid - 1] 与 [mid, right] ,mid 被分到右边,对应 int mid = left + (right - left + 1) / 2;。
至于为什么划分是这种对应关系,原因在于区间只有 22 个数的时候,如果中间数的取法不对,一旦进入的分支不能使得区间缩小,会出现 死循环。暂时不理解问题不大,需要在练习中进行调试;
退出循环的时候有 left == right 成立,此时如果能确定问题一定有解,返回 left 即可,如果不能确定,需要单独判断一次。
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
外观数组
给定一个正整数 n
,输出外观数列的第 n
项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = "1"
countAndSay(n)
是对countAndSay(n-1)
的描述,然后转换成另一个数字字符串。
前五项如下:
1. 1 2. 11 3. 21 4. 1211 5. 111221 第一项是数字 1 描述前一项,这个数是1
即 “ 一 个 1 ”,记作"11"
描述前一项,这个数是11
即 “ 二 个 1 ” ,记作"21"
描述前一项,这个数是21
即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是1211
即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
例如,数字字符串 "3322251"
的描述如下图:

示例 1:
输入:n = 1 输出:"1" 解释:这是一个基本样例。
示例 2:
输入:n = 4 输出:"1211" 解释: countAndSay(1) = "1" countAndSay(2) = 读 "1" = 一 个 1 = "11" countAndSay(3) = 读 "11" = 二 个 1 = "21" countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"
提示:
1 <= n <= 30
public String countAndSay(int n) {
StringBuffer res = new StringBuffer();
res.append(1);
for (int i = 1; i < n; i++) {
StringBuffer sbf = new StringBuffer();
int start = 0;
for (int j = 1; j < res.length(); j++) {
if (res.charAt(j) != res.charAt(start)) {
sbf.append(j-start).append(res.charAt(start));
start = j;
}
}
sbf.append(res.length() - start).append(res.charAt(start));
res = sbf;
}
return res.toString();
}
要点:嵌套循环,外层循环是每次都会被直接更新而不是补充,你曾循环包括两个下标,一个是扫描的下标,一个是重复位的开始下标。