Java 实现单向链表
2015-09-27 17:04
411 查看
Java 类库中其实是提供了链表的实现类的,但是如果自己来实现会不会很有成就感呢?
我们知道,Java 官方是没有指针的概念的,当然我们可以把对象的引用理解为指针,虽然与 C 或 C++ 中的指针概念不尽相同。想要自己实现链表,最重要的一步就是怎么表示一个链表中的结点。在 Java中,我们可以定义一个专门表示结点的类,最好是内部类,确保类的封装性与完整性。此结点类可定义如下:
确定了结点的表示方法,余下的关键问题就是链表的表示方法。我们可以定义一个头结点,初始化为null。然后对于链表增删改查操作都要在此基础上进行。
链表源代码如下:
首先,每一次在末尾添加结点都会遍历整个链表。其次,对于链表长度的获取也需要遍历整个链表。这两个操作都会多耗费很多时间。所以更好的解决办法是定义一个变量记录链表长度,每次添加或删除结点的时候就将此变量加 1 或者减 1。除此之外,再定义一个指向末尾结点的引用,每次添加或删除结点时对应将此引用修改,这样在末尾添加结点时就可以节省遍历链表的时间。
我们知道,Java 官方是没有指针的概念的,当然我们可以把对象的引用理解为指针,虽然与 C 或 C++ 中的指针概念不尽相同。想要自己实现链表,最重要的一步就是怎么表示一个链表中的结点。在 Java中,我们可以定义一个专门表示结点的类,最好是内部类,确保类的封装性与完整性。此结点类可定义如下:
class Node { public Node(String name, int age) { this.name = name; this.age = age; this.next = null; } private String name; private int age; private Node next; }此处结点的数据类型可以固定写在该类中,毕竟结点中存放的数据一般都是相同类型的。
确定了结点的表示方法,余下的关键问题就是链表的表示方法。我们可以定义一个头结点,初始化为null。然后对于链表增删改查操作都要在此基础上进行。
链表源代码如下:
/** * 实现自己的链表类 * * @author Wll * */ public class LinkedListDemo { public static void main(String[] args) { LinkedListDemo list = new LinkedListDemo(); Node tom = new Node("Tom", 20); Node jack = new Node("Jack", 21); Node michael = new Node("Michael", 18); Node lisa = new Node("Lisa", 20); Node kitty = new Node("Kitty", 19); list.add(tom); list.add(jack); list.add(michael); list.add(lisa); list.add(kitty); System.out.println("+++++链表长度+++++"); System.out.println(list.size()); System.out.println("\n+++++遍历链表结果+++++"); list.traverse(); System.out.println("\n+++++删除第 5个结点+++++"); try { System.out.println(list.delete(4)); list.traverse(); } catch (MyIndexOutOfBoundsException e) { System.out.println(e.getMessage()); } System.out.println("\n+++++修改第 3 个结点+++++"); try { System.out.println(list.modify(2, "Modified", 100)); list.traverse(); } catch (MyIndexOutOfBoundsException e) { System.out.println(e.getMessage()); } System.out.println("\n+++++查找第 6 个结点+++++"); try { System.out.println(list.indexOf(5)); } catch (MyIndexOutOfBoundsException e) { System.out.println(e.getMessage()); } System.out.println("\n+++++插入第 3 个结点+++++"); Node lucy = new Node("Lucy", 22); try { System.out.println(list.insertAt(2, lucy)); list.traverse(); } catch (MyIndexOutOfBoundsException e) { System.out.println(e.getMessage()); } System.out.println("\n+++++重置(清空)链表+++++"); System.out.println(list.reset()); list.traverse(); } /** * 在最后一个结点后面添加一个新结点 * * @param node * 待添加的新结点 * @return 若插入成功返回 true,否则 false */ public boolean add(Node newNode) { Node p = head; if (head == null) { // 添加到头部 head = newNode; return true; } else { // 添加到末尾结点后面 // 这里是 p.next,因为如果是 p, 则 while 循环结束后 p 必定为 null,p.next 就会空指针异常 while (p.next != null) { p = p.next; } p.next = newNode; return true; } } /** * 在任意位置插入一个新结点 * * @param index * 插入的位置 * @param newNode * 插入的结点 * @return 若插入成功返回 true,否则 false * @throws IndexOutOfBoundsException */ public boolean insertAt(int index, Node newNode) throws MyIndexOutOfBoundsException { Node p = head; int count = 1; if (index < 0 || index > this.size()) { throw new MyIndexOutOfBoundsException("插入异常,请检查下标范围!"); } if (index == 0) { newNode.next = head; head = newNode; return true; } else { while (p != null) { if (count++ == index) { newNode.next = p.next; p.next = newNode; return true; } p = p.next; } return false; } } /** * 删除一个已有结点 * * @param index * 该节点的索引,以 0 开始。第 2 个结点索引就是 1 * @return 若删除成功返回 true,否则返回 false * @throws IndexOutOfBoundsException */ public boolean delete(int index) throws MyIndexOutOfBoundsException { int count = 1; Node p = head; if (index < 0 || index > this.size() - 1) { throw new MyIndexOutOfBoundsException("删除异常,请检查下标范围!"); } if (index == 0) { head = p.next; return true; } else { while (p != null) { if (count++ == index) { p.next = p.next.next; return true; } p = p.next; } return false; } } /** * 修改结点内容 * * @param index * 结点所在索引 * @param name * 结点 name 属性 * @param age * 结点 age 属性 * @return 若修改成功返回 true,否则返回 false * @throws IndexOutOfBoundsException */ public boolean modify(int index, String name, int age) throws MyIndexOutOfBoundsException { Node p = head; int count = 0; if (index < 0 || index > this.size() - 1) { throw new MyIndexOutOfBoundsException("修改异常,请检查下标范围!"); } while (p != null) { if (count++ == index) { p.name = name; p.age = age; return true; } p = p.next; } return false; } /** * 查找指定索引处的结点 * * @param index * 结点索引 * @return 索引处结点 * @throws IndexOutOfBoundsException */ public Node indexOf(int index) throws MyIndexOutOfBoundsException { Node p = head; int count = 0; if (index < 0 || index > this.size() - 1) { throw new MyIndexOutOfBoundsException("查询异常,请检查下标范围!"); } while (p != null) { if (count++ == index) { return p; } p = p.next; } return null; } /** * 获得链表的大小 * * @return 链表长度 */ public int size() { int count = 0; Node p = head; while (p != null) { count++; p = p.next; } return count; } /** * 遍历链表 */ public void traverse() { Node p = head; int count = 0; while (p != null) { System.out.println(p); count++; p = p.next; } if (count == 0) { System.out.println("链表为空!"); } } /** * 重置链表 */ public boolean reset() { head = null; return true; } private Node head = null; /** * 内部结点类 * * @author Wll * */ private static class Node { public Node(String name, int age) { this.name = name; this.age = age; this.next = null; } @Override public String toString() { return this.getClass().getName() + "[Name=" + this.name + ", Age=" + this.age + "]"; } private String name; private int age; private Node next; } } class MyIndexOutOfBoundsException extends Exception { private static final long serialVersionUID = 1L; public MyIndexOutOfBoundsException(String msg) { this.msg = msg; } @Override public String getMessage() { return this.msg; } private String msg; }虽然上边的代码可以实现链表的常用操作,但是可以想象当链表中的结点数太大时性能会有所下降。下面是对于提升上面代码执行效率的一点思考。
首先,每一次在末尾添加结点都会遍历整个链表。其次,对于链表长度的获取也需要遍历整个链表。这两个操作都会多耗费很多时间。所以更好的解决办法是定义一个变量记录链表长度,每次添加或删除结点的时候就将此变量加 1 或者减 1。除此之外,再定义一个指向末尾结点的引用,每次添加或删除结点时对应将此引用修改,这样在末尾添加结点时就可以节省遍历链表的时间。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- [C/C++]反转链表
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树