PageRenderTime 57ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/Java/Java常用数据结构.md

https://bitbucket.org/fushenghua/interview-java
Markdown | 668 lines | 455 code | 213 blank | 0 comment | 0 complexity | 052694daa7f11fbfce1760afb196c468 MD5 | raw file
  1. # Java常用数据结构
  2. 最近在整理[Android](http://teachcourse.cn/tag/android "【查看含有[Android]标签的文章】")岗位[面试题](http://teachcourse.cn/tag/%e9%9d%a2%e8%af%95%e9%a2%98 "【查看含有[面试题]标签的文章】")的[答案](http://teachcourse.cn/tag/%e7%ad%94%e6%a1%88 "【查看含有[答案]标签的文章】"),虽然工作已有两年,独立开发了好几个APP,但在不查资料的情况下,回答这些试题非常的困难,瞬间感觉一万点伤害。即使不为了找工作,整理这样一份Android面试题答案,也可以加深对各个知识点的理解,这一整套面试题基于《*[最全的BAT大厂面试题整理](https://www.jianshu.com/p/c70989bd5f29)*》,然后将每一个分类拆分成附带参考答案的独立的文章,在此非常感谢整理试题的原作者。
  3. [Android学习笔记六Java基础知识](http://teachcourse.cn/2602.html)
  4. [Android学习笔记七Java源码深入学习](http://teachcourse.cn/2606.html)
  5. [Android学习笔记八Java常用数据结构](http://teachcourse.cn/2609.html#)
  6. [Android学习笔记九Java线程多线程和线程池27号更新](http://teachcourse.cn/2609.html#)
  7. *Note that文章中给出的答案尽管参考了网上的很多资料但肯定回答得还不够到位更需要在实际的环境中运用验证有木有推荐岗位哈*
  8. * 常用数据结构简介
  9. 常用的数据结构有数组链表队列散列
  10. * 并发集合了解哪些
  11. | 常用并发集合 | 举例 |
  12. | :-- | :-- |
  13. | 并发List | VectorCopyOnWriteArrayList |
  14. | 并发Set | CopyOnWriteSetConcurrentSkipListSet |
  15. | 并发Queue | ArrayBlockQueueConcurrentLinkedQueueLinkedBlockingQueueLinkedTransferQueuePriorityBlockingQueueSynchronousQueue |
  16. | 并发Deque | ConcurrentLinkedDequeLinkedBlockingDeque |
  17. 详情
  18. Vector和CopyOnWriteArrayList是线程安全的
  19. 区别
  20. Vector给每个方法添加了synchronized同步锁保证多个线程同时访问`add()``get()``remove()`等方法的安全但不保证遍历的线程安全即一个线程遍历另外一个线程执行添加删除或其他为了保证遍历的安全需要给遍历的Vector对象添加同步锁
  21. CopyOnWriteArrayList的实现原理每个线程访问的是CopyOnWriteArrayList的一个副本最后将原对象引用指向最后的CopyOnWriteArrayList的地址保证线程安全
  22. [如何线程安全地遍历ListVectorCopyOnWriteArrayList](https://www.cnblogs.com/wucao/p/5350461.html)
  23. CopyOnWriteSet的实现原理和CopyOnWriteArrayList一样通过对副本进行访问最后将引用指向该地址
  24. Queue和Deque前者是普通的队列后者是双端的队列双端队列是指队列的头部或尾部可以进行入队操作和出队操作
  25. [Java中的queue和deque](http://blog.csdn.net/shf4715/article/details/47052385)
  26. * 列举java的集合以及集合之间的继承关系
  27. **List集合**
  28. ArrayList集合
  29. ![ArrayList继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/arraylist_structure.png)
  30. LinkedList集合
  31. ![LinkedList继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/linkedlist_structure.png)
  32. Vector集合
  33. ![Vector继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/vector_structure.png)
  34. **Map集合**
  35. HashMap集合
  36. ![HashMap继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/hashmap_structure.png)
  37. LinkedHashMap集合
  38. ![LinkedHashMap继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/linkedhashmap_structure.png)
  39. TreeMap集合
  40. ![TreeMap继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/treemap_structure.png)
  41. HashTable集合
  42. ![HashTable继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/hashtable_structure.png)
  43. Properties集合
  44. ![Properties继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/properties_structure.png)
  45. *备注*`Properties``HashTable`是Map接口的历史属性
  46. **Set集合**
  47. HashSet集合
  48. ![HashSet继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/hashset_structure.png)
  49. LinkedHashSet集合
  50. ![LinkedHashSet继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/linkedhashset_structure.png)
  51. TreeSet集合
  52. ![TreeSet继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/treeset_structure.png)
  53. > [Android Studio UML插件用法](http://plantuml.com/class-diagram)
  54. **Deque集合**
  55. ArrayDeque集合
  56. ![ArrayDeque继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/arraydeque_structure.png)
  57. **Queue集合**
  58. PriorityQueue集合
  59. ![PriorityQueue继承关系](http://7xrk8u.com1.z0.glb.clouddn.com/priorityqueue_structure.png)
  60. * 集合类以及集合框架
  61. Collection体系
  62. ![Collection集合框架](http://7xrk8u.com1.z0.glb.clouddn.com/1342513304_6964.png)
  63. Map体系
  64. ![Map集合框架](http://7xrk8u.com1.z0.glb.clouddn.com/1342513335_2987.png)
  65. > [Java集合类详解](http://blog.csdn.net/u014136713/article/details/52089156)
  66. * 容器类介绍以及之间的区别
  67. > [Java容器类](http://alexyyek.github.io/2015/04/06/Collection/)
  68. * List,Set,Map的区别
  69. **Set集合**
  70. 不允许存储`a.equlas(b)`两个相同的对象否则后存储的对象会代替已存在的对象
  71. ![Set接口相关方法](http://7xrk8u.com1.z0.glb.clouddn.com/20180310171742.png)
  72. **List集合**
  73. 允许存储`a.equals(b)`两个相同的对象同时允许存储多个null值可以根据元素的整型索引快速访问元素
  74. 除了具备Set集合提供的方法外SetList都继承自Collection接口还添加了多个根据索引访问修改元素的方法
  75. ![List接口相关方法](http://7xrk8u.com1.z0.glb.clouddn.com/20180310171705.png)
  76. **Map集合**
  77. 不允许存储重复的键同时不允许将自身对象作为键存储但允许将自身对象作为值存储
  78. * List和Map的实现方式以及存储方式
  79. ArrayList以数组表的方式存储数据允许存储相同的对象方便快速地通过索引查询
  80. LinkedList以链表的方式存储数据允许存储相同的对象适合频繁地插入删除操作
  81. Vector除了支持同步操作外和ArrayList基本一样
  82. HashMap以分散数组表的方式存储数据不允许存储键相同的对象同时也不允许将自身对象作为键存储但可以将自身对象作为值存储也可以存储值相同的对象
  83. LinkedHashMap以分散链表的方式存储数据继承自HashMap优化了数据的插入删除操作
  84. TreeMap以二叉查找树的方式存储数据除了具备Map集合的特点外TreeMap还对存储的数据进行排序
  85. > [ListMapSet按存储方式说说都是怎么存储的?](http://blog.csdn.net/Mr_linjw/article/details/51335490)
  86. >
  87. > [几种 Map 内部存储方式的介绍( Java 为例讲解 )](http://blog.csdn.net/weixinzhang/article/details/50614438)
  88. * HashMap的实现原理
  89. 为了提高查询的效率和减少空间的浪费初始化HashMap的容量大小必须是2的n次方
  90. 尽量避免扩充容量防止扩容对性能造成影响扩容后的HashMap需要重新计算已存在数据的在新数组中的位置扩容后的大小是原来的两倍
  91. 默认负载因子0.75是对时间和空间的平衡选择
  92. 快速失败策略HashMap不是线程安全的在进行迭代的时候有其他线程修改了map会抛出ConcurrentModificationException
  93. HashMap是一种数组+链表的存储方式对数据进行迭代需要遍历两次
  94. **第一种**
  95. ```
  96. Map map = new HashMap();
  97.   Iterator iter = map.entrySet().iterator();
  98.   while (iter.hasNext()) {
  99.   Map.Entry entry = (Map.Entry) iter.next();
  100.   Object key = entry.getKey();
  101.   Object val = entry.getValue();
  102.   }
  103. ```
  104. 效率高,以后一定要使用此种方式
  105. **第二种**
  106. ```
  107. Map map = new HashMap();
  108.   Iterator iter = map.keySet().iterator();
  109.   while (iter.hasNext()) {
  110.   Object key = iter.next();
  111.   Object val = map.get(key);
  112.   }
  113. ```
  114. 效率低,以后尽量少使用
  115. * HashMap数据结构
  116. 是一个数组+链表的数据结构内部定义了一个Entry数组数组中的每一项又是一个链表
  117. * HashMap源码理解
  118. 根据实际存储数据的长度初始化HashMap容量初始化的容量大小必须是2的n次方尽量避免扩充容量
  119. 默认HashMap的初始化容量为16负载因子为0.75它是对时间和空间的平衡选择
  120. 快速失败策略HashMap是非线程安全的对数据进行迭代的时候其他线程试图修改map会抛出ConcurrentModificationException异常
  121. * HashMap如何put数据从HashMap源码角度讲解
  122. ```
  123. public V put(K key, V value) {
  124. // HashMap允许存放null键和null值。
  125. // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
  126. if (key == null)
  127. return putForNullKey(value);
  128. // 根据key的keyCode重新计算hash值。
  129. int hash = hash(key.hashCode());
  130. // 搜索指定hash值在对应table中的索引。
  131. int i = indexFor(hash, table.length);
  132. // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
  133. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  134. Object k;
  135. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  136. V oldValue = e.value;
  137. e.value = value;
  138. e.recordAccess(this);
  139. return oldValue;
  140. }
  141. }
  142. // 如果i索引处的Entry为null,表明此处还没有Entry。
  143. modCount++;
  144. // 将key、value添加到i索引处。
  145. addEntry(hash, key, value, i);
  146. return null;
  147. }
  148. ```
  149. 添加到HashMap中的数据先判断key值是否为nullkey值为null直接将value值放置在数组的第一个位置
  150. 否则计算key值的hashCode同时定位数组中的位置如果该hashCode已存在那么将value值添加到该位置的链表上否则直接将value值添加到数组上
  151. > [HashMap的实现原理](http://www.importnew.com/16301.html)
  152. * HashMap怎么手写实现
  153. ```
  154. public class HashMap<K,V>{
  155. static HashMapEntry<K,V> implement Map.Entry<K,V>{
  156. K key;
  157. V value;
  158. HashMapEntry<K,V> next;
  159. int hash;
  160. ...
  161. }
  162. int DEFUALT_CAPACITY=16;//定义默认的容量大小
  163. int DEFUALT_LOAD_FACTOR=0.75;//定义默认的加载因子
  164. static final HashMapEntry<?,?> EMPTY_TABLE={};//定义默认的空数组
  165. HashMapEntry<K,V>[] table=(HashMapEntry<K,V>[])EMPTY_TABLE;
  166. public HashMap<K,V>(int capacity,int loadFactor){
  167. this.DEFAULT_CAPACITY=capacity;
  168. this.DEFAULT_LOAD_FACTOR=loadFactor;
  169. if(table==EMPTY_TABLE){
  170. table=new HashMapEntry[DEFAULT_CAPACITY*DEFAULT_LOAD_FACTOR];
  171. }
  172. }
  173. public V put(K key,V value){
  174. //计算key对应的hash值
  175. int hash=key.hashCode();
  176. int i=indexFor(hash,table.length);
  177. //判断是否存在相同的key,存在,则覆盖
  178. for(HashMapEntry<K,V> e=table[index];e!=null;e=e.next){
  179. if(e.hash==hash&&(e.key==key||e.key.equals(key))){
  180. int oldValue=e.value;
  181. e.value=value;//覆盖旧value
  182. return oldValue;
  183. }
  184. }
  185. addEntry(hash,key,value,i);
  186. return null;
  187. }
  188. int indexFor(int hash,int length){
  189. return hash& (length-1);
  190. }
  191. void addEntry(int hash,V key,V value,int index){
  192. ...
  193. }
  194. }
  195. ```
  196. * ConcurrentHashMap的实现原理
  197. 除了具备HashMap的特点外ConcurrentHashMap是线程安全的采用的是分段锁技术比其他同步锁的方式`HashTable``Collections.synchronizedMap()`性能更高
  198. *分段锁技术*指的是ConcurrentHashMap加锁的是Map.Entry对象在JDK_1.8.0_51版本使用的是synchronized同步代码块由JVM自动添加并释放锁而在其它JDK版本也有使用Lock方式的
  199. ```
  200. /** Implementation for put and putIfAbsent */
  201. final V putVal(K key, V value, boolean onlyIfAbsent) {
  202. ...
  203. for (Node<K,V>[] tab = table;;) {
  204. Node<K,V> f; int n, i, fh;//Node继承自Map.Entry
  205. if (tab == null || (n = tab.length) == 0)
  206. tab = initTable();
  207. else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
  208. ...
  209. }
  210. else if ((fh = f.hash) == MOVED)
  211. tab = helpTransfer(tab, f);
  212. else {
  213. V oldVal = null;
  214. //使用synchronized同步代码块,加锁的是Node对象
  215. synchronized (f) {
  216. if (tabAt(tab, i) == f) {
  217. ...
  218. }
  219. }
  220. ...
  221. }
  222. }
  223. addCount(1L, binCount);
  224. return null;
  225. }
  226. ```
  227. > [ConcurrentHashMap原理分析](http://www.importnew.com/16142.html)
  228. >
  229. > [ConcurrentHashMap实现原理](http://blog.csdn.net/dingji_ping/article/details/51005799)
  230. * ArrayMap和HashMap的对比
  231. ArrayMap的数据存储方式使用的是两个小数组一个数组按顺序存储key对应的hash值一个数组根据key的顺序存储key-value值查询的时候在第一个数组中获取hash值的索引再由索引在后一个数组获取value值
  232. HashMap的数据存储方式使用的是数组+链表根据key对应的hash值检索数组是是否存在对应的value值如果不存在直接将value存储到索引所在位置如果存在将放置在该位置的链表头部
  233. 区别
  234. ArrayMap数据的访问速度比HashMap更快适合应用在数据量不多1000以内访问操作频繁插入和删除较少的场景
  235. * HashTable实现原理
  236. HashTable是以数组+链表的方式存储数据数组存储的是key对应的hash值
  237. HashTable的方法是同步的说明它是线程安全的继承自Dictionary实现Map接口
  238. HashTable默认容量大小为11默认加载因子为0.75分析JDK1.8_
  239. HashTable的keyvalue都不可以为null
  240. > [Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例](http://www.cnblogs.com/skywang12345/p/3310887.html)
  241. * TreeMap具体实现
  242. ...
  243. > [红黑树数据结构剖析](http://www.cnblogs.com/fanzhidongyzby/p/3187912.html)
  244. >
  245. > [红黑树系列集锦](http://blog.csdn.net/v_JULY_v/article/category/774945)
  246. >
  247. > [Java提高篇二七-----TreeMap](http://blog.csdn.net/chenssy/article/details/26668941)
  248. * HashMap和HashTable的区别
  249. HashMapHashTable存储数据的方式是一样的都是数组+链表的方法
  250. HashMap允许keyvalue为null如果key为null直接将value存储到数组的第一个位置HashTable不允许keyvalue为null如果value为null抛出NullPointException异常
  251. HashMap是非线程安全的HashTable相关方法添加了synchronized同步锁是线程安全的
  252. HashMap快速失败机制在遍历数据的时候其它线程试图修改删除HashMap数据会抛出ConcurrentModificationException异常
  253. * HashMap与HashSet的区别
  254. 数据结构不一样HashMap是以数组+链表的方式存储数据数组存储的是HashEntry实体每个HashEntry实体保存key和value同时持有指向下一个元素的引用next
  255. HashSet的内部实现实例化一个HashMap对象将要存储的数据作为HashMap对象的key将一个固定Object对象`PRESENT`作为value
  256. HashMap中不允许重复的键HashSet不允许存储相同的对象如果存储两个相同的对象后存储的对象会覆盖已存储的对象
  257. HashMap继承自AbstractMap实现Map接口HashSet继承自AbstractSet实现Set接口
  258. > [HashMap和HashSet的区别](http://www.importnew.com/6931.html)
  259. * HashSet与HashMap怎么判断集合元素重复
  260. 比较集合中是否存在当前对象的hash值如果已存在再通过equals方法比较两个对象是否相同如果hash值一样同时equals比较相同则判断它们是重复的元素
  261. 所以存储到HashSet和HashMap集合中的元素重写equals方法的同时需要重写hasCode方法否则使用默认的equals方法和hashCode方法
  262. * 集合Set实现Hash怎么防止碰撞
  263. 出现碰撞的原因是因为存储到Set集合的对象定位到Entry数组相同的位置数组的index由存储对象的hash值和数组的length长度决定即index=hash & (length-1)
  264. 优化过的hash值和2的n次方的length长度使得元素的分布更加的均匀发生碰撞的几率减少从而有效地防止碰撞
  265. * ArrayList和LinkedList的区别以及应用场景
  266. ArrayList继承自AbstractList实现List接口RandomAccessFile接口以数组的方式存储数据
  267. LinkedList继承自AbstractSequentialList实现List接口Deque接口以链表的方式存储数据
  268. 数组的方式存储数据方便快速查询元素适合应用于查询多删除和插入较少的场景
  269. 链表的方式存储数据删除和删除元素时不需要移动元素的位置性能方面比ArrayList更优适合应用于删除和插入较多的场景
  270. * 数组和链表的区别
  271. 数组的方式存储数据元素在内存中是连续存放可以通过下标快速访问数组中任一元素当插入或删除一个元素时需要移动大部分的元素
  272. 链表的方式存储数据元素在内存中不是顺序存放一个元素持有下一个元素的引用将指针指向下一个元素依次类推当查询元素时需要依次遍历链表中的各个元素
  273. * 二叉树的深度优先遍历和广度优先遍历的具体实现
  274. ![二叉树遍历算法](http://7xrk8u.com1.z0.glb.clouddn.com/805461-20160514164458359-1600906940.png)
  275. **深度优先搜索**(Depth First Search)是沿着树的深度遍历树的节点尽可能深的搜索树的分支以上面二叉树为例深度优先搜索的顺序为ABDECFG怎么实现这个顺序呢深度优先搜索二叉树是先访问根结点然后遍历左子树接着是遍历右子树因此我们可以利用堆栈的先进后出的特点现将右子树压栈再将左子树压栈这样左子树就位于栈顶可以保证结点的左子树先与右子树被遍历
  276. ```
  277. public static void print(BinaryTreeNode node){
  278. System.out.println(node.value);
  279. }
  280. ```
  281. ```
  282. public static void preOrder(BinaryTreeNode node){
  283. if(node!=null{
  284. print(node)
  285. preOrder(node.left);
  286. preOrder(node.right);
  287. }
  288. }
  289. ```
  290. **广度优先搜索**(Breadth First Search),又叫宽度优先搜索或横向优先搜索是从根结点开始沿着树的宽度搜索遍历上面二叉树的遍历顺序为ABCDEFG.
  291. 可以利用队列实现广度优先搜索
  292. ```
  293. public static void levelOrderTranversal(BinaryTreeNode root){
  294. Queue<BinaryTreeNode> queue=new LinkedList<BinaryTreeNode>();
  295. if(root!=null){
  296. queue.add(root);
  297. while(!queue.isEmpty){
  298. BinaryTreeNode node=queue.poll();
  299. print(node);
  300. if(node.left!=null){
  301. queue.add(node.left);
  302. }
  303. if(node.right!=null){
  304. queue.add(node.right);
  305. }
  306. }
  307. }
  308. }
  309. ```
  310. > [树的深度优先遍历和广度优先遍历的原理和java实现代码](http://outofmemory.cn/code-snippet/4189/biinary-tree-java)
  311. >
  312. > [二叉树的广度优先遍历深度优先遍历的递归和非递归实现方式](https://www.cnblogs.com/gl-developer/p/7259251.html)
  313. * 堆的结构
  314. 堆的结构是一颗完全二叉树
  315. 完全二叉树是指除底层外其余各层任意节点的子节点数都达到了最大数底层的所有节点都连续集中在左边
  316. ![完全二叉树](http://7xrk8u.com1.z0.glb.clouddn.com/818487-20151007234152284-380514952.jpg)
  317. > [基本数据结构(Heap)的基本概念及其操作
  318. > ](https://www.cnblogs.com/JVxie/p/4859889.html)
  319. * 堆和树的区别
  320. 将满足结构性的树称为堆堆也可以说是一种树
  321. 结构性指的是除底层外其余各层任意节点的节点数都达到了最大数底层的所有节点都连续集合中左边
  322. 二叉堆指的是满足堆的结构性同时任意节点的值小于其所有子节点的值
  323. * 堆和栈在内存中的区别是什么
  324. 是jvm管理的最大一块内存同时也是Java GC主要的区域堆是用来存储对象实例和数组值所有线程共享
  325. 分为Native方法栈和JVM 前者主要是存储Native方法的状态后者是线程私有的每个方法在调用的时候都会创建一个栈帧栈帧存储有局部变量等信息方法被调用时栈帧被压入JVM栈中,方法执行完成栈帧出栈
  326. > [JVM内存管理及GC机制](http://blog.csdn.net/suifeng3051/article/details/48292193)
  327. * 什么是深拷贝和浅拷贝
  328. **深拷贝**也叫深克隆将原对象的所有字段拷贝到副本中不可是原对象的值类型字段还是引用类型字段在副本中都会被重新的创建并赋值对副本值类型字段或引用类型字段的修改不会影响原类型实现Cloneable接口覆盖close方法
  329. **浅拷贝**也叫浅克隆将原对象的所有字段拷贝到副本中对副本中值类型字段的修改不会影响原类型但对副本中引用类型字段的修改会影响原类型实现Cloneable接口重写close方法
  330. > [Java中的深拷贝和浅拷贝](http://blog.csdn.net/chjttony/article/details/7477346)
  331. * 手写链表逆序代码
  332. ```
  333. public static Node reverseSingleList(Node node){
  334. Node p1,p2=null;
  335. p1=node;//记录node节点本身地址
  336. while(node.next!=null){
  337. p2=node.next;//记录node节点引用地址
  338. node.next=p2.next;//记录新节点持有的引用地址
  339. p2.next=p1;//改变指针指向
  340. p1=p2;//记录新的节点本身地址
  341. }
  342. return p2;
  343. }
  344. ```
  345. > [java 实现单链表的逆序](http://blog.csdn.net/u012571415/article/details/46955535)
  346. >
  347. > [Java单链表的逆序](http://blog.csdn.net/wxm349810930/article/details/46724691)
  348. >
  349. > [链表逆序(JAVA实现)](https://www.cnblogs.com/jsczljh/p/3765720.html)
  350. * 讲一下对树B+树的理解
  351. 树是一种由一个根节点和无数个子节点组成的数据结构除根节点外每一个子节点都拥有一个父节点和无数个子节点常用的树的集合有TreeMapTreeSet
  352. B+树是其中一种...
  353. > [从B树B+B*树谈到R ](http://blog.csdn.net/v_july_v/article/details/6530142)
  354. >
  355. > [B树和B+树的总结](https://www.cnblogs.com/George1994/p/7008732.html)
  356. >
  357. > [在线可视化树操作](https://www.cs.usfca.edu/~galles/visualization/BTree.html)
  358. * 讲一下对图的理解
  359. ...
  360. * 判断单链表成环与否
  361. ```
  362. public class SingleList{
  363. static class Node {
  364. int value;
  365. Node next;
  366. public Node(int i){
  367. this.value=i;
  368. }
  369. }
  370. /**
  371. *创建一条单链表
  372. *@params head 表示单链表开始位置
  373. *@params length 表示单链表的长度
  374. */
  375. public Node createSingleList(Node head,int length){
  376. if(head==null){
  377. return null;
  378. }
  379. Node p1=head;//记录开始的内存地址
  380. int i=0;
  381. while(i<length){
  382. Node node=new Node(i);
  383. p1.next=node;//记录持有的引用地址
  384. p1=node;//移动指针到下一个节点
  385. i++;
  386. }
  387. return head;//返回开始的列表地址
  388. }
  389. /**
  390. *判断一条单链表是否成环
  391. *@params node 传入的单链表
  392. *@return 成环,返回true;否则,返回false
  393. */
  394. public boolean loopSingleList(Node node){
  395. if(node==null||node.next==null){
  396. return false;
  397. }
  398. Node p1,p2;//定义两个节点记录指针开始位置
  399. p1=node;//指针p1每次向前移动一步
  400. p2=node;//指针p2每次向前移动两步
  401. Node last;
  402. while((last=p2.next)!=null&&last.next!=null){
  403. p1=p1.next;
  404. p2=last.next;
  405. //不断移动之后,找到两个地址一样的节点
  406. if(p1==p2){
  407. return true;
  408. }
  409. }
  410. return false;
  411. }
  412. }
  413. ```
  414. > [面试算法链表成环的检测](https://www.jianshu.com/p/6ff4f6cef1d0)
  415. >
  416. > [判断单链表是否成环算法](http://blog.csdn.net/fu908323236/article/details/78205462)
  417. * 链表翻转翻转一个单项链表
  418. ```
  419. pulibc void reverseSingleList(Node head){
  420. Node p1,p2=null;
  421. p1=head;//记录节点内存地址
  422. while(head.next!=null){
  423. p2=head.next;//记录节点持有的引用地址
  424. head.next=p2.next;//记录下一个节点持有的引用地址
  425. p2.next=p1;//改变节点的指针指向
  426. p1=p2;//记录下一个节点内存地址
  427. }
  428. return p2;
  429. }
  430. ```
  431. * 合并多个单有序链表假设都是递增的
  432. 思路依次比较两个有序单链表将较小的单链表的节点提取出来组成一个新链表最后将没有遍历结束的链表的引用赋值给新链表
  433. ```
  434. public static Node mergeSingleListOrdered(Node firstSingleListOrdered,Node secondSingleListOrdered){
  435. Node p1=firstSingleListOrdered;
  436. Node p2=secondeSingleListOrdered;
  437. if(p1==null){
  438. return p2;
  439. }else if(p2==null){
  440. return p1;
  441. }
  442. Node newHead=null;
  443. if(p1.value<p2.value){
  444. newHead=p1;//记录新链表开始位置
  445. newHead.next=mergeSingleListOrdered(p1.next,p2);
  446. }else{
  447. newHead=p2;//新链表开始位置
  448. newHead.next=mergeSingleListOrdered(p1,p2.next);
  449. }
  450. return newHead;
  451. }
  452. ```
  453. > [合并两个有序递增的链表使得合并后新链表还是有序的](http://blog.csdn.net/jcm666666/article/details/52280623)
  454. >
  455. > 参考资料[最全的BAT大厂面试题整理](https://www.jianshu.com/p/c70989bd5f29)》