Java中的链表数据结构
首先,我们来定义一个链表的数据结构,如下:
1 public class Link {
2 private int value;
3 private Link next;
4 public void set_Value(int m_Value) {
5 this.value = m_Value;
6 }
7 public int get_Value() {
8 return value;
9 }
10 public void set_Next(Link m_Next) {
11 this.next = m_Next;
12 }
13 public Link get_Next() {
14 return next;
15 }
16 }
有了这个数据结构后,我们需要一个方法来生成和输出链表,其中链表中每个元素的值采用的是随机数。
生成链表的代码如下:
1 public static Link init(int count, int maxValue)
2 {
3 Link list = new Link();
4 Link temp = list;
5 Random r = new Random();
6 temp.set_Value(Integer.MIN_VALUE);
7 for (int i = 0; i < count; i++)
8 {
9 Link node = new Link();
10 node.set_Value(r.nextInt(maxValue));
11 temp.set_Next(node);
12 temp=node;
13 }
14 temp.set_Next(null);
15 return list;
16 }
17
18 public static Link init(int count)
19 {
20 return init(count, Integer.MAX_VALUE);
21 }
对于链表的头结点,我们是不存储任何信息的,因此将其值设置为Integer.MIN_VALUE。我们重载了生成链表的方法。
下面是打印链表信息的方法:
1 public static void printList(Link list)
2 {
3 if (list == null || list.get_Next() == null)
4 {
5 System.out.println("The list is null or empty.");
6 return;
7 }
8 Link temp = list.get_Next();
9 StringBuffer sb = new StringBuffer();
10 while(temp != null)
11 {
12 sb.append(temp.get_Value() + "->");
13 temp=temp.get_Next();
14 }
15 System.out.println(sb.substring(0, sb.length() - 2));
16 }
好了,有了以上这些基础的方法, 我们就可以深入探讨链表相关的面试题了。
- 链表反转 思路:有两种方法可以实现链表反转,第一种是直接循环每个元素,修改它的Next属性;另一种是采取递归的方式。 首先来看直接循环的方式:
1 public static Link Reverve(Link list) 2 { 3 if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) 4 { 5 System.out.println("list is null or just contains 1 element, so do not need to reverve."); 6 return list; 7 } 8 Link current = list.get_Next(); 9 Link next = current.get_Next(); 10 current.set_Next(null); 11 while(next != null) 12 { 13 Link temp = next.get_Next(); 14 next.set_Next(current); 15 current = next; 16 next = temp; 17 } 18 list.set_Next(current); 19 20 return list; 21 }然后是递归方式:
1 public static Link RecursiveReverse(Link list) 2 { 3 if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) 4 { 5 System.out.println("list is null or just contains 1 element, so do not need to reverve."); 6 return list; 7 } 8 9 list.set_Next(Recursive(list.get_Next())); 10 11 return list; 12 } 13 14 15 private static Link Recursive(Link list) 16 { 17 if (list.get_Next() == null) 18 { 19 return list; 20 } 21 Link temp = Recursive(list.get_Next()); 22 list.get_Next().set_Next(list); 23 list.set_Next(null); 24 25 return temp; 26 - 输出指定位置的元素(倒数第N个元素) 思路:采用两个游标来遍历链表,第1个游标先走N步,然后两个游标同时前进,当第一个游标到最后时,第二个游标就是想要的元素。
1 public static Link find(Link list, int rPos) 2 { 3 if (list == null || list.get_Next() == null) 4 { 5 return null; 6 } 7 int i = 1; 8 Link first = list.get_Next(); 9 Link second = list.get_Next(); 10 while(true) 11 { 12 if (i==rPos || first == null) break; 13 first = first.get_Next(); 14 i++; 15 } 16 if (first == null) 17 { 18 System.out.println("The length of list is less than " + rPos + "."); 19 return null; 20 } 21 while(first.get_Next() != null) 22 { 23 first = first.get_Next(); 24 second = second.get_Next(); 25 } 26 27 return second; 28 } - 删除指定节点 思路:可以分情况讨论,如果指定节点不是尾节点,那么可以采用取巧的方式,将指定节点的值修改为下一个节点的值,将指定节点的Next属性设置为Next.Next;但如果指定节点为尾节点,那么只能是从头开始遍历。
1 public static void delete(Link list, Link element) 2 { 3 if (element.get_Next() != null) 4 { 5 element.set_Value(element.get_Next().get_Value()); 6 element.set_Next(element.get_Next().get_Next()); 7 } 8 else 9 { 10 Link current = list.get_Next(); 11 while(current.get_Next() != element) 12 { 13 current = current.get_Next(); 14 } 15 current.set_Next(null); 16 } 17 } - 删除重复节点 思路:采用hashtable来存取链表中的元素,遍历链表,当指定节点的元素在hashtable中已经存在,那么删除该节点。
1 public static void removeDuplicate(Link list) 2 { 3 if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) return; 4 Hashtable table = new Hashtable(); 5 Link cur = list.get_Next(); 6 Link next = cur.get_Next(); 7 table.put(cur.get_Value(), 1); 8 while(next != null) 9 { 10 if (table.containsKey(next.get_Value())) 11 { 12 cur.set_Next(next.get_Next()); 13 next = next.get_Next(); 14 } 15 else 16 { 17 table.put(next.get_Value(), 1); 18 cur= next; 19 next = next.get_Next(); 20 } 21 22 } 23 } - 寻找链表中间节点 思路:采用两个游标的方式,第一个游标每次前进两步,第二个游标每次前进一步,当第一个游标到最后时,第二个游标就是中间位置。需要注意的是,如果链表元素的个数是偶数,那么中间元素应该是两个。
1 public static void findMiddleElement(Link list) 2 { 3 if (list == null || list.get_Next() == null) return; 4 System.out.println("The Middle element is:"); 5 if (list.get_Next().get_Next() == null) 6 { 7 System.out.println(list.get_Next().get_Value()); 8 } 9 Link fast = list.get_Next(); 10 Link slow = list.get_Next(); 11 while(fast.get_Next() != null && fast.get_Next().get_Next() != null) 12 { 13 fast = fast.get_Next().get_Next(); 14 slow = slow.get_Next(); 15 } 16 17 if (fast != null && fast.get_Next() == null) 18 { 19 System.out.println(slow.get_Value()); 20 } 21 else 22 { 23 System.out.println(slow.get_Value()); 24 System.out.println(slow.get_Next().get_Value()); 25 } 26 } - 链表元素排序 思路:链表元素排序,有两种方式,一种是链表元素本身的排序,一种是链表元素值得排序。第二种方式更简单、灵活一些。
1 public static void Sort(Link list) 2 { 3 if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) 4 { 5 return; 6 } 7 Link current = list.get_Next(); 8 Link next = current.get_Next(); 9 while(current.get_Next() != null) 10 { 11 while(next != null) 12 { 13 if (current.get_Value() > next.get_Value()) 14 { 15 int temp = current.get_Value(); 16 current.set_Value(next.get_Value()); 17 next.set_Value(temp); 18 } 19 next = next.get_Next(); 20 } 21 current = current.get_Next(); 22 next = current.get_Next(); 23 } 24 } - 判断链表是否有环,如果有,找出环上的第一个节点 思路:可以采用两个游标的方式判断链表是否有环,一个游标跑得快,一个游标跑得慢。当跑得快的游标追上跑得慢的游标时,说明有环;当跑得快的游标跑到尾节点时,说明无环。 至于如何找出换上第一个节点,可以分两步,首先确定环上的某个节点,计算头结点到该节点的距离以及该节点在环上循环一次的距离,然后建立两个游标,分别指向头结点和环上的节点,并将距离平摊(哪个距离大,先移动哪个游标,直至两个距离相等),最后同时移动两个游标,碰到的第一个相同元素,就是环中的第一个节点。
1 public static Link getLoopStartNode(Link list) 2 { 3 if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) 4 { 5 return null; 6 } 7 int m = 1, n = 1; 8 Link fast = list.get_Next(); 9 Link slow = list.get_Next(); 10 while(fast != null && fast.get_Next() != null) 11 { 12 fast = fast.get_Next().get_Next(); 13 slow = slow.get_Next(); 14 if (fast == slow) break; 15 m++; 16 } 17 if (fast != slow) 18 { 19 return null; 20 } 21 Link temp = fast; 22 while(temp.get_Next() != fast) 23 { 24 temp = temp.get_Next(); 25 n++; 26 } 27 Link node1 = list.get_Next(); 28 Link node2 = fast; 29 if (m < n) 30 { 31 for (int i = 0; i < n - m; i++) 32 { 33 node2 = node2.get_Next(); 34 } 35 } 36 if (m > n) 37 { 38 for (int i = 0; i < m - n; i++) 39 { 40 node1 = node1.get_Next(); 41 } 42 } 43 while(true) 44 { 45 if (node1 == node2) 46 { 47 break; 48 } 49 node1 = node1.get_Next(); 50 node2 = node2.get_Next(); 51 } 52 53 return node1; 54 55 } - 判断两个链表是否相交 思路:判断两个链表的尾节点是否相同,如果相同,一定相交
1 public static boolean isJoint(Link list1, Link list2) 2 { 3 if (list1 == null || list2 == null || list1.get_Next() == null || list2.get_Next() == null) 4 { 5 return false; 6 } 7 Link node1 = list1; 8 Link node2 = list2; 9 while(node1.get_Next() != null) 10 { 11 node1 = node1.get_Next(); 12 } 13 while(node2.get_Next() != null) 14 { 15 node2 = node2.get_Next(); 16 } 17 18 return node1 == node2; 19 } - 合并两个有序链表 思路:新建一个链表,然后同时遍历两个有序链表,比较其大小,将元素较小的链表向前移动,直至某一个链表元素为空。然后将非空链表上的所有元素追加到新建链表中。
1 public static Link merge(Link list1, Link list2) 2 { 3 Link list = new Link(); 4 list.set_Value(Integer.MIN_VALUE); 5 Link current1 = list1.get_Next(); 6 Link current2 = list2.get_Next(); 7 Link current = list; 8 while(current1 != null && current2 != null) 9 { 10 Link temp = new Link(); 11 if (current1.get_Value() > current2.get_Value()) 12 { 13 temp.set_Value(current2.get_Value()); 14 current2 = current2.get_Next(); 15 } 16 else 17 { 18 temp.set_Value(current1.get_Value()); 19 current1 = current1.get_Next(); 20 } 21 current.set_Next(temp); 22 current = temp; 23 } 24 if (current1 != null) 25 { 26 while(current1 != null) 27 { 28 Link temp = new Link(); 29 temp.set_Value(current1.get_Value()); 30 current.set_Next(temp); 31 current = temp; 32 current1 = current1.get_Next(); 33 } 34 } 35 36 if (current2 != null) 37 { 38 while(current2 != null) 39 { 40 Link temp = new Link(); 41 temp.set_Value(current2.get_Value()); 42 current.set_Next(temp); 43 current = temp; 44 current2 = current2.get_Next(); 45 } 46 } 47 48 current.set_Next(null); 49 50 return list; 51 } - 交换链表中任意两个元素(非头结点) 思路:首先需要保存两个元素的pre节点和next节点,然后分别对pre节点和next节点的Next属性重新赋值。需要注意的是,当两个元素师相邻元素时,需要特殊处理,否则会将链表陷入死循环。
1 public static void swap(Link list, Link element1, Link element2) 2 { 3 if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null || 4 element1 == null || element2 == null || element1 == element2) 5 return; 6 7 Link pre1 = null, pre2 = null, next1 = null, next2 = null; 8 Link cur1=element1, cur2=element2; 9 Link temp = list.get_Next(); 10 boolean bFound1 = false; 11 boolean bFound2 = false; 12 while(temp != null) 13 { 14 if(temp.get_Next() == cur1) 15 { 16 pre1=temp; 17 next1 = temp.get_Next().get_Next(); 18 bFound1 = true; 19 } 20 if (temp.get_Next() == cur2) 21 { 22 pre2 = temp; 23 next2 = temp.get_Next().get_Next(); 24 bFound2=true; 25 } 26 if (bFound1 && bFound2) break; 27 temp = temp.get_Next(); 28 } 29 30 if (cur1.get_Next() == cur2) 31 { 32 temp = cur2.get_Next(); 33 pre1.set_Next(cur2); 34 cur2.set_Next(cur1); 35 cur1.set_Next(temp); 36 } 37 else if (cur2.get_Next() == cur1) 38 { 39 temp = cur1.get_Next(); 40 pre2.set_Next(cur1); 41 cur1.set_Next(cur2); 42 cur2.set_Next(temp); 43 } 44 else 45 { 46 pre1.set_Next(cur2); 47 cur1.set_Next(next2); 48 pre2.set_Next(cur1); 49 cur2.set_Next(next1); 50 } 51 }这里,还有另外一种取巧的方法,就是直接交换两个元素的值,而不需要修改引用。
Java中的链表数据结构的更多相关文章
- Java基础-JAVA中常见的数据结构介绍
Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...
- 【Java】Java中的Collections类——Java中升级版的数据结构【转】
一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数 ...
- java中的各个数据结构区别
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢 ...
- Java中HashMap的数据结构
类声明: 概述: 线程不安全: <Key, Value>两者都可以为null: 不保证映射的顺序,特别是它不保证该顺序恒久不变: HashMap使用Iterator: HashMap中ha ...
- Java中常见的数据结构的区别
把多个数据按照一定的存储方式,存储起来,称存储方式之为数据结构. 数据的存储方式有很多,数组,队列,链表,栈,哈希表等等. 不同的数据结构,性能是不一样的,比如有的插入比较快,查询比较快,但是删除比较 ...
- Java 中常见的数据结构
1.数据结构有什么作用? 当使用 Java 里面的容器类时,你有没有想过,怎么 ArrayList 就像一个无限扩充的数组,也好像链表之类的.很好使用,这就是数据结构的用处,只不过你在不知不觉中使用了 ...
- java中常用的数据结构--Collection接口及其子类
java中有几种常用的数据结构,主要分为Collection和map两个主要接口(接口只提供方法,并不提供实现),而程序中最终使用的数据结构是继承自这些接口的数据结构类. 一.集合和数组的区别 二.C ...
- Java中常用的数据结构类
结构体系图 List ArrayList.LinkedList.Vector有什么区别? ArrayList 只能装入引用对象(基本类型要转换为封装类): 线程不安全: 底层由数组实现(顺序表),因为 ...
- java中实现链表(转)
分析: 上述节点具备如下特征: 1. 每个节点由两部分组成(存储信息的字段,存储指向下一个节点的指针) 2. 节点之间有着严格的先后顺序. 3. 单链表节点是一种非线性的结构,在内存中不连续分配空间. ...
随机推荐
- 【Bootstrap】3.优化站点资源、完成响应式图片、让传送带支持手势
A.优化站点资源 速度很重要.用户很关心.我们的站点必须加载够快,否则用户就会走人.SEO 也很重要.我们的站点必须加载够快,否者搜索排名就会下降. 明白了这样,我们就来清点一下 [Bootstrap ...
- Oracle中用随机数更新字段----将一张表的数据插入另一张表----环境设置
DECLARE CURSOR recordCursor IS SELECT longitude,latitude FROM WR_WIUST_B_SEC FOR UPDATE; recordRow r ...
- atitit.userService 用户系统设计 v4 q316 .doc
atitit.userService 用户系统设计 v4 q316 .doc 1. 新特性1 2. Admin login1 3. 用户注册登录2 3.1. <!-- 会员注册使用 --> ...
- 【代码笔记】iOS-利用图片序列创建动态图片效果
一,效果图. 二,代码. RootViewController.m - (void)viewDidLoad { [super viewDidLoad]; // Do any additional se ...
- 【读书笔记】iOS-使用Web Service-基于客户端服务器结构的网络通信(一)
Web Service技术是一种通过Web协议提供服务,保证不同平台的应用服务可以互操作,为客户端程序提供不同的服务. 目前3种主流的Web Service实现方案用:REST,SOAP和XML-RP ...
- AFNetworking菊花转圈圈
注意,此圈圈是在左上角,特别小,不注意是看不到的 加载这个东西,要先引入头文件: AFNetworkActivityIndicatorManager.h 然后只要一句代码就可以实现,默认情况下AFN的 ...
- 开启Apache mod_rewrite模块完全解答
启用mod_rewrite模块 在conf目录的httpd.conf文件中找到 LoadModule rewrite_module modules/mod_rewrite.so 将这一行前面的#去掉. ...
- Spark:利用Eclipse构建Spark集成开发环境
前一篇文章“Apache Spark学习:将Spark部署到Hadoop 2.2.0上”介绍了如何使用Maven编译生成可直接运行在Hadoop 2.2.0上的Spark jar包,而本文则在此基础上 ...
- Java中用内存映射处理大文件
在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如 ...
- Getting Started with ASP.NET Web API 2 (C#)
By Mike Wasson|last updated May 28, 2015 7556 of 8454 people found this helpful Print Download Com ...