简谈JAVA基础--双向链表(LinkedList)
来源:互联网 发布:数学手册 知乎 编辑:程序博客网 时间:2024/06/05 06:52
双向链表其实与单链表类似,只是存储结构略有不同。单链表采用指针指向下一个节点的方式,而双向链表多了一个指针指向前一个节点。
单链表的原理与实现参考:简谈JAVA基础--单链表
双链表与单链表相比多了一个指针变量来指向下一个节点的地址。
每个节点包含三部分: prev前一个节点的指针变量,data节点数据, next 后一个节点的指针变量
在单链表中,由于只有next,所以每个节点的数据地址由上一个节点所知,而对链表的所有操作,都需要通过一个头节点来开始进行。
而双向链表中,由于有了prev的存在,所以可以通过尾节点和头节点来开始进行操作。知道了某个节点,就得到了前后的节点所在位置。
在java中,LinkedList底层就是通过一个双向链表来实现的。有时间可以看一下源码,写的很精妙。
下面是根据LinkedList自己实现的双链表。
下面的图片来源:http://blog.csdn.net/wangzhen209/article/details/8180462
结点结构:
链表的存储结构:
插入操作
删除操作
实现双向链表源码( 可以看一下JDK中的LinkedList ):
package Chapter1;import java.lang.reflect.Array;import java.util.Collection;/** * 实现双向链表。 * Created by yukaiji on 2017/9/13. */public class MyLinkedList<T> implements java.io.Serializable { private static final long serialVersionUID = -8247167820457785557L; /** List长度 **/ private int size; /** 操作次数 **/ private int count; /** 头节点 **/ private Node<T> head; /** 尾节点 **/ private Node<T> last; /** * 节点类 */ private class Node<T> { private T data; private Node<T> prev; private Node<T> next; public Node(Node<T> prev, T data, Node<T> next) { this.data = data; this.prev = prev; this.next = next; } } /** * 构造方法 */ public MyLinkedList() { } /** * 添加对象到表头 */ public void addFirst(T data) { Node<T> headNode = head; // 创建一个新的节点 Node<T> newNode = new Node<>(null, data, headNode); // 将头节点指向新的节点 head = newNode; // 如果原先的头节点是null 代表新添加的该链表的第一个节点。 // 将尾节点指向新节点,因为该链表只存在一个节点,头尾相同 if (headNode == null) { last = newNode; } else { // 如果不是第一个节点。则将前一个节点的perv指向新添加的节点 headNode.prev = newNode; } size++; count++; } /** * 添加对象到表尾 */ public void addLast(T data) { Node<T> lastNode = last; // 创建一个新的节点 Node<T> newNode = new Node<T>(lastNode, data, null); // 将尾节点指向新的节点 last = newNode; // 如果原先的尾节点为空,代表该节点为该链表的第一个节点 // 头节点也为新节点,首尾相同 if (lastNode == null) { head = newNode; } else { // 如果不是第一个节点。则将前一个节点的next指向新添加的节点 lastNode.next = newNode; } size++; count++; } /** * 从链表头部删除一个节点 */ public void removeFirst() { // 创建头节点的下一个节点对象 Node<T> headNext = head.next; // 因为要将该头结点删除,所以把数据和下个节点的指针置为空 head.data = null; head.next = null; // 头节点指向该节点的下一个 (也就是删除头结点) head = headNext; // 如果头结点的下一个为空,说明要删除的节点是链表中唯一的节点。 // 将尾节点置为null if (headNext == null) { last = null; } else { // 否则将删除后的头节点的prev置为null (头节点perv永远为null) head.prev = null; } size--; count++; } /** * 从链表尾部删除一个节点(内容与removeFirst相反,参考removeFirst) */ public void removeLast() { Node<T> lastPrev = last.prev; last.data = null; last.prev = null; last = lastPrev; if (lastPrev == null) { head = null; } else { last.next = null; } size--; count++; } /** * 打印链表 */ public void printList() { for (Node node = head; node != null; node = node.next) { System.out.print(node.data + " "); } System.out.println(); } /** * 链表长度 */ public int size() { return size; } /** * 链表是否为空 */ public boolean isEmpty() { return head == null && last == null; } /** * 链表是否包含对象o */ public boolean contains(Object o) { return indexOf(o) != -1; } /** * 链表转换为数组 */ public Object[] toArray() { Object[] array = new Object[size]; int i = 0; for (Node node = head; node != null; node = node.next) { array[i++] = node.data; } return array; } /** * 链表转换为指定类型数组 */ public <E> E[] toArray(E[] arr) { // 如果传递的数组长度小于链表的长度,创建一个指定类型、链表长度的新数组 if (arr.length < size) { arr = (E[]) Array.newInstance(arr.getClass().getComponentType(), size); } Object[] array = arr; int i = 0; for (Node node = head; node != null; node = node.next) { array[i++] = node.data; } // 将最后一位置为null代表数组结束 if (arr.length > size) { array[size] = null; } return arr; } /** * 获得传递对象所在的索引位置 */ public int indexOf(Object o) { int index = 0; if (o == null) { for (Node node = head; node != null; node = node.next) { if (node.data == null) { return index; } index++; } } else { for (Node node = head; node != null; node = node.next) { if (o.equals(node.data)) { return index; } index++; } } return -1; } /** * 添加操作,默认采用表尾插入 */ public void add(T t) { addLast(t); } /** * 删除指定节点。 * 这里有四种情况: * 1.节点在头部 * 2.节点在尾部 * 3.节点在中间 * 4.无节点 */ public T removeNode(Node<T> node) { T data = node.data; Node<T> nodeNext = node.next; Node<T> nodePrev = node.prev; if (nodePrev == null) { head = nodeNext; } else { nodePrev.next = nodeNext; } if (nodeNext == null) { last = nodePrev; } else { nodeNext.prev = nodePrev; } size--; count++; return data; } /** * 根据传递对象删除第一个遇到的元素。 */ public boolean remove(Object o) { if (o == null) { for (Node<T> node = head; node != null; node = node.next) { if (node.data == null) { removeNode(node); return true; } } } else { for (Node<T> node = head; node != null; node = node.next) { if (o.equals(node.data)) { removeNode(node); return true; } } } return false; } /** * 删除c列表中所有在链表中对应的第一个相同元素。 */ public boolean removeAll(Collection<?> c) { Object[] objs = c.toArray(); for (Object o : objs) { T t = (T) o; remove(t); } return true; } /** * 删除index位置的节点 */ public T remove(int index) { return removeNode(node(index)); } /** * 清空链表 */ public void clear() { for (Node<T> node = head; node != null; ) { Node<T> nodeNext = node.next; node.data = null; node.next = null; node.prev = null; node = nodeNext; } head = last = null; size = 0; count++; } /** * 将c插入到链表尾部 */ public boolean addAll(Collection<? extends T> c) { // 直接调用在链表尾部插入c return addAll(size, c); } /** * 将c插入到链表的index节点前 */ public boolean addAll(int index, Collection<? extends T> c) { indexIsException(index); Object[] array = c.toArray(); int num = array.length; if (num == 0) { return false; } Node<T> pred, succ; // 这里判断是否从尾部插入 if (index == size) { succ = null; pred = last; } else { // 获得index位置的节点 succ = node(index); // 记录该节点的前一个节点。 pred = succ.prev; } for (Object o : array) { // 类型转换 T t = (T) o; // 创建新的节点,前节点指针指向index的前节点。 Node<T> node = new Node<>(pred, t, null); // 如果index前节点为null ,代表index 为头节点 if (pred == null) { // 这样将头结点指向新插入的节点 head = node; } else { // 将index前节点的后节点指针指向新的节点(因为插入的新节点在指定index的前面) pred.next = node; } pred = node; } if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += num; count++; return true; } /** * 获取index节点数据 */ public T get(int index) { return node(index).data; } public boolean retainAll(Collection<?> c) { return false; } /** * 在索引index处添加一个对象 */ public void add(int index, T element) { indexIsException(index); if (index == size) { addLast(element); } else { Node<T> succ = node(index); Node<T> prev = succ.prev; Node<T> node = new Node<>(prev, element, succ); succ.prev = node; if (prev == null) { head = node; } else { prev.next = node; } } size++; count++; } /** * 判断索引是否小于0 */ public Boolean indexIsException(int index) { if (index < 0) { System.out.println("索引小于0"); return false; } return true; } /** * 返回指定对象最后出现的位置 */ public int lastIndexOf(Object o) { int count = size; if (o == null) { for (Node<T> node = last; node != null; node = node.prev) { count--; if (node.data == null) { return count; } } } else { for (Node<T> node = last; node != null; node = node.prev) { count--; if (o.equals(node.data)) { return count; } } } return -1; } /** * 获取index位置的节点 */ Node<T> node(int index) { // 这里为什么判断传过来的index 是否小于链表长度的一半呢? // 因为这里涉及到效率问题,如果小于一半,从前向后遍历,如果大于一般从后向前遍历。 if (index < (size >> 1)) { Node<T> x = head; // 从前向后遍历,x每次为当前节点的下一个,直到找到index节点 for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<T> x = last; // 从后向前遍历,x每次为当前节点的前一个,直到找到index节点 for (int i = size - 1; i > index; i--) x = x.prev; return x; } }}
测试函
public static void main(String[] args) { MyLinkedList<String> myLinkedList = new MyLinkedList<String>(); myLinkedList.add("a"); myLinkedList.add("b"); myLinkedList.add("c"); myLinkedList.add("d"); myLinkedList.add("e"); myLinkedList.printList(); myLinkedList.remove("c"); myLinkedList.printList(); myLinkedList.addFirst("1"); myLinkedList.printList(); myLinkedList.addLast("2"); myLinkedList.printList(); List<String> addList = new ArrayList<String>(); addList.add("A"); addList.add("B"); myLinkedList.addAll(addList); myLinkedList.printList(); System.out.println(myLinkedList.get(3)); System.out.println(myLinkedList.contains("d")); System.out.println(myLinkedList.indexOf("e")); myLinkedList.removeFirst(); myLinkedList.printList(); myLinkedList.removeLast(); myLinkedList.printList(); myLinkedList.clear(); myLinkedList.printList(); }
、
阅读全文
0 0
- 简谈JAVA基础--双向链表(LinkedList)
- 双向链表(LinkedList) java实现
- Java LinkedList双向链表源码分析
- 双向链表(LinkedList)
- LinkedList 双向链表
- [java数据结构]--java双向链表LinkedList的简单实现
- linkedlist双向链表结构
- Java-----Collection 实现的LinkedList(双向链表)
- 双向链表在java中的应用举例-LinkedList
- 自己实现Java中基于双向链表的LinkedList
- 简单实现 linkedList 双向链表
- 利用双向链表实现LinkedList
- LinkedList,双向链表的实现
- [数据结构]双向链表实现LinkedList
- LinkedList之双向链表结构
- 简单实现LinkedList(双向链表)
- LinkedList底层原理(双向链表)
- LinkedList : 双向链表与实现
- 创建要素类,关系类,ITopologicalOperator接口
- PHP 配置虚拟域名访问
- Cannot create PoolableConnectionFactory (Communications link failure
- Jmeter 接口测试中使用Beanshell断言: 将接口响应报文与数据库结果对比
- 2017 ACM/ICPC Asia Regional Shenyang transaction transaction transaction
- 简谈JAVA基础--双向链表(LinkedList)
- 算法系列之--Kotlin的算法实战比较(原)
- android studio开发:判断网络是否连接
- zabbix配置ldap认证
- 鸟哥私房菜第十三章习题答案
- USB之(四)HID设备类协议
- Bagging与随机森林算法原理小结
- word第一篇csdn博客文章
- NVIDIA驱动安装