1.两数之和
给定一个整数数组 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)
的算法吗?
我的
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
for(int i=0;i<nums.length;i++){
int temp = target - nums[i];
if(hashtable!=null && hashtable.containsKey(temp)){
return new int[]{hashtable.get(temp),i};
}
hashtable.put(nums[i],i);
}
return null;
}
别人的
class Solution {
public int[] twoSum(int[] nums, int target) {
int length = nums.length;
for(int i = 0; i < length - 1; i++){
int back_i = length - i - 1;
for(int j = i + 1; j < length; j++){
int back_j = length - j - 1;
if(nums[i] + nums[j] == target){
return new int[]{i,j};
}
if(nums[back_i] + nums[back_j] == target){
return new int[]{back_i, back_j};
}
}
}
return new int[0];
}
}
思路不一样:
思路不一样 每次不止比较1对数的和
2.字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
提示:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i]
仅包含小写字母
我的
class Solution {
public static List<List<String>> groupAnagrams(String[] strs) {
List<List<String>> resData = new ArrayList<>();
for(int i = 0 ; i<strs.length ; i++){
String sortedString = sortString(strs[i]);
if(resData.size() == 0){
List<String> innerList = new LinkedList<>();
innerList.add(sortedString);
innerList.add(strs[i]);
resData.add(innerList);
}else{
Boolean isOld = false;
for(int j = 0 ; j<resData.size(); j++){
String existingSortedString = resData.get(j).get(0);
if(sortedString.equals(existingSortedString)){
List<String> innerList = resData.get(j);
innerList.add(strs[i]);
resData.set(j,innerList);
isOld = true;
break;
}
}
if (!isOld){//isNew
List<String> innerList = new ArrayList<>();
innerList.add(sortedString);
innerList.add(strs[i]);
resData.add(innerList);
}
}
}
for(int j = 0 ; j<resData.size(); j++){
List<String> innerList = resData.get(j);
innerList.remove(0);
resData.set(j,innerList);
}
// 按内部 List 的 size 升序排序
Collections.sort(resData, new Comparator<List<?>>() {
@Override
public int compare(List<?> list1, List<?> list2) {
return Integer.compare(list1.size(), list2.size());
}
});
return resData;
}
public static String sortString(String input) {
// 将字符串转换为字符数组
char[] charArray = input.toCharArray();
// 对字符数组进行排序
Arrays.sort(charArray);
// 将排序后的字符数组转换为字符串
return new String(charArray);
}
}
别人的
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
return new AbstractList<List<String>>() {
private final List<List<String>> list = new ArrayList<>();
public void initList() {
if (list.isEmpty()) {
Map<String, List<String>> map = new HashMap();
for(String s:strs){
char[] cc=s.toCharArray();
Arrays.sort(cc);
String key =Arrays.toString(cc);
List<String>list=map.getOrDefault(key, new ArrayList());
list.add(s);
map.put(key,list);
}
list.addAll(map.values());
}
}
@Override
public List<String> get(int index) {
initList();
return list.get(index);
}
@Override
public int size() {
initList();
return list.size();
}
};
}
}
思路一样,实现方式的优化:
确定每个字符串通过同一排序规则得到唯一排序字符串,由此判断是否为由相同字母组成的字符串,加入到同一list 去掉每个内层list的第一位 按内层list的size从小到大排序
0.1.代码总结
public static void main(String[] args) {
// 1
int[] nums = {2,7,11,15};
int target = 9;
int[] twoSum = twoSum2(nums, target);
System.out.println(Arrays.toString(twoSum));
// 2
String[] strs = {"eat","tea","tan","ate","nat","bat"};
List<List<String>> groupAnagrams = groupAnagrams2(strs);
System.out.println(groupAnagrams);
}
public static int[] twoSum2(int[] nums, int target) {
//思路不一样 每次不止比较1对数的和
int length = nums.length;
for(int i = 0; i < length - 1; i++){
int back_i = length - i - 1;
for(int j = i + 1; j < length; j++){
int back_j = length - j - 1;
if(nums[i] + nums[j] == target){
return new int[]{i,j};
}
if(nums[back_i] + nums[back_j] == target){
return new int[]{back_i, back_j};
}
}
}
return new int[0];
}
//2-answer
public static List<List<String>> groupAnagrams2(String[] strs) {
return new AbstractList<List<String>>() {
private final List<List<String>> list = new ArrayList<>();
public void initList() {
if (list.isEmpty()) {
Map<String, List<String>> map = new HashMap();
for(String s:strs){
//1.sort 思路:确定每个字符串通过同一排序规则得到唯一排序字符串
char[] cc=s.toCharArray();
Arrays.sort(cc);
String key = Arrays.toString(cc);
//2.addList 由唯一排序字符串判断是否为由相同字母组成的字符串,加入到同一list
List<String> list = map.getOrDefault(key, new ArrayList());
list.add(s);
map.put(key, list);
}
list.addAll(map.values());
}
}
@Override
public List<String> get(int index) {
initList();
return list.get(index);
}
@Override
public int size() {
initList();
return list.size();
}
};
}
//1-my
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<nums.length;i++){
int temp = target - nums[i];//思路
if(map!=null && map.containsKey(temp)){
return new int[]{map.get(temp),i};
}
map.put(nums[i],i);
}
return null;
}
//2-my
public static List<List<String>> groupAnagrams(String[] strs) {
//思路:确定每个字符串通过同一排序规则得到唯一排序字符串,由此判断是否为由相同字母组成的字符串加入到同一list
//去掉每个内层list的第一位
//按内层list的size从小到大排序
List<List<String>> resData = new ArrayList<>();
for(int i = 0 ; i<strs.length ; i++){
String sortedString = sortString(strs[i]);
if(resData.size() == 0){
List<String> innerList = new LinkedList<>();
innerList.add(sortedString);
innerList.add(strs[i]);
resData.add(innerList);
}else{
Boolean isOld = false;
for(int j = 0 ; j<resData.size(); j++){
String existingSortedString = resData.get(j).get(0);
if(sortedString.equals(existingSortedString)){
List<String> innerList = resData.get(j);
innerList.add(strs[i]);
resData.set(j,innerList);
isOld = true;
break;
}
}
if (!isOld){//isNew
List<String> innerList = new ArrayList<>();
innerList.add(sortedString);
innerList.add(strs[i]);
resData.add(innerList);
}
}
}
for(int j = 0 ; j<resData.size(); j++){
List<String> innerList = resData.get(j);
innerList.remove(0);
resData.set(j,innerList);
}
// 按内部 List 的 size 升序排序
Collections.sort(resData, new Comparator<List<?>>() {
@Override
public int compare(List<?> list1, List<?> list2) {
return Integer.compare(list1.size(), list2.size());
}
});
return resData;
}
public static String sortString(String input) {
// 将字符串转换为字符数组
char[] charArray = input.toCharArray();
// 对字符数组进行排序
Arrays.sort(charArray);
// 将排序后的字符数组转换为字符串
return new String(charArray);
}
0.2.相关
AI:使用通俗易懂的例子的来讲解AbstractList
看起来用户可能在学习Java集合框架,特别是想深入理解如何自定义List实现。
用户可能已经了解了AbstractList的基本用法,但希望有更简单、生活化的例子来加深理解。他们可能对之前的技术示例感到有些复杂,或者需要更直观的解释来巩固概念。需要将AbstractList的概念与实际生活中的场景结合,帮助用户形成直观印象。
需要确定用户的基础水平。之前的回答涉及到了代码示例,所以用户可能有一定的编程经验,但可能需要更基础的比喻或例子。例如,用日常生活中的事物来类比AbstractList的作用,比如菜单、书架等。
用户的需求不仅仅是知道如何用代码实现,而是理解AbstractList存在的意义和适用场景。可能需要强调为什么使用AbstractList而不是直接实现List接口,比如减少必须实现的方法数量,提供默认实现等。
另外,用户可能想知道在什么情况下应该继承AbstractList,比如需要自定义列表行为,或者包装现有数据结构。需要提供简单明了的例子,比如只读列表、固定大小列表,或者组合多个列表的情况。
需要避免使用过于技术化的术语,转而使用比喻和简单代码示例。例如,用书架的比喻来解释不可变列表,用购物清单来演示可变列表的实现。同时,对比直接实现List接口和继承AbstractList的差异,突出AbstractList的便利性。
还要注意用户可能的误区,比如认为继承AbstractList必须实现所有方法,而实际上只需要实现get和size。或者对于修改操作,默认会抛出异常,需要覆盖set、add等方法才能支持修改。
最后,确保例子涵盖不同的使用场景,如不可变列表、包装现有数组、动态扩展的列表等,帮助用户全面理解AbstractList的灵活性和实用性。
好的!我用一个 「菜单本」 的比喻来帮你理解 AbstractList
,并配上一个超简单的代码例子。
📖 比喻场景
假设你要做一个 「饭店菜单系统」,但有个特殊要求:
普通服务员只能 查看 菜单(不能修改)
店长有 修改菜单 的权限
如果用 AbstractList
来实现,就像这样:
🎯 代码实现
import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;
// 自定义菜单类(继承AbstractList)
class Menu extends AbstractList<String> {
private String[] dishes; // 存储菜品
// 构造方法
public Menu(String[] dishes) {
this.dishes = Arrays.copyOf(dishes, dishes.length);
}
// 必须实现的方法1:获取菜品(普通服务员用)
@Override
public String get(int index) {
return dishes[index];
}
// 必须实现的方法2:获取菜单长度
@Override
public int size() {
return dishes.length;
}
// 可选实现:修改菜品(只有店长能调用)
@Override
public String set(int index, String newDish) {
String oldDish = dishes[index];
dishes[index] = newDish;
return oldDish; // 返回被替换的旧菜名
}
}
public class Test {
public static void main(String[] args) {
// 初始菜单
String[] initialDishes = {"鱼香肉丝", "宫保鸡丁", "麻婆豆腐"};
List<String> menu = new Menu(initialDishes);
// 普通服务员查看菜单
System.out.println("当前菜单:" + menu);
// 输出:[鱼香肉丝, 宫保鸡丁, 麻婆豆腐]
// 尝试修改菜单(会报错,因为没权限)
// menu.add("水煮鱼"); // 抛出 UnsupportedOperationException
// 店长修改第二道菜
if (menu instanceof Menu) {
Menu bossMenu = (Menu) menu;
String old = bossMenu.set(1, "辣子鸡");
System.out.println("替换掉:" + old); // 输出:替换掉:宫保鸡丁
}
System.out.println("新菜单:" + menu);
// 输出:[鱼香肉丝, 辣子鸡, 麻婆豆腐]
}
}
🔑 关键点解析
为什么用 AbstractList?
它帮你实现了
List
接口中大部分复杂方法(如iterator()
)你只需专注实现
get()
和size()
这两个核心方法
修改控制:
默认所有修改方法(如
add()
、remove()
)会抛出异常如果想让某些角色能修改,只需覆盖对应方法(比如例子中的
set()
)
实际应用场景:
创建 只读列表(不覆盖修改方法)
包装现有数组(如例子中的
String[] dishes
)实现特殊规则的列表(如固定长度列表、组合多个列表)
💡 对比直接实现 List 接口
如果不用 AbstractList
,你需要实现 List
接口的 24个方法!而用 AbstractList
只需要实现 2个方法,其他方法已有默认实现。
就像做菜单时,有人帮你写好了打印菜单、搜索菜品等功能,你只需告诉它「菜品存在哪」和「有多少道菜」。