LeetCode面试最常见100题详解(1)两数之和

  1. 两数之和
    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
    示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路:
(1)已知值返回索引,联想kv键值对,map存放值与索引
(2)怎么找到值与差值的联系,遍历数组的过程中,将差值与值的索引存放map中(而不是存放值与值的索引,这样map的作用就是存放了一个索引以及另一个索引的值,可通过另一个值找出其索引,而目标是要求两个索引),后面遍历的时候计算差值肯定会出现map中已有的,因此可以做判断。

public class Java_Test {
    public static int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {  // 遍历数组
            if (map.containsKey(nums[i])) {  // 判断map的key中是否包含当前索引下值所对应差值
                return new int[]{map.get(nums[i]), i};  // 包含则返回map中差值对应的索引(原值的索引)与当前索引(当前值=差值时的索引,即差值的索引)
            }
            map.put(target - nums[i], i);  // 放入差值与值的索引,索引作为value是因为map.get方法获取的是value,而value索引值是我们需要得到的
        }
        return null;
    }

    public static void main(String[] args) {
        int[] array = new int[]{7, 8, 9, 10, 11};
        System.out.println(Arrays.toString(twoSum(array, 19)));  // 会打印出[2, 3]
    }
}

知识点:
(1)Static关键字,一句话来概括:方便在没有创建对象的情况下来进行调用。根据类名就可以访问。
含义:静态,在程序载入时就已经分配了内存空间,无需创建对象,一直都存在(程序载入后),处于静态状态。如和数学相关的静态资源放在java.lang.Math中。类的初始化早于类的new。所以静态方法不能引用非静态资源。静态资源的加载顺序是严格按照静态资源的定义顺序来加载的。静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问。静态代码块是严格按照父类静态代码块->子类静态代码块的顺序加载的,且只加载一次。
Ⅰ. 修饰类,只能修饰静态内部类(类中类,也叫匿名内部类)
Ⅱ. 修饰方法,通过类名调用,本题中main方法就是直接调用twoSum方法,同一个类省略了类名
Ⅲ. 修饰变量,通过类名调用
Ⅳ. 修饰代码块,叫静态代码块,类第一次被加载时执行。
Ⅴ. import static java.lang.Math.*; 类中可以直接使用sin等函数了,这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名.资源名,可以直接使用资源名。

(2)包装类。Java中包含的8种基本数据类型不支持面向对象的编程机制。通过包装类可以将基本数据类型的值包装为引用数据类型的对象。特殊:char对应的包装类为:Character;int对应的包装类为:Integer。其它的首字母大写。特性:自动装箱是指将基本数据类型赋给对应的包装类变量Integer b = a;反之,自动拆箱就是将包装类对象类型直接赋给一个对应的基本数据类型变量int c = b。
Ⅰ. 包装类String类的valueOf()方法可以将8种基本数据类型转换为对应的字符串类型。自动装箱
基本类型---->包装类型(字符串) String.valueOf(int) Integer.valueOf(int)
Ⅱ. 包装类的静态方法parseXxx()可将变量内容匹配的字符串转换为对应基本数据类型。自动拆箱
包装类型(字符串)---->基本类型 Integer.parseInt(Str)
Ⅲ. 包装类都重写了Object类中的toString()方法,以字符串的形式返回被包装的基本数据类型的值。
包装类型---->字符串 Integer.toString() Arrays.toString()

(3)类型判断。boolean b = obj instanceof Class

(4)泛型。
集合可以存储任意类型的对象元素,但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就统一变成了Object类型。换句话说,在程序中无法确定一个集合中的元素到底是什么类型,那么在取出元素时,如果进行强制类型转换就很容易出错。
引入泛型后限定了集合元素的数据类型,使得限定的数据类型程序在编译期间就会出现报错提示,避免程序在运行期间发生错误。泛型只能接受类,所有的基本数据类型必须使用包装类。
泛型必须是包装类类型,也就是只能代表引用类型(是对象),而不能是基本数据类型(不支持面向对象的编程机制),因为在程序中,有些结果有些数据可能会返回空值(空值与0不同),类似于 int 这些基本类型没有 null 值可言。基本数据类型的定义是存放在栈中的,但是我们创建对象而引出来的实际数值则是放在堆里的,堆的速度远远不如栈。而且基本数据类型变量的创建和销毁都非常快,而类定义的变量还需要 JVM 去销毁。所以,从性能考虑还是需要基本数据类型。
Ⅰ. 泛型定义类 class Point {}
Ⅱ. 泛型定义方法public T testMethod(T t){}
Ⅲ. 泛型定义接口 interface IMessage {}
Ⅳ. 通配符,“?”代替任意类型,由于是不确定类型,无法修改。这种使用List<?>的方式就是父类引用指向子类对象 public static void printList(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
泛型<? extends E>指的是E及其子类
泛型List<? super E>指的是E及其子类
Ⅳ. 泛型擦除 myClass.getClass() 变成Object

(5)Map
Map常见方法有map.containsKey()map.get(key) map.put(k, v) map.keySet() map.values() (collection类型) map.size() map.toString()

(6)打印数组
Ⅰ. for循环 ; for each(增强for) for (int i : Array) {System.out.print(i);}
Ⅱ. Arrays.toString() java.util包的数组类的静态方法,多维数组用Arrays.deepToString()
Ⅲ. Arrays.asList() 不能用来打印多维数组
Ⅳ. 迭代器接口 Iterator it = Arrays.asList().iterator(); while(it.hasNext()) {sout(it.next())}
Ⅴ. Stream API Arrays.stream(Array).forEach(System.out::print);

(7)线程安全
一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性。线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
并行(parallel):指在同一时刻,有多条任务在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。
并发(concurrency):指在同一时刻只能有一个任务执行,但多个任务被快速的轮换执行。并发可以在单处理器和多处理器系统中都存在。宏观上(一个时间段内)看起来是多个任务(多处理器上看起来像并行)一起执行的。线程、进程都可并发。
在这里插入图片描述

进程(Process) 是系统进行资源分配和调度的基本单位,进程是线程的容器。进程=线程+内存+文件/网络句柄。
线程(thread) 是操作系统能够进行运算调度的最小单位。是进程中的实际运作单位。一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程=栈+程序计数器+线程本地储存。

线程安全问题是指,多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。
而且线程安全需要遵从三概念:
1、原子性
即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
2、可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
3、有序性
即程序执行的顺序按照代码的先后顺序执行。

(8)保证线程安全的方法
Ⅰ. 同步锁(synchronized关键字)
方法加锁、同步代码块
Ⅱ. Lock类
ReentrantLock、ReentrantReadWriteLock 可重入读写锁
优先使用顺序:
Lock类 > 同步代码块 > 同步方法
Ⅲ. 原子类
原子基本类型、原子数组类型、原子引用类型、原子更新器
Ⅳ. volatile关键字(只有在保证原子性的前提下才可以保证线程安全)
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其 他线程来说是立即可见的。
2)禁止进行指令重排序。
https://segmentfault.com/a/1190000041430421

(9)创建线程的方法
Ⅰ. 继承Thread类
Ⅱ. 实现Runnable接口
Ⅲ. 实现Callable接口
Ⅳ. 使用线程池
https://segmentfault.com/a/1190000041430421

(9)java集合
在这里插入图片描述
在这里插入图片描述
(10)Collection、Map与Iterator
Collection接口继承了迭代器Iterator接口
Map可以迭代,但是它不继承collection接口,更加不可能继承迭代器
Map迭代的方式:

"方式一:通过Map.keySet遍历key和value:"
        for (Integer key : map.keySet()) {
            String s = map.get(key);
            System.out.println(key + ": " + s);
        }
"方式二:通过Map.entrySet遍历key和value:"
        System.out.println("推荐,尤其是容量大时");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
"方式三:通过Map.entrySet使用iterator遍历key和value:"
        Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> entry = iterator.next();
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
"方式四:通过Map.values()遍历所有的value,但不能遍历key"
        for (String value : map.values()) {
            System.out.println(value);
        }

(11)访问权限修饰符
在这里插入图片描述
(12)数组
多个相同类型数据的组合,数组中的元素可以是基本数据类型,也可以是引用类型。数组本身属于引用类型。
动态初始化:int[] arr = new int[3]; arr[0] = 3;
静态初始化:int[] arr = new int[]{3, 9, 8};
数组与集合区别:
Ⅰ. 数组声明了它容纳的元素的类型,而集合不声明。
Ⅱ. 数组是静态的,大小固定,创建后无法改变容量。集合是可以动态扩展容量,集合有更多的成员方法,能满足更多的需求。
Ⅲ. 数组的存放的类型只能是一种,集合存放的类型可以不是一种。
Ⅳ. 数组是java内置的数据类型,是线性排列的,执行效率是最快的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值