面经题答案整理
Java基础 1.Java底层源码分析
String 和 StringBuilder的区别? StringBuilder和StringBuffer区别? 参考资料: https://www.jianshu.com/p/8c724dd28fa4 公共点:java中的三个类来表示和操作字符串 String是内容不可变的字符串, 被创建之后就不能改变 String类的底层使用了一个不可变的字符数组 StringBuilder和StringBuffer 是内容可以改变的字符串,底层使用的是char数组(没有final关键字修饰) char[] value
String 的拼接 c = “a” + “b”底层是用StringBuilder或StringBuffer的append方法来实现的 String 转 SB 转 String效率较低,如果存在较多的String拼接,不要使用 StringBuilder或StringBuffer
StringBuilder是线程不安全的,效率较高,JDK1.5之后新添加的,StringBuffer是线程安全的,效率较低
HashMap的相关问题
参考资料: https://tech.meituan.com/2016/06/24/java-hashmap.html https://leetcode.com/problems/design-hashmap/ https://juejin.cn/post/6844903504776003592
说一说HashMap的存储, 哈希冲突,扩容,并发访问怎么解决? HashMap的并发不安全体现在哪? HashMapz在任意时刻可以有多个线程同时写HashMap,会导致数据不一致。如果要满足线程安全的要求, 可以用Colletioncs的synchronizedMap方法使HashMap具备线程安全,或者使用ConcurrentHashMap HashMap在扩容时,对读写操作有什么特殊处理?
概述: HashMap继承AbstractMap 实现了Map的接口 HashMap根据键的hashCode值存储数据, 大多数情况下可以直接定位到它的值, HashMap最多只允许一条记录的键为null,多条记录的值为null。 底层的实现是 数组+链表+红黑树,核心元素包括 size loadFactor 负载因子 threshold: 阈值 用来触发扩容机制 threashold = capacity * loadfactor Node<K,V>[] table Node<K, V> 包括 hash key value Node<K, V> next
根据key获取哈希桶数组索引位置详解 Hash算法本质: 取key的hashCode、高位运算、取模运算 static final int hash(Object key) { //jdk1.8 & jdk1.7 int h; // h = key.hashCode() 为第一步 取hashCode值 // h ^ (h >>> 16) 为第二步 高位参与运算 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
HashMap的put()方法流程
HashMap的插入使用单链表的头插入方式, 在同一位置上新元素会被放在链表的头部
HashMap的扩容机制: 对读写操作有什么特殊处理 ? 使用2次幂扩容, 所以元素的位置要么在原位置, 要么在原位置再移动二次幂
扩容过程中hash函数得到的index可能会改变 只需要看一下新增的bit是0还是1就能直接确定位置, 如果是1, 扩容后索引变为 “原索引 + oldCap” hash冲突的确定是同样的HashCode(同样的index) 但是不一样的key如果是一样的key直接覆盖就可以了 HashMap通过拉链法解决hash冲突: 创建链表数组,如果遇到了哈希冲突,将冲突的值加到链表中即可
ConcurrentHashMap如何实现线程安全的?
JDK 1.7使用的分段锁
ConcurrentHashMap里面包含一个Segment数组, 实现ReentrantLock,是可重入锁, HashEntry用于存储键值对数据, 每个Segment守护HashEntry,修改数据必须要有Segment锁
JDK 1.8使用了 synchronized关键字和CAS算法
synchronized只锁定首节点,如果没有hash冲突,就不会产生并发
Java接口和抽象类的区别:
2.多线程 参考资料: https://www.jianshu.com/p/40d4c7aebd66 50道Java线程面试题 https://www.cnblogs.com/chanshuyi/p/5351723.html Java多线程总结 https://juejin.cn/post/6844903681515585543#heading-4 https://juejin.cn/post/6844903703430823944#heading-14
Java并发变成包含的4层含义 内存模型和一致性协议 锁相关的理论 无锁的数据结构 各个语言的并发模型 一般的并发基础都是基于单机的 但是分布式锁也需要考虑是否重入、阻塞中断、超时唤醒、失效取消等问题
谈一下Java多线程实现的几种方法? 继承Thread类,重写父类的run() 方法 实现Runnable接口 实现Callable接口
对比: 实现Runnable接口可以避免单继承局限 线程池只能放入实现Runnable或Callable类的线程 Runnable接口可以被多个Thread实现,代码与数据可以被共享 接口实现时,必须使用Thread.currentThread()来访问当前线程
实现Runnable和Callable区别 Runnable是执行工作的独立任务,但它不返回任何值, 如果希望任务在完成的时候能返回一个值, 可以实现Callable接口而不是Runnable接口 Callable接口的call()方法允许抛异常,而Runnable接口的run()不能上抛异常
说一下线程的生命周期,线程的不同状态
线程的五种状态: 创建,就绪,运行,阻塞,死亡 创建: 声明了线程对象,但是并没有调用start()方法 就绪: 声明了对象 + 调用了start() 方法。是此时线程调度程序还没有将该线程设置为当前线程,当线程运行结束或从等待和睡眠中恢复之后也是处于就绪状态 运行: 就绪的线程变为了当前的线程,就进入运行状态,执行run方法当中的代码 阻塞: 线程正在运行的过程中,被暂停 + 等待过程 sleep, suspend, wait方法都可以造成线程阻塞 死亡: run方法结束执行或者调用了stop方法之后,线程就死亡
启动线程的两种方法: 继承Thread, 重写run方法并用start 方法启动线程 实现Runnable接口,实现run方法, 用new Thread(Runnable target).start()方法来启动
手写多线程计数 synchronized关键字 同步对象是谁?
说一下对于线程安全的理解 如何启动,关闭线程
Thread的run() 和 start() 方法的区别是什么? start()方法: 作用是启动一个新线程,使得新线程处于(可运行的状态), 一旦得到了CPU的时间片,就开始执行相应的run() 方法(异步执行) 可以实现多线程,start不能够被重复调用,
run()方法: 是Thread类中的一个普通方法, 直接调用run方法并不会启动新线程,程序依然只有主线程这一个线程,其程序的执行路径仍然只有一条,还是要顺序执行并等待run方法体执行完毕后才能继续执行下面的代码。用run开始线程就和多线程屁关系没有
Java并发编程实战
第一章 简介 操作系统为各个独立的进程分配各种资源: 内存 + 文件句柄 + 安全证书等 不同的进程中间可以通过粗粒度的通信机制来交换数据: 包括套接字、信号处理器、共享内存、信号量以及文件等
Java中的锁 参考博客: https://tech.meituan.com/2018/11/15/java-lock.
Java有哪些锁机制, 分别有什么特点?
乐观锁和悲观锁 乐观锁(读操作多的场景使用): 不添加锁,值在更新数据的时候判断是否有别的线程更新了数据, 如果没有, 则将自己的修改写入 使用CAS算法来实现 悲观锁(写操作多的场景使用): 在获取数据的时候直接加锁。 synchronized关键字和Lock的实现类都是悲观锁
// ------------------------- 悲观锁的调用方式 ------------------------- // synchronized public synchronized void testMethod() { // 操作同步资源 } // ReentrantLock private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁 public void modifyPublicResources() { lock.lock(); // 操作同步资源 lock.unlock(); }
// ------------------------- 乐观锁的调用方式 ------------------------- private AtomicInteger atomicInteger = new AtomicInteger(); // 需要保证多个线程使用的是同一个AtomicInteger atomicInteger.incrementAndGet(); //执行自增1
原子类 通过使用CAS乐观锁 在可伸缩性和活跃性上有优势, 但是在竞争条件较强时的性能不如锁定 自旋锁与适应性自旋锁(强化版自旋锁) 自旋锁是为了避免CPU切换状态而让线程自旋来等待锁开销 适应性指的是 自旋的时间(次数)不固定, 由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。 成功率大->批准自旋时间长 成功率低->直接阻塞线程 取消自旋
无锁、偏向锁、轻量级锁、重量级锁 这四个词是用来描述锁的状态,针对synchronized关键字
锁加在Java对象头内部 两个关键数据 Mark Word 标记字段 存储对象的Hash Code, 分代年龄和锁标志信息 Class 类型指针: 对象指向它的类元数据
Monitor锁(内部锁) 一个同步机制, 通常被描述为一个对象
synchronized 通过Monitor来实现线程同步,而Monitor依赖于底层操作系统的Mutex Lock互斥锁 互斥锁被成为“重量级锁”, JDK 6中为了减少 获得锁和释放锁带来的性能消耗,引入了“偏向锁” 和 “轻量级锁”
无锁: 没有对资源绑定,所有的线程都能访问并修改同一个资源, 但同时只有一个线程能修改成功, 其他失败的线程会不断重试。CAS就是一种无锁实现。
偏向锁: 如果一段同步代码一直 被一个线程访问, 那么该线程会自动获得锁,降低获取锁的代价, Mark Word中存储偏向的线程ID, 在线程进入和退出同步块时检查Mark Word是否存储指向当前线程的偏向锁 偏向锁不会被主动释放, 只有当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。
轻量级锁: 当锁是偏向锁的时候, 被另外的线程访问,偏向锁就会升级成为轻量级锁, 其他线程会通过自旋方式尝试获取锁, 不会阻塞
重量级锁: 当有第三个等待线程时,轻量级锁升级为重量级锁, 锁标志变为”10”,此时等待锁的线程都会进入阻塞状态。
总结: 偏向锁对比Mark Word, 避免CAS操作。 轻量级锁通过CAS和自旋来解决加锁问题重量级锁将线程阻塞。
公平锁与非公平锁 公平锁: 按照顺序 非公平锁: 可以插队 可以减少唤醒线程的开销, 整体的效率可能会高 ReentrantLock中有内部类 Sync, Sync有 FairSync 和 NonfairSync两个子类
公平锁比非公平锁多了一个 check条件 !hasQueuePredecessors() 这个方法主要是判断当前线程是否位于同步队列中的第一个。如果是则返回true,否则返回false。
ABA问题: 解决办法是打时间戳 JDK1.5之后提供了 AtomicStampedReference来解决ABA问题, 调用compareAndSet() 方法先检查当前引用和当前标志与预期引用和预期标志是否相等 V = 待读写的内存位置 A = 进行比较的值 B = 待写入的新值, 只有当V的值等于A时, CAS才会通过原子方式用新值B来更新A, 否则不执行操作
可重入锁与非可重入锁 可重入锁: 同一个线程在外层方法获取锁的时候,再进入该线程的内层方法时会自动获取锁(前提: 同一个对象或class内部), 不会因为之前已经获取过但还没释放而阻塞。 ReentrantLock 和 synchronized都是可重入锁, 优点是可以避免死锁。
public class Widget { public synchronized void doSomething() { System.out.println("方法1执行..."); doOthers(); }
public synchronized void doOthers() {
System.out.println("方法2执行...");
}
} 上面的代码里面的两个方法都被synchronized修饰了,因为可重入所以doSomething() 可以调用doOthers() 的方法 内部维护了一个同步状态status来记录重入次数,
独享锁与共享锁 独享锁(排它锁): 该锁一次只能被一个线程持有, 如果线程T对数据A加上了排它锁之后, 其它线程不能再对A加任何类型的锁。线程T可以读+写。JDK中synchronized 和 JUC中的Lock的实现类都是互斥锁 共享锁: 该锁可以被多个线程持有, 如果线程T对数据A加上共享锁后, 其他线程只能对A再加共享锁, 不能加排它锁。获得共享锁的线程只能读数据, 不能改数据。 如果存在读锁,写锁不能备货区,
读锁: tryAcquireShared(int unused) 如果其他线程有写锁, 失败。如果当前线程有写锁或写锁未被获取,则当前线程增加读状态 + 获取读锁。
谈一谈对线程安全的理解?
要想编写线程安全的代码,核心在于对状态访问操作进行管理,特别是对于共享的(变量可以由多个线程同时访问)和可变的(变量的值在生命周期内可以发生变化)状态的访问
三种方式可以保证线程安全: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 synchronized
读写锁的使用 参考资料: 读写锁的实现原理 https://juejin.cn/post/1
3.Java类相关 1.内部类 2.抽象类和接口的区别
4.集合接口+底层实现+几个集合类的对比
List类相关内容: List集合源码剖析-Java3y
- HashMap和HashTable的区别? 共同点: HashMap和HashTable都可以存储key-value的数据,都实现了Map接口 不同点: 1.HashMap是可以用null作为key或者value的,而HashTable不可以 2.HashMap线程不安全, HashTable线程安全 3.HashMap containsValue 和 containsKey, HashTable contains 4.继承不同 HashMap继承AbstractMap<K,V> , Hashtable<K, V> extends Dictionary<K, V>
使用场景 ConcurrentHashMap一个Map拆分成了N个小的Segment,每个Map分别加锁可以提高效率 N倍(默认16倍)
2.HashSet 和 TreeSet的区别?
3.Java里的HashMap, ConcurrentHashMap, 红黑树如何实现的? HashMap实现参考美团技术博客 https://tech.meituan.com/java_hashmap.html
ConcurrentHashMap(简称Con)参考Java3y
ConcurrentHashMap底层:散列表 + 红黑树 和HashMap是相同的 顶部注释的几个点: 检索时是返回的最新设置结果,且是不加锁的操作可能会和update操作(put, remove)重叠,但保证线程安全 初始化时设置长度能提高效率 不允许key或者value是null Con 和 HashTable区别 HashTable是现在每个方法上都加入了Synchronized来完成同步,效率低,而Con是通过部分加锁和CAS(Compare and Swap)算法来实现同步 CAS CPU指令? 什么是CPU指令? 简单的流程: 1. 先比较相等 2.只有相等条件下才进行替换
volatile关键字: volatile仅用来保证变量对于所有线程的可见性(多线程的环境下,当这个变量修改时,所有的线程都会知道该变量被修改了),但不能够保证操作的原子性(修改变量在JVM中分为好几步,在这几步之内其实是不安全的)
简单讲讲Java中的原子类? Atomic指的是一个操作是不可中断的,即使在多个线程一起执行时,一个操作一旦开始,就不会被其他线程干扰 JUC包中的原子类 基本类型 数组类型 引用类型 对象的属性修改类型
public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值,并设置新的值 public final int getAndIncrement()//获取当前的值,并自增 public final int getAndDecrement() //获取当前的值,并自减 public final int getAndAdd(int delta) //获取当前的值,并加上预期的值 boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update) public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
AtomicInteger 主要利用CAS + volatile 和native方法来保证原子性
如果在基于锁的类中包含有细粒度的操作, 那么当在锁上存在激烈竞争时,调度开销与工作开销的比值会非常高。 解决这个问题 我们使用原子变量
原子变量类相当于一种泛化的volatile变量, 能够支持原子的和有条件的读-改-写操作, 它直接利用了硬件对并发的支持,具有更高的可伸缩性
5.JVM相关 类加载机制: 类从被加载到虚拟机内存中开始 到卸载出内存,整个生命周期包括: 加载,验证,准备,解析,初始化,使用,卸载7个阶段:
前5个阶段加载,验证,准备,解析,初始化 是类加载的极端 https://blog.csdn.net/javazejian/article/details/73413292 https://zhuanlan.zhihu.com/p/25228545 两道实战面试题https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html
加载: 创建一个class对象 验证: 确保Class文件的字节流中包含信息符合当前虚拟机要求,不危害虚拟机自身安全。主要包括四种验证: 文件格式验证,元数据验证,字节码验证,符号引用验证 准备: 为类变量分配内存冰洁设置该类变量的初始值为0 解析: 将常量池中的符号替换为直接引用过程 初始化:类加载最后阶段,若当前类具有超类,则对其进行初始化
Java Runtime Environment (JRE) 类库 JVM .java -> .class -> JVM -> 机器码010101
JVM属于JRE的一部分
JVM运行时数据区?
数据 + 指令 + 控制 操作系统三大要素
数据: (线程共享) 方法区: 存放被虚拟机加载的类信息、常量、静态变量、 运行时常量池 Heap: 在虚拟机启动时创建 用来存放对象实例 垃圾收集器管理的主要区域, 可以为物理上不连续的内存空间 指令: (线程独立 和线程生死同步) 程序计数器: 指向当前线程正在执行的字节码指令的地址 线程轮流切换、分支、跳转、异常处理都需要用到 虚拟机栈: 每个方法执行时候都会创建一个栈帧 存局部变量、操作栈、动态链接、等信息 当前线程运行方法时所需要的数据、指令、返回地址 局部变量表所需内存空间在编译期间完成分配 本地方法栈 : Native方法
线程: 最小的执行单位
说一下代码的栈溢出和堆溢出? https://blog.csdn.net/u011983531/article/details/63250
堆溢出: 在创建对象时没有可以分配的堆内存 OutOfMemoryError:java heap space
栈溢出:
线程请求的栈深度大于虚拟机所允许的最大深度
虚拟机在扩展栈深度时无法申请到足够的内存空间
内存泄露(Memory Leak): 程序无法释放已经申请的内存空间 内存溢出(Memory Overflow): 在是申请内存时,没有足够的内存空间供其使用, 申请一个integer, 给一个long
memory leak 最终会导致 memory overflow
Java的内存分区? Java的内存模型?
说一下Java的垃圾回收算法?
GC要完成的三件事情:
哪些内存需要回收?
引用计数器? 效率还可以但是无法解决循环引用的问题
根搜索算法: 通过一些 GC Roots作为起点,从这些节点开始向下搜索,,当一个对象和GC Roots之间没有任何引用相连,则证明这个对象不可用
可作为GC Roots的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
虚拟机栈中引用的对象
本地方法栈中Native方法
根搜索算法中涉及到 引用, Java中的4种引用类型:
强引用 不回收
软引用 内存溢出钱考虑
弱引用 下一次GC前
虚引用
什么时候回收? 如何回收?
四种垃圾回收算法:
标记-清除算法 : 标记后清除
缺点: 效率 + 空间 效率不高 空间碎片太多导致没有足够大的连续内存来容纳分配的大对象
复制算法: 内存分相等两块,将小块复制到大块,垃圾清除,交替保留 用来回收新生代对象 使用大的Eden 和 小的Survivor 因为90%+对象 死得快
缺点: 空间利用率 折半
标记-整理-清除: 将所有存活对象向一边移动,清除余下的内存
分代收集: 新生代-> 复制 因为死的多 老年代 -> 标记清除 或 标记整理清楚 死的少
说一说对volatile变量的理解? volatile可以看做是最轻量级的同步机制,使用volatile修饰的变量具有特殊的访问规则,具体来说是两种特性: 1. 保证此变量对所有线程的可见性 2. 禁止指令重排序优化youhua 6.Java IO多路复用模式
https://www.zhihu.com/question/28594409
数据库基础 SQL Online Tutorial W3School http://www.w3school.com.cn/sql/sql_join_inner.asp 1.Inner join, Outer join, Left join, Right join, Full join的区别,返回结果 Inner join: 两个表取交集,必须同时在两个表内都匹配 Outer join: 两个表取并集 Left join: 左边表全部保留 右边表如果和左边表匹配就保留匹配结果,没有和左边表匹配的就用NULL 与Left join相反 Full join 所有结果都保留 == Outer join 左边匹配不上右边的左边留空,右边匹配不上左边的右边留空
- 数据库事务的特点: 比如A有500块 B有500块 A给B转账100块: 从A中读取余额: 500 A中减去100 500 - 100 = 400 结果写入A账号的数据库 从B中读取余额 对B账号做加法操作 500 + 100 = 600 把结果写入B账号的数据库
原子性: 事务包含的所有操作要么全部成功,要么全部失败回滚。保证整个数据库事务是不可分割的工作单位 上述6步之中,任意一步出现了问题,必须回退到第一步操作之前的状态。 一致性: 事务必须要使数据库从一个一致性状态转换为另一个一致性状态,事务执行之前和执行之后状态可以不同,弹药保持一致 两个用户之间的转账 和应该保持不变
隔离性(并发控制,可串行化,锁) :多个用户并发访问数据库时,比如操作同一张表,数据库为每一个用户开启的事务,不能够被其它的事务操作所干扰。
持久性:一个事务一旦被提交了,那么对于数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不应该丢失提交事务的操作。 例子:JDBC操作数据库时,在提交事务方法之后,提示用户事务操作完成,那么即便数据库出问题,也可以保证了事务已经完全被数据库记录。
- 事务的隔离级别: 原理参考:https://www.cnblogs.com/wajika/p/6680200.html https://www.cnblogs.com/wajika/p/6680196.html https://blog.csdn.net/zht666/article/details/51691958
1.读取未提交内容 READ UNCOMMITTED: 最低级别的隔离,允许其他事务看到当前事务未提交的数据,应用很少,因为安全性差且性能并不会提高太多。 产生问题: 脏读 + 不可重复 + 幻读 脏读:读到了未提交的数据 事务对当前被读取的数据不加锁; 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。
2.读取提交内容 READ COMMITTED:保证当前事务的数据只有在被提交之后才能被另一个数据读取。 产生问题:不可重复读 在同一个事务中执行完全相同select语句可以看到不一样的结果。 可能原因: 1. 一个交叉的事务有新的commit, 导致数据改变 2. 一个数据库被多个实例操作,使得同一事务的其他实例在该事务处理期间有新的commit 事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁; 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
3.可重读 REPEATABLE READ:保证同一事务的多个实例在并发读取数据时,看到相同的数据行 产生问题:幻读(Phantom Read) : 当用户读取某一个范围内的数据行时,另一个事务又在该范围内插入了新行,当用户再次读取该范围的数据时,会发现有新的幻影行产生 事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放; 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
4.可串行化 SERIALIZABLE:最高级别的隔离,通过强制事务排序,使事务之间不能够相互冲突,从而解决幻读的问题,在每个读的数据行上加上共享锁 产生问题: 胆量的超时现象和锁竞争
事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放; 事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。
4.数据库的三个范式 1NF: 列不可再分 2NF: 3NF: 一个数据库表中不包含已经在其他表中已包含的非主键信息 (外键)
5.数据库的索引和索引底层数据结构?B数和B+树的区别? 参考资料: mysql索引的数据结构 https://www.jianshu.com/p/1775b4ff123a
MySQL索引使用的数据结构主要有BTree索引 和 哈希索引 BTree M阶的B-Tree要满足 每个节点最多有M个孩子 除根节点和叶节点外,其它每个节点有M/2个孩子
对于每个节点主要包含一个关键字数组Key[],一个指针数组(指向儿子)Son[]
聚集索引: 表中数据按照索引的顺序存储 非聚集索引: 表中数据存储顺序与索引顺序无关
读书笔记 《MySQL技术内幕第二版》
第六章 锁 共享锁(S Lock): 允许事务读一行数据 排他锁(X Lock): 允许事务删除或更新一行数据 锁兼容(Lock Compatible): 事务T1 获得行r1 的共享锁, T1 T2都想要读r1, 没有问题, T2也能够得到r1的共享锁, 但如果T3想要获得行r的排他锁,等T1 T2 释放r1的共享锁,锁不兼容
锁定级别:排他锁 > 共享锁 排他锁要等共享锁, 兼容指的是对于同一行记录的锁
多粒度锁定: 允许对行级别和表级别的锁同时存在。为了支持不同粒度,InnoDB存储引擎支持额外的锁方式,称为意向锁(Intention Lock IX), 意向锁将锁定的对象分为多个层次。
第七章 事务 事务是数据库区别于文件系统的特征,文件系统中,正在写文件时,如果操作系统崩溃,那么文件就可能被损坏。但是事务只会把数据库从一种一致状态转移到另一种一致状态。 【感觉事务就是让颗粒度更大】 事务可以是一条非常简单的SQL语句,也可以是一组复杂的SQL语句,事务是访问并更新数据库中各种数据项的一个程序执行单元。
网络基础
参考资料: TCP相关 https://www.jianshu.com/p/65605622234b 计算机网络相关知识点总结 传输层: 提供进程和进程之间的逻辑通信 可以调用下层网络层的服务: 实现主机与主机之间的逻辑通信 复用: 分用 差错检查 端口: 标识主机中的应用进程 总共有16bit 服务端 客户端 49152 FTP 21 TELNET 23 DNS 53 HTTP 80 套接字: Socker = (主机IP地址和端口号) 唯一标识了网络中的主机和它上面的一个进程
UDP是无连接的,减少开销和发送数据之前的是 1.TCP和UDP链接协议相关 User Datagram Protocol 用户数据报 无连接的,减少开销和发送前的延长 UDP使用最大努力交付,不保证可靠交付 面向报文的, 对于应用层传来的数据包不做任何切割和处理,保留整个应用层报文到UDP报文中 没有拥塞控制,适合实时应用 首部开销 8B 首部格式: 2B * 4 16位源端口号 目的地端口号 UDP长度 UDP检验和
Transmission Control Protocol 传输控制协议 面向连接 只能有两个端点,连接点对点 提供可靠交付的服务、 全双工通信 面向字节流: 文件被标记为字节 TCP把应用程序交下来的数据看成仅仅是一连串的无结构字节流
TCP报文段
固定字段是20B, 此外还有选项字段 长度可变 总长度 = 固定字段 +选项字段 源端口 目的端口 16bit * 2 = 32 bit = 8B 序号 32bit : 表示本报文段所发送数据的第一个字节的序号 确认号 32bit: 期望收到对方下一个报文段的第一个数据字节序号, 若确认号为N, 则证明到序号N - 1为止所有数据都已经收到 数据偏移(首部长度): TCP报文段的数据起始距离 TCP报文段的起始处有多远, 以4B为单位 6个控制位: SYN = 1 表明是一个连接请求/连接接受报文 URG = 1 紧急字段, 表示该报文段很紧急, 要求最先处理 ACK = 1 确认号有效, 建立连接后, 所有传送的报文段都必须将ACK设置为1 FIN = 1 表明此报文段发送方数据已经发完, 要求释放连接
TCP和UDP的区别 使用场景?
TCP的应用场景: 要求通信数据准确可靠 万维网HTTP 邮件 SMTP 文件传输 FTP 远程终端接入 TELNET 大文件 有确认、流量控制、计时器及连接管理 UDP的应用场景: 对通信及时性要求较高的时候 小文件 QQ语音、直播 尽最大努力交付的协议
1.TCP如何保证传输是安全的,TCP如何保证可靠传输?TCP的加密机制? 可靠: 保证接收方进程从缓存区中读出的字节流与发送方发出的字节流是完全一样的 四种可靠传输的机制: 校验: 与UDP校验一样, 增加伪首部 序号: 确认: 默认使用累计确认 来返回确认号字段 重传: 通过确认号字段来确认 超时重传: TCP的发送方在规定时间(也叫做重传时间)内没有收到确认就重传已经发送的报文段 自适应算法 动态改变重传时间RTTs
冗余ACK(冗余确认): 每当比期望序号大的失序报文段到达时, 发送一个冗余ACK, 致命下一个期待字节的序号
快速重传: 当发送方收到3个对于报文段1的冗余ACK -> 认为2报文段丢失,重传2号报文段 TCP的无差错传输?
参考讲解: https://www.youtube.com/watch?v=MrZVMLppgNU&index=41&list=PLO-vrUGI62psPscir0dZPfavFm-9l84iY
基本概念: 滑动窗口协议 发送窗口 接收窗口
实现无差错传输的解决方案: 核心思想:采用一些可靠传输协议,使得
出现差错时,让发送方重传差错数据:即 出错重传 当接收方来不及接收收到的数据时,可通知发送方降低发送数据的效率:即 速度匹配
方案1: 自动重传请求 方案2: 流量控制 & 拥塞控制 拥塞控制的四种算法: 假定: 数据单方向传送,而另一个方向只传送确认
慢开始 拥塞避免 快重传: 当收到了3个重复的确认 就执行快重传算法 重复的确认会在包丢失的时候发生 快恢复 传输轮次: RTT 一个往返时延 一批报文发送到接收到确认的时间 接收窗口: 接收方制定 自身能力 拥塞窗口: 发送方根据网络情况 设定的发送能力
开始阶段: 指数增长 达到一个阈值后进入线性增长,加法增大 当遇到了网络拥塞时, 将阈值重新设定为当前报文段数量 / 2 拥塞窗口初始值 cwnd = 1 一个报文段 MSS
不降到1, 而降到阈值
2.TCP三次握手过程
客户端TCP先向服务器端TCP发送一个特殊的TCP报文段,该报文段不包含应用层数据。在报文段首部中一个标志位(SYN比特)标记为1, 并选择一个随机的初始序号,并将此编号放置于起始的SYN序号字段中,该报文段会被封装在一个IP数据报中,并发送给服务器。 客户端进入SYN_SENT状态 seq = x(随机) 并不起作用,因为没有应用层数据
当服务器收到客户端发出的SYN报文段后,服务器会从数据报中提取出SYN报文段,并为该TCP连接分配TCP缓存和变量,同时发送一个允许连接的SYN报文,包含三个重要信息: 1. SYN位比特设置为1 2.该报文段首部的确认号字段设置为client_isn + 1 3.服务器将自己的server_isn放置到TCP报文段首部序号字段中。SYNACK报文段 服务器进入SYN_RECV状态
在收到SYNACK报文段后,客户也要给该连接分配缓存和变量。并向服务器发送一个确认的报文 确认字段设定为server_isn + 1, SYN位 = 0。ESTABLISHED
为什么需要三次握手? 防止服务器因为不合理的连接请求一直在等待 客户端的请求 浪费资源
TCP报文字段
- 关闭连接四次挥手的过程
任何一端都可能终止连接。 1.请求关闭的一端发送一个FIN 比特设定为1的报文 2.服务器端发送确认报文ACK 进入FIN_WAIT1 3.再次发送FIN比特 FIN_WAIT2 4.当收到FIN比特的同时发送确认报文ACK Client不会立即关闭,而是进入TIME_WAIT状态等待2MSL时间长(一个回复+一个发送所需的时间),我们假定网络是不可靠的,最后的ACK报文可能会丢失,所以需要进行等待。 Server如果没有收到ACK,将不断重复发送FIN片段,如果在TIME_WAIT等待过程中Client收到了FIN,会再多等待2MSL.
4.四次挥手的原因 -因为网络不可靠报文会丢失 为了保证客户端发送的最后一个断开连接的报文能够到达服务器
很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。但是可能不会立即关闭Server,只有等待所有的报文都发送后再发送一个FIN为1的报文确认可以关闭。
报文能够到达服务器 TCP中的拥塞控制? 2.HTTP与HTTPS相关 简单介绍一下HTTP协议? HTTP: HyperText Transfer Protocol 超文本传输协议 属于应用层的协议,作用是为了确定进程之间通信标准,特点是无连接、无状态、传输格式简单、可靠性和兼容性好
HTTP报文结构 请求行(Request Line): 请求方法、主机域名、资源路径&版本协议 请求头(Headers): 声明 客户端|服务器 的部分基本信息 请求体(Body): 发送的数据信息
通用的报文头:
请求头:
响应头:
HTTP的一些缺点: 通信使用明文(不加密), 内容可能会被窃听 应对方案: SSL(Secure Socket Layer, 安全套接层) 加密HTTP的通信内容,用SSL简历安全通信线路后,可以在该线路上进行HTTP通信 不验证通信方的身份,可能会遭遇伪装 HTTP协议中的请求和响应不会对通信方进行确认,也就是说存在无法确认类似 “服务器是否就是发送请求中URI真正制定的主机?”“响应是否返回到真正的客户端?” 等问题 会产生DoS攻击(Denial of Service 拒绝服务攻击) 应对方案: 查证书, 证书是由值得信任的第三方机构颁发的,用来证明服务器和客户端是实际存在的
无法证明报文的完整性,有可能遭遇篡改。无法证明完整也就等于无法判断信息是否准确,有没有伪造的信息?会产生MITM(Man in the Middle attack) 中间人攻击,在请求或相应的传输中途遭到篡改,无法确定客户端想要下载的文件就是服务器上的文件应对方案: MD5 和 SHA-1散列值校验 或确认文件的数字签名
HTTP + 加密+ 认证 + 完整性保护 = HTTPS
3.在浏览器中输入一个URL的全过程 查询浏览器缓存 DNS, 获取域名对应的IP地址 先检查本地hosts文件-> 本地DNS解析器缓存 -> 本地DNS 浏览器与服务器通过三次握手建立TCP连接 浏览器想服务器发送HTTP请求, 应用层 - 传输层 -网络层 - 数据链路层 - 物理层 服务器收到请求后, 根据路径参数和后端的处理生成html代码返回给浏览器 浏览器得到完整HTML后进行解析和渲染 浏览器发送AJAX请求
用到的协议:TCP IP OPSF:用来确定路由选择的 ARP:将IP地址转换为MAC地址
- Session 和 Cookie的区别: Session 跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。 Cookie是在客户端记录信息确定用户身份,而Session是在服务器端记录信息确定用户身份, Session的实现依赖于Cookie, seesionId
cookie存放在浏览器端,session用在服务器端
cookie不太安全,别人可以分析存放在本地的cookie进行cookie欺骗,考虑到安全应当使用session. session会在一定时间内保存在服务器上,当访问增多,会占用服务器的性能,如果想要减轻服务器压力,考虑使用cookie
应用场景: 登录信息等重要信息放在session之中,而其他需要保留的信息可以使用cookie, 购物车最好使用cookie, 但是cookie是可以在客户端禁用的,可以使用cookie + 数据库的实现,如果cookie不能取数据时,就从客户端获取
https://juejin.cn/post/6844903434366222350
ping命令使用的网络协议? DNS : 域名 -> IP ARP: IP -> MAC 地址 ICMP: 用来测试另一台主机是否联通 Internet Control Message Ptotocol 是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。 断点续传功能如何实现?
HTTP1.1协议(RFC2616)中定义了断点续传相关的HTTP头 Range和Content-Range字段,一个最简单的断点续传实现大概如下: 1.客户端下载一个1024K的文件,已经下载了其中512K 2. 网络中断,客户端请求续传,因此需要在HTTP头中申明本次需要续传的片段: Range:bytes=512000- 这个头通知服务端从文件的512K位置开始传输文件 3. 服务端收到断点续传请求,从文件的512K位置开始传输,并且在HTTP头中增加: Content-Range:bytes 512000-/1024000 并且此时服务端返回的HTTP状态码应该是206,而不是200。
如果在这期间文件出现了改动,可以通过Last-Modified Header 实现Last-Modified来标识文件的最后修改时间,这样即可判断出续传文件时是否已经发生过改动 终端在发起续传请求时应该在HTTP头中申明If-Match 或者If-Modified-Since 字段,帮助服务端判别文件变化
存取方式 隐私策略 有效期 服务器压力不同 浏览器支持 跨域支持 Spring框架基础
学习一种框架最先需要知道的是为什么需要使用这个框架, 任何一个框架的发明都是为了解决编程的痛点。
Don’t Repeat Yourself 原则 抽象为方法, 抽象为类, 抽象为多个类的组合(容器类) 是为了帮助我们编写代码而被设计出来的 设计模式级别的抽象(对于具备一定规模的代码才有效) 框架层面上的应用
设计模式的使用场景: 举例: 一个简单的数据库事务,虽然只有一个查询和一个更新,但是想要将其继续简化却并不容易,虽然其中有关于业务逻辑的部分只是少量几行代码,但是初始化,异常,提交,回滚操作让我们很难抽取出一个合适的方法来。
我们发现之所以我们难以抽取方法,主要是因为流程,因为里面牵扯到流程控制,而流程控制一般是由我们程序员来控制的,所以也就必然需要我们手动编码来完成。
框架其实就是一个或一组特殊的类库,特殊在什么地方?特殊在控制权转移!
JavaWeb的发展脉络: 静态网页时代 Servlet时代 JSP包打天下的时代 Servlet + JSP 时代 MVC模式时代
Spring的第一步 理解IOC 和 AOP, 然后学习Spring MVC即使就是Java EE开发? 什么是Java EE?
Tomcat Tomcat提供能够让别人远程访问自己写的页面的一个程序
1.Spring的理解? IOC + AOP 为什么要把创建对象交给Spring容器?
Spring是一个J2EE开源框架,核心思想是IOC和AOP。 Spring让java开发模块化,并且可以全面贯穿逻辑层,表现层与持久层。让每一个功能模块都可以独立分开,降低耦合,提高代码复用率。为简化企业级应用开发而生, 使用Spring可以使得简单的JavaBean实现以前只有EJB才能实现的功能。
声明式事务?? 轻量级: Spring是非侵入式的,用Spring不需要实现任何借口,继承父类就能够使用 依赖注入(DI - dependency injection, IOC) 面向切面编程: AOP-aspect oriented programming 一站式: 在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(Spring实现了展现层的SpringMVC和持久层的Spring JDBC)
搭建环境: 将下面的包加入到工程中的classpath下面 Spring的配置文件: 一个或多个配置文件, 用于在Spring IOC容器里配置Bean, Bean的配置文件可以放在ClassPath下, 也可以在其它目录下 spring-beans spring-core spring-context spring-expression
Spring bean configuration file applicationContext.xml
- 创建SpringIOC容器对象 ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);
ApplicationContext代表Spring里面的IOC容器,是一个接口, ClassPathXmlApplicationContext 表示配置文件在类路径下 applicationContext.xml 配置文件的名字
- 从IOC容器中获取Bean实例 HelloWorld helloWorld = (HelloWorld) ctx.getBean(“helloWorld”); bean 名字 = getBean() 是重载方法
创建容器的时候会进行一个初始化方法 以及调用set方法
几个基本问题: 什么是ServletContext, 和tomcat等web容器的关系是什么? 依赖注入,控制反转和非侵入式。
Servle
工厂设计模式: 参考Java3y
简单/静态工厂模式 工厂方法模式 抽象工厂模式 为什么要用工厂模式,工厂模式的好处是什么?工厂模式衍生出的三种形式是什么?
举例: 读取文件需要调用BufferReader方法 让创建对象变得简单而且修改对象时能很方便呢? 工厂将创建对象的过程给屏蔽了
public class ReaderFactory { public static Reader getReader() throws FileNotFoundException { File file = new File("aa.txt"); FileReader fileReader = new FileReader(file); BufferedReader reader = new BufferedReader(fileReader); return reader; } }
public class FileOperateA {
public static void main(String[] args) throws FileNotFoundException {
//-------我有工厂了,还用自己搞吗?不用了!
//File file = new File("aa.txt");
//FileReader fileReader = new FileReader(file);
//BufferedReader bufferedReader = new BufferedReader(fileReader);
//-------我有工厂了,还用自己搞吗?不用了!
// 用工厂来创建出对象
Reader reader = ReaderFactory.getReader();
// 读写文件....
}
}
linux基础 用户权限相关的命令
线程和进程的区别: 进程是具有一定独立功能的程序关于某个数据集合上的运行活动, 进程是系统进行资源分配和调度的独立单位。
线程是进程的一个实体,CPU调度和分派的基本单位,比进程更小,基本上自己不拥有系统资源,只拥有程序运行必不可少的资源(程序计数器,寄存器,栈),可以同属一个进程的其它线程共享进程的资源,提高程序的运行效率。
一个线程可以创建和撤销另一个线程,同一个进程中多个线程可以并发执行。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行。
JUC java.util.concurrent
ThreadLocal类 通过将共享数据封装为不共享数据来保证同步,被称为线程封闭,常应用于JDBC的Connection对象 将JDBC的连接保存到ThreadLocal对象中,每个线程都会拥有属于自己的连接
synchronized关键字以及实现原理 可以修饰 1.普通方法 2.静态方法 3.代码块
synchronized关键字经过编译后,会在同步块的前后分别形成moniterenter和monitorexit两个字节码指令,这两个字节码指令需要一个reference类型参数来致命要锁定和解锁的对象,获取对象的锁,没被锁定或当前线程已经拥有那个对象的锁,就把锁的计数器加一,执行monitorexit指令时将计数器-1。
Java中的锁结构
悲观锁(适合写多读少)/乐观锁(适合写少读多): 自己用数据的时候一定有/没有别的线程来修改数据,要/不要在获取数据的时候加上锁 补充: 乐观锁修改数据时不加锁,只会在更新数据的时候判断之前有没有别的线程更新了该数据,如果没有更新,将自己的修改写入,否则执行其他操作(报错或重试) synchronized关键字和Lock实现类都是悲观锁, 关系型数据库中也常见,比如行级锁,写入锁都在操作之前上锁
乐观锁最常采用CAS算法来实现, CAS全称Compare And Swap, 是一种无锁算法,在不使用锁的情况下实现多线程之间的变量同步,atomic类就通过CAS来实现乐观锁
CAS用到三个操作数字: 内存值V, 进行比较的值A,需要写入的新值B, 当前仅当V的值等于A时,CAS通过原子方式用新值B更新V。
潜在问题:
- ABA问题,看似没变其实变了又变, 打时间戳
- 循环时间开销大
- 只能保证对于一个共享变量的原子操作
JUC 类图 是为java并发编程设计的工具类: locks 提供显式锁相关功能 atomic 提供原子变量功能,构建非阻塞算法基础 executor 提供线程池功能 collections 提供并发容器功能 tools 提供同步工具类,如信号量,闭锁,栅栏等功能
数据结构基础 设计一个高效率获取第K大和前K个元素的数据结构 实现HashMap
OO Design Design Twitter 实现
算法基础
基础排序算法
手写快排 public class Quick { private Quick() {}
private static void sort(int[] a, int lo, int hi) {
if (hi <= lo) return;
int pivot = partition(a, lo, hi);
sort(a, lo, pivot - 1);
sort(a, pivot + 1, hi); }
private static int partition(int[] a, int lo, int hi) { int i = lo; int j = hi + 1; int v = a[lo]; // select first element as the pivot point while (true) { while (a[++i] < v)) { if (i == hi) break; } // 停在了从左边数起第一个比v大的元素 (out of place) while (v < a[--j]) { if (j == lo) break; } //停在了从右边数起第一个比v小的元素 (out of place) if (i >= j) break; swap(a, i, j); } // 现在的j停在了比 v小的所有元素中的最后一个元素 swap(a, lo, j); return j; // j 和 lo交换之后 v就回到了应该在的位置 }
private static void swap(int[] a, int i, int j) { int swap = a[i]; a[i] = a[j]; a[j] = swap; }
public static void main(String[] args) { int[] a = new int[]{1, 97, 3, 45, 5, 8}; Quick.sort(a, 0, a.length - 1); }
}
手写堆排序? void heapSort(int[] arr, int n){ for (int i = n / 2 - 1; i >= 0; i--) { heapify(arr, n, i); // creates a max heap } for (int i = n - 1; i >= 0; i--) { swap(arr[0], arr[i]); // swap first and last node heapify(arr, i, 0); } }
手写归并排序
不同排序算法的时间复杂度?排序的稳定性?
https://zhuanlan.zhihu.com/p/36120420 https://blog.csdn.net/u010983881/article/details/76383527
堆排序、快速排序、希尔排序、直接选择排序不是稳定的排序算法; 基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。
稳定性定义:排序前后两个相等的数相对位置不变,则算法稳定。 详解: 冒泡排序 小的元素向前或大的元素向后, 比较的对象也是相邻的两个元素 -> 稳定 选择排序 每个位置都选择当前最小的,如果一趟选择中,当前元素比一个元素小,而小的元素前一个元素又和要交换的值相等,稳定性被破坏 例如:序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了 -> 不稳定 插入排序 在已经有序的序列上做操作,一次插入一个元素, 想要插入的元素和已经有序的最大者开始比较,如果比它大直接插入到其后面,否则一致向前找直到找到该插入的位置,如果碰见相等的,放在相等的后面,先到先得 快速排序 中枢元素交换的时候会打破稳定性 举例: 序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱 归并排序 稳定 堆排序 堆的结构是节点i的孩子为2 * i 和 2 * i +1节点,大顶堆 void heapSort(int[] arr, int n) { for 9
海量数据处理 大量的url如何去重? 1.如果内存够用,将url作为key存入hash表 将hash值作为value, HashSet 去重
找数字类: 在内存不够的情况下,如何从 2亿个整数中找出不重复的整数? 40亿个非负整数中找出所有没有出现过的数字? 最多使用1GB内存
2-Bitmap方法: 对于每一个整数分配2bit 00代表没出现 01代表出现一次 10代表出现两次 11无意义 , 扫描2亿个整数, 并修改Bitmap中对应位, 00 -> 01 10 -> 11 10不变, 扫描结束后,输出对应位为 01的所有整数
32位无符号整数范围 0 - 4294967295
2^32 = 4 294 967 296 * 2 bit
1GB = 8 000 000 000 bit = 1 000 000 000 B
哈希表 key: 数字 value: 该数字出现的次数 32位整数取值(0 - 42亿) 4B key 和 value都是4B就足够, 那么一条Hash表的记录就是8B, 当哈希表的记录数为40亿个时,40亿 × 8 = 320亿字节 = 32 GB内存 内存中放不下, 改为BitMap来记录 2^32 / 8 = 2 ^ 29 = 536870912 大概500MB 扫描这个40亿个数,将每个数对应位的0 -> 1,最后扫一遍BitMap, 其中bit位为0的就是没出现的数字, 返回该数字即可。
进阶: 如果只给10MB内存,找到40亿中一个没出现的数字? 因为 500MB/64 < 10MB 所以将40亿个数分为64个区间, 4 294 967 296 / 64 = 67108864个数字, 统计落在每个区间上的数字有多少, 肯定有一个区间上的计数小于67108864, 然后找到这个区间之后,再按照0 / 1 对应每个数字 这次的索引是 [num - 67108864 * N] N为区间的序号
Amazon OA面经
OA1 Debug:. check 1point3acres for more. https://www.evernote.com/client/ ... %2F636f07d57c2eb3ea
Reasoning: https://www.evernote.com/client/ ... %2F0ebd7ed6e9399009
file:///home/auguskong/Downloads/AmazonOA%E6%95%B4%E7%90%862019.02.pdf
OA2
https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=480213&extra=page%3D1%26filter%3Dsortid%26sortid%3D311%26orderby%3Ddateline%26sortid%3D311%26orderby%3Ddateline https://docs.google.com/document/d/1uQAAe21jQzJiuNzJs2WZ7bdzM5Rx2gHiRghflLxEx8c/edit?usp=sharing
Longest Palindrome Substring
Most Common Word
- Longest Substring with At Most K Distinct Characters sliding window Dungeon Game
- Subtree with Maximum Average class ResultType { TreeNode node; int sum; int size; public ResultType(TreeNode node, int sum, int size) { this.node = node; this.sum = sum; this.size = size; } } private ResultType result = null; 找出所有长度为K, 含有K-1个不同字符的子串
https://leetcode.com/discuss/interview-question/124652/Amazon-onsite-interview-question
https://github.com/jayshah19949596/CodingInterviews/tree/master/Amazon%20SDE%20-%201%20-%20FBA%20Team
https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=480213 https://drive.google.com/file/d/1TjufxeW4LZzfsGfzJ9dPU2lJqGMMEC3d/view
subStringLessKDist getFastestComponent
given an array of n point, and the problem is to find out the top k closest pair of points in the array.
Shorest Path
- Given a M*N array. 1point3acres
- Start from right left corner, end at 9
- Path can only build on value of 1
- Find the shortest path from the start to target
https://www.1point3acres.com/bbs/forum.php?mod=viewthread&tid=485925&extra=page%3D3%26filter%3Dsortid%26sortid%3D311%26searchoption%5B3046%5D%5Bvalue%5D%3D5%26searchoption%5B3046%5D%5Btype%5D%3Dradio%26sortid%3D311%26orderby%3Ddateline