线性表的Java实现--链式存储(双向链表)
有了单向链表的基础,双向链表的实现就容易多了。
双向链表的一般情况:
增加节点:
删除节点:
双向链表的Java实现:
package com.liuhao.algorithm; public class DuLinkList<T> { /** * 内部类:链表中的一个节点 * * @author liuhao data 节点中的数据 prev 指向前一个节点的引用 next 指向下一个节点的引用 */ private class Node { private T data;// 保存的数据元素 private Node prev;// 指向上一个节点 private Node next;// 指向下一个节点 public Node() { } public Node(T data, Node prev, Node next) { super(); this.data = data; this.prev = prev; this.next = next; } } private Node header;// 头结点 private Node tail;// 尾节点 private int size;// 链表中元素个数 // 创建空链表 public DuLinkList() { header = null; tail = null; } // 已指定数据元素创建链表,只有一个元素 public DuLinkList(T element) { header = new Node(element, null, null); // 只有一个节点,header,tail都指向该节点 tail = header; size++; } // 返回链表长度 public int length() { return size; } // 获取指定位置的数据元素 public T get(int index) { return this.getNodeByIndex(index).data; } // 获取指定位置的节点 private Node getNodeByIndex(int index) { if (index < 0 || index > size - 1) { throw new IndexOutOfBoundsException("索引超出线性表范围"); } if (index < size / 2) { Node current = header; for (int i = 0; i < size / 2 && current != null; i++, current = current.next) { if (i == index) { return current; } } } else { Node current = tail; for (int i = size - 1; i >= size / 2 && current != null; i--, current = current.prev) { if (i == index) { return current; } } } return null; } // 按值查询所在的位置 public int locate(T element) { Node current = header; for (int i = 0; i < size - 1 && current != null; i++, current = current.next) { if (element.equals(current.data)) { return i; } } return -1; } // 向指定位置插入元素 public void insert(T element, int index) { if (index < 0 || index > size) { throw new IndexOutOfBoundsException("索引超出线性表范围"); } if (header == null) { this.add(element); } else { if (0 == index) { this.addAtHead(element); } else { Node prev = this.getNodeByIndex(index - 1);// 获取插入节点的前一个节点 Node next = prev.next;// 待插索引处的节点 Node newNode = new Node(element, prev, next);// 新增节点,让它的prev指向之前的节点。next指向之后的节点 prev.next = newNode;// 之前的节点的next指向当前节点 next.prev = newNode;// 之后节点的prev指向当前节点 size++; } } } // 采用尾插法添加新节点 public void add(T element) { // 若还是空表,则将header和tail都指向该元素即可 if (header == null) { header = new Node(element, null, null); tail = header; } else { // 创建信节点,prev指向tail Node newNode = new Node(element, tail, null); // 令tail的next指向新节点 tail.next = newNode; tail = newNode;// 把新节点设为尾节点 } size++; } // 采用头插发添加新节点 public void addAtHead(T element) { Node newNode = new Node(element, null, header); header.prev = newNode; header = newNode; // 如果插入之前是空表 if (tail == null) { tail = header; } size++; } // 删除指定索引处的元素 public T delete(int index) { if (index < 0 || index > size - 1) { throw new IndexOutOfBoundsException("索引超出线性表范围"); } Node del = null; if (index == 0) { del = header; header = header.next; header.prev = null; } else { Node prev = this.getNodeByIndex(index - 1);// 获取索引处之前的节点 del = prev.next;// 获取索引处的节点 // 让之前的节点的next指向下一个节点 prev.next = del.next; // 有可能删除的是最后一个元素,若直接调用next.prev可能会出错 if (del.next != null) { del.next.prev = prev; } //若删除的是最后一个元素,那么就要重置tail; tail = prev; del.prev = null; del.next = null; } size--; return del.data; } // 删除最后一个元素 public T remove() { return this.delete(size - 1); } // 判断是否为空 public boolean isEmpty() { return size == 0; } // 清空线性表 public void clear() { header = null; tail = null; size = 0; } public String toString() { if (size == 0) { return "[]"; } else { StringBuilder sb = new StringBuilder("["); for (Node current = header; current != null; current = current.next) { sb.append(current.data.toString() + ", "); } sb.append("]"); int len = sb.length(); // 删除多余的“,”和空格 return sb.delete(len - 3, len - 2).toString(); } } }
测试代码:
package com.liuhao.test;
import org.junit.Test;
import com.liuhao.algorithm.DuLinkList;
public class DuLinkListTest {
@Test
public void test() {
//测试构造函数
DuLinkList<String> duList = new DuLinkList("好");
System.out.println(duList);
//测试添加元素
duList.add("ni");
duList.add("没");
System.out.println(duList);
//在头部添加
duList.addAtHead("五月");
System.out.println(duList);
//在指定位置添加
duList.insert("摩卡", 2);
System.out.println(duList);
//获取指定位置处的元素
System.out.println("第2个元素是(从0开始计数):" + duList.get(2));
//返回元素索引
System.out.println("摩卡在的位置是:" + duList.locate("摩卡"));
System.out.println("moka所在的位置:" + duList.locate("moka"));
//获取长度
System.out.println("当前线性表的长度:" + duList.length());
//判断是否为空
System.out.println(duList.isEmpty());
//删除最后一个元素
duList.remove();
System.out.println("调用remove()后:" + duList);
//获取长度
System.out.println("当前线性表的长度:" + duList.length());
//删除指定位置处元素
duList.delete(3);
System.out.println("删除第4个元素后:" + duList);
//获取长度
System.out.println("当前线性表的长度:" + duList.length());
//清空
duList.clear();
System.out.println(duList);
//判断是否为空
System.out.println(duList.isEmpty());
}
}
代码获取地址:https://github.com/liuhao123/JavaMore.git
相关推荐
在Java中实现线性表,通常会涉及到两种常见的存储方式:顺序存储和链式存储。 一、顺序存储结构 1. 数组实现:线性表的顺序存储是最直观的方式,即使用数组来存储元素。数组的特点是访问速度快,但插入和删除操作...
线性表的链式存储是相对于顺序存储的一种方式,尤其适用于动态变化的数据集合。 在链式存储中,线性表的数据元素并不必须存储在连续的内存位置,而是通过链接的方式将它们串联起来。具体来说,单链表是最基本的形式...
在链式存储的线性表中,主要有单链表和双向链表两种形式: 1. 单链表:每个节点只有一个指向下一个节点的指针,最后一个节点的指针为空,表示链表的结束。单链表的插入和删除操作相对简单,只需要修改相邻节点的...
线性表有两种主要的存储方式:顺序存储和链式存储。 2.1 顺序存储结构 - 顺序表: 顺序表是线性表的顺序存储结构,它使用一块连续的内存空间来存储表中的元素。这种结构的特点包括: - 容量固定:在创建时需要预先...
链式存储结构则使用链表实现,每个元素(节点)包含数据域和指针域,指针指向下一个元素。这种结构允许在不移动其他元素的情况下插入和删除元素,因此插入和删除操作相对高效,但访问元素(尤其是中间位置的元素)...
**双向链表**是链式线性表的一种变体,每个节点除了包含指向下一个节点的指针外,还有一个指向前一个节点的指针。这种结构使得在双向链表中进行前后移动更加方便,但也增加了额外的存储开销。 在实际应用中,线性表...
在实际编程中,栈和线性表的实现可以根据需求进行优化,比如使用动态数组(如C++的std::vector或Java的ArrayList)来平衡空间效率和动态扩展的需求,或者使用双向链表(如C++的std::list或Java的LinkedList)来提高...
顺序表是用一维数组来存储线性表,而链表则是通过链式结构来连接各个元素。 1. **顺序表**: - **定义**:顺序表是一种将线性表中的所有元素连续地存储在一块内存区域中的数据结构。它的优点是访问速度快,因为...
本章节介绍了线性表的相关概念,并通过顺序存储与链式存储的方式实现了线性表,同时比较了两种实现方式的优劣。 **3.1 线性表及抽象数据类型** - **线性表定义** - 线性表的定义与特点 - 线性表与数组的区别 - ...
数据结构课程实验报告1主要关注线性表的实现和二叉树的构建,涉及两种不同的存储结构:顺序存储和链式存储。以下是针对这两种结构及其相关操作的详细说明。 1. 顺序存储结构的线性表实现(1.1部分) 线性表是一种...
本文将深入探讨双向线性表的链式存储,包括双向链表和双向循环链表的原理、操作及其实现。 双向链表是一种线性数据结构,与单向链表不同,它在每个节点中不仅保存了指向下一个节点的指针,还保存了指向前一个节点的...
在实际应用中,线性表的存储结构通常有两种主要形式:顺序存储结构和链式存储结构。 顺序存储结构是将线性表中的元素按其逻辑顺序依次存放在一片连续的内存区域中,这种存储方式使得数据访问变得简单且高效。顺序表...
- **链表(Linked Linear List)**:使用链式存储结构,每个元素(节点)包含数据域和指针域,指针指向下一个元素。插入和删除操作相对高效,但访问速度较慢,因为需要遍历链表。 3. **线性表插入操作**: - **...