目录
java集合、数组、List、哈希表、Map、Collections、java泛型、java泛型接口_哔哩哔哩_bilibili
0.2、个人总结,刷题时常用到的一些与字符串和数组相关的方法:
0.2.2、Arrays类相关方法:(以Arrays类名调用):
0.3.1、String类相关方法(以实例化后的引用对象名调用):
1:处理字符串问题的类方法(静态方法public static )valueOf(),
0.哈希表理论基础及其常用方法总结:
0.1、collection 源码
手撕Set,Map,LinkedList,ArrayList,List,HashSet,HashMap,Array源码的B站教学视频
java集合、数组、List、哈希表、Map、Collections、java泛型、java泛型接口_哔哩哔哩_bilibili
0.2、个人总结,刷题时常用到的一些与字符串和数组相关的方法:
0.2.0、Class Arrays
- java.lang.Object
-
- java.util.Arrays
-
public class Arrays extends Object
该类包含用于操作数组的各种方法(如排序和搜索)。 该类还包含一个静态工厂,可以将数组视为列表。如果指定的数组引用为空,则该类中的方法都抛出一个
NullPointerException
,除非另有说明。
0.2.1、Arrays类相关方法:(以数组名调用)
-
数组名可以调用的:常用的,不外乎如上的,长度,克隆,判断内容相等否,还要哈希码以及返回字符串描述。
-
其中关于equals(object)方法。在Java中,双等号(==)判断的是他的身份,equals判断的是它的内容,身份相同了,内容自然相同!
-
这里不多赘述,后期会补equals(obj)和等号的区别。
public class ArraysTest { public static void main(String[] args) { int[] nums = new int[]{1,0,2,4}; int[] nums1 = new int[]{1,0,2,4,666}; int[] nums2 = new int[]{1,0,2,4}; int[] nums3 = {1,0,2,4}; int[] nums4 = {1,0,2,4}; int[] nums5 = nums3; int length = nums.length; System.out.println(length); System.out.println(nums.equals(nums1)); System.out.println(nums.equals(nums2)); System.out.println(nums3.equals(nums4)); System.out.println(nums3.equals(nums5)); System.out.println(nums2.equals(nums3)); } }
结果是
-
0.2.2、Arrays类相关方法:(以Arrays类名调用):
-
1:asList
-
这里需要注意asList方法两点,一个是他的返回值是Obj对象类型的(包装类)
-
还有一点就是Java版本搞起来之后,列表的声明虽然可以add不同类型的对象进去,但是严进宽出,后续会补充关于集合类的专题。此处重点讨论方法
-
2:binarySearch
-
二分法查找 :方法看起来多,但是就两种,
-
一种参数列表是 要查找的数组 nums 和要查找的值 key
-
还有一种是在上一种的范围是加上了起始位置和终止位置
-
-
但是这里是左闭右开的!!!!一定要注意!!!
-
其次还有一点,这个方法查找不到时返回的负数是随机的,跟我们二分查找练习自己手敲代码和平时刷题要求的 - 1不同,随即返回,刷题用的时候注意数组下标越界,漏右边的数,以及返回值异常!~
-
-
3:copyOf
- 扩容专用,扩完容后吃嘛嘛香
-
4:sort
- 数组排序,在处理字符数组的字母异数词问题中妙用无穷
-
- 此外:在处理数组问题是,还要注意到一个比较有用的小tips
- 题目不要求返回索引而是要求返回数组的元素值的时候,通过sort进行排序之后问题往往迎刃而解,操作起来大有可为
- (但是注意部分题目如二分查找变种,你这样搞面试官会让你回去等通知~
0.3、String类
0.3.0、String类 :Class String
- java.lang.Object
-
- java.lang.String
-
All Implemented Interfaces:
Serializable , CharSequence , Comparable < String >
public final class String extends Object implements Serializable, Comparable<String>, CharSequence
String
类代表字符串。 Java程序中的所有字符串文字(例如"abc"
)都被实现为此类的实例。字符串不变; 它们的值在创建后不能被更改。 字符串缓冲区支持可变字符串。 因为String对象是不可变的,它们可以被共享。
-
此处就不得不提到字符串的初始化声明方式了:(后续发专帖补充)
0.3.1、String类相关方法(以实例化后的引用对象名调用):
即:通过数组名.方法进行调用。
方法令人眼花缭乱,可以自己测试方法,但是 String的length不同于Arrays的
Arrays的length是 public final int!因为数组在声明的时候长度就要确定,且是固定不变。加上final修饰符就无法修改
但是String获取长度的是方法。有括号
关于能以stringName 即字符串名(对象的引用变量名) 调用这么多方法,我个人的一点浅薄的见解是
String 后面的 不只是 stringName,还是一个引用变量,每个对象(字符串)都是截然不同的!
因此可以根据实例化出来的不同的对象,进行对象名((字符串名)||(引用变量名))进行调用这么多五彩缤纷眼花缭乱的方法。
正如我们每个人,生而不同,有各自的特征,但最终走的无非就是那几条相同的路,殊途同归。
只但愿你我路上不孤单,以梦为马,不负韶华。
0.3.2、String类的方法
1:处理字符串问题的类方法(静态方法public static )valueOf(),
妙用无穷~
2:处理字符串问题的实例方法toCharArray()
妙用无穷~
char[] ch = {'n','i','h','a','o'};
String s = String.valueOf(ch);
System.out.println(s);
String str = "HelloWorld";
char[] hello = str.toCharArray();
for (char ch1:hello )
{
System.out.print(ch1+" ");
}
nihao
H e l l o W o r l d
结果如上!
valueOf(参数)方法,查源码我们不难发现是静态方法,也就是说,我们可以通过String调用,也可以通过实例化后的 对象名调用 但是官方(包括IDEA)显然希望我们能够通过String类名直接调用。
依我浅薄之见,那些没有static的,不是类方法的,需要通过实例化对象才能调用的,就是因为不同的对象有不同的特征~(属性和行为)~如 stringName.length();方法 ,不同的实例化后的字符串对象有不同的长度 。
那些static修饰的可以以类名直接调用的类方法(静态方法),其实是比较抽象且统一的。
比如阅读 valueOf方法的底层源码,不难发现,这个读取字符数组,最终生成字符串的方法,
他的返回值还是调用了字符串的初始化~~~~
0.4、总结
其实上述关于Arrays和String的类,虽然有三千多字,但是只是Arrays和String类的九牛之一毛,但是刷题中比较常用的这两个类中的方法以及源码分析。越学习发觉自己是一个菜狗。然而不断砥砺前行,坚持下去,你会发现,你变成了一只大菜狗。
开个玩笑,上述总结仅供自己复习参考。由于编制水平有限,文章中难免有断章取义和理解不到位的鄙薄浅陋之见解, 还望读者能不吝赐教,大家一同分享,共同进步。
另外想在此给自己布置个任务,看看源码,那些底层源码的static方法,类方法,是不是具有高度抽象统一的特点,能通过类名.方法调用直接达到预期功能?
然后那些实例化方法,是否必须通过实例化确定具体属性,行为特征的对象才能调用的。是结合特征达到预期功能的?以上仅为本人猜想。
分割线————————————————————————————————————分割线
1.1、
1.2.1、有效的字母异位词 242
题目链接:
力扣
数组其实就是一个简单哈希表,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。定一个数组叫做record,大小为26 就可以了,初始化为0,因为字符a到字符z的ASCII也是26个连续的数值。
因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下表0,相应的字符z映射为下表25。
再遍历 字符串s的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。
那么最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。
最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。
时间复杂度为O(n),空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为O(1)。
录哥的做法空间消耗小,如果是用下面的处理字母异位词的子串先把字符串转成字符数组 调用toCharArray是一次然后对数组排序Arrays.sort是一次,反而复杂了。
class Solution
{
public boolean isAnagram(String s, String t)
{
int[] hasharr=new int[26];
for(char m : s.toCharArray())
{
hasharr[m-'a']+=1;
}
for(char n : t.toCharArray())
{
hasharr[n-'a']-=1;
}
for(int i:hasharr)
{
if(i!=0)
return false;
}
return true;
}
}
1.2.2、赎金信 383
class Solution
{
public boolean canConstruct(String ransomNote, String magazine)
{
//首先,赎金信和杂志,信的每个字符肯定是由 杂志的得到的 如果信的长度长于杂志 白给
//也就是说杂志的长度必定是大于等于赎金信的长度
//信的长度等于杂志 那就要求 信中的每个字符,杂志中都必须有,把杂志存入哈希集合set,这时候
//然后把杂志转成字符串挨个遍历,这种如果是 aab和bba直接破防了
//所以应该都转字符串数组,挨个遍历,开长度为26的数组,挨个记录,每个字母的个数 如果杂志中26个字母,出现在赎金信里的需要用到的都大于等于赎金信中要求的,那么可以完成任务,返回true,如果有一个小于,直接白给
//所以还是最简单的,存入数组,数组又不大,全是英文哪怕大小写都有都在也才五十二个
//常数级开辟数组空间,所需空间为O(1)。
//调用两个 toCharArray方法 遍历两次 ,时间复杂度为 (2n+方法),所以还是O(n)
char[] s1 = ransomNote.toCharArray();
char[] s2 = magazine.toCharArray();
int [] ch1 = new int[27];
int [] ch2 = new int[27];
for(char i : s1)
{
ch1[i-'a']++;
}
for(char i : s2)
{
ch2[i-'a']++;
}
for(int i=0;i<ch1.length;i++)
{
if(ch1[i] > ch2[i])
{
return false;
}
}
return true;
}
}
//首先,赎金信和杂志,信的每个字符肯定是由 杂志的得到的 如果信的长度长于杂志 白给
//也就是说杂志的长度必定是大于等于赎金信的长度
//信的长度等于杂志 那就要求 信中的每个字符,杂志中都必须有,把杂志存入哈希集合set,这时候
//然后把杂志转成字符串挨个遍历,这种如果是 aab和bba直接破防了,两个元素记录出现过没有但不记录出现个数,其实用HashMap键值对映射这二十六个也可以,只不过太奢侈了
//所以应该都转字符串数组,挨个遍历,开长度为26的数组,挨个记录,每个字母的个数 如果杂志中26个字母,出现在赎金信里的需要用到的都大于等于赎金信中要求的,那么可以完成任务,返回true,如果有一个小于,直接白给
//所以还是最简单的,存入数组,数组又不大,全是英文哪怕大小写都有都在也才五十二个
//常数级开辟数组空间,所需空间为O(1)。
//调用两个 toCharArray方法 遍历两次 ,时间复杂度为 (2n+方法),所以还是O(n)
1.2.3、字母异位词分组 49
class Solution
{
public List<List<String>> groupAnagrams(String[] strs)
{
Map <String,List<String>> map = new HashMap<>();
for(String s : strs)
{
char[] ch = s.toCharArray();
Arrays.sort(ch);
String newS = new String(ch);
List<String> list= map.getOrDefault(newS,new ArrayList<>());
//如果已经有的话,映射出来,以键查值,然后取出来列表list,再加当前新字符串进去
//如果还没有的话,返回值是对象,把对象返回给 list,正好把地址给他的引用变量 ,初始化声明
//取出后进行 添加当前字符串进列表
list.add(s);
map.put(newS,list);//把键值对的映射整个放入地图中
}
return new ArrayList<List<String>>(map.values());//返回的设置也很巧妙
//把已经弄好的整个集合整个的放入新的列表中。
}
}
1.2.4、找到字符串中所有的字母异位词 438
暴力解法安慰奖
class Solution
{//本题的若干思路,两个字符串均长度为1 不用考虑 空串 其中 根据p处理 p是大佬,p的长度定死s中字符串的所需长度
//这种 异位词 的 子串的 排列组合 经典起手 字符串转置
public List<Integer> findAnagrams(String s, String p)
{
int lens = s.length();
int lenp = p.length();
char[] chs =s.toCharArray();
char[] chp = p.toCharArray();
Arrays.sort(chp);
String newStr = new String(chp);//子串排序之后的
List<Integer> list = new ArrayList<Integer> ();
for(int i=0;i<=lens-lenp;i++)
{
String temp = String.valueOf(chs,i,lenp);
if(isSonString(temp,newStr))
{
list.add(i);
}
}
return list;
}
public boolean isSonString(String s,String newStr)
{
char[] ch = s.toCharArray();
Arrays.sort(ch);
String nb = new String(ch);
return nb.equals(newStr);
}
}
//本题为什么做的慢?
//字符串 转 字符数组 的方法记不住?小辣鸡
//字符数组 截取一定长度 转换成字符串的方法记不住,小小辣鸡
//Java中 == 是用来比较身份的 ,记不住,小小小辣鸡 用equals方法。
//本题为什么做的慢?
//字符串 转 字符数组 的方法记不住?小辣鸡
//字符数组 截取一定长度 转换成字符串的方法记不住,小小辣鸡
//Java中 == 是用来比较身份的 ,记不住,小小小辣鸡 用equals方法。
所以特此总结
滑动窗口解法
巧妙地滑动,意义无非就是代替循环,减少cpu的操作,提高效率
所以 比较异位词的方式,不难发现题目给出了:
说真的整理这篇博客之前,本人水平有限,还不敏感,难以发现这个细节条件
在力扣中,如果官方给的条件你还没用上,那你的代码一定还有可以剪枝优化的地方!
所以老规矩开数组,把比较换成常量级操作的常数复杂度
此外滑动窗口就是通过控制滑动的范围。
1.3.1、两个数组的交集 349
本意是一个哈希集合就可以,给一个数组去重,然后再另一个中contains方法判存在
最后集合想返回入一个int型数组中,Set类中的实例化方法 toArray(),返回值类型
但是实操不行,因为Java的Jdk中没有提供这个方法
所以要再浪费一些空间。
int[] resArr = new int[resSet.size()];
int index = 0;
//将结果集合转为数组
for (int i : resSet) {
resArr[index++] = i;
}
对集合进行遍历,存入数组,输出。
1.3.2、两个数组的交集(2) 350
补一个大佬的:
解题思路
获取较短数组A
将数组A遍历存放到Hash表里,key是元素,value是该元素的出现次数
遍历数组B,每遍历一个元素从Hash表里查找是否存在该元素
如果存在,且次数大于0,则该元素就是交集的元素之一,同时在Hash表里将该元素的个数减去1
复杂度分析
时间复杂度:O(n)
只需要分别遍历数组nums1、nums2 = 2 * O(n) = O(n)
空间复杂度:O(n)
需要额外的空间来存储每个元素的出现次数
代码
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
convene = {}
if len(nums1) > len(nums2):
tmp = nums1 # 如果num1元素大于num2,则交换两个数组的值
nums1 = nums2
nums2 = tmp
for num in nums1: # 使用较小的数组来生成hash表,以节省空间
if num in convene:
convene[num] += 1
continue
convene[num] = 1
result = []
# 遍历较长的数组,如果元素在hash表里存在,且个数大于0,则说明该元素是交集
for num in nums2:
if num in convene and convene[num] > 0:
result.append(num)
convene[num] -= 1
return result
1.4.0、快乐数(暂空)(后续补充)
1.5、两数之和(哈希表解法) 1
class Solution
{
public int[] twoSum(int[] nums, int target)
{
//我的初始原想法是,索引不会重复,内容会重复,所以就用键值对对应数组下标和数组内容
//因为键值对中的键值key无法被覆盖,但是key对应的value会被新的value覆盖!
//而且map中的 方法 获取的 keyset和 values都是直接获取全部的键或者值
//键 值 只能单独判断是否包含,如果要获取value 只能用get(key)
//本题我们使用哈希map,要求返回索引下标,最终还要取出来,那就要把下标存成value。
//也就是说,数组元素内容存成key,那么内容重复,如示例三,怎么做?
//重复不用怕,key = 3 内容重复,要么两个值加起来等于target 凑出来 6
//要么就是两个加起来不等target,这时候把他更新一下覆盖就行了,
//而且如果重复,官方说了只有一种答案,也就是说,3+3凑不出来6 假设target=8,那你后面也别想 找到5了,因为只有一个答案,即如果有两个3,3和5对应的索引下标就不止一组,哈哈
//所以不用担心key对应的value被覆盖
Map<Integer,Integer> map = new HashMap();
//真就这么巧妙么?奇技淫巧,如果你一开始不给map的键值对定类型,那么他用get方法get的就是Object类型的,对象类型的!
//此外最开始最好写成Map的父类,子类对象向上转型。
//这种面向对象的编程对后续大有裨益
int len = nums.length;
map.put(nums[0],0);
for(int i=1;i<len;i++)
{
if(map.containsKey(target-nums[i]))
{
return new int[] {map.get(target-nums[i]),i};
}
map.put(nums[i],i);//将其设置在最后放入,巧妙避免和自己重复~~
}
return new int[0];
}
}