线性表的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());
}
}
线性表的Java实现--链式存储(双向链表)的更多相关文章
- 线性表的Java实现--链式存储(单向链表)
单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素.由于不需要按顺序存储,链表在 ...
- 数据结构导论 四 线性表的顺序存储VS链式存储
前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char nam ...
- 算法与数据结构(一) 线性表的顺序存储与链式存储(Swift版)
温故而知新,在接下来的几篇博客中,将会系统的对数据结构的相关内容进行回顾并总结.数据结构乃编程的基础呢,还是要不时拿出来翻一翻回顾一下.当然数据结构相关博客中我们以Swift语言来实现.因为Swift ...
- 线性表的顺序存储和链式存储的实现(C)
//线性表的顺序存储 #include <stdio.h>typedef int DataType;#define MaxSize 15//定义顺序表typedef struct { Da ...
- c数据结构 -- 线性表之 复杂的链式存储结构
复杂的链式存储结构 循环链表 定义:是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环) 优点:从表中任一节点出发均可找到表中其他结点 注意:涉及遍历操作时,终止条件是判断 ...
- 线性表的顺序存储和链式存储c语言实现
一.线性表的顺序存储 typedef int ElemType;typedef struct List { ElemType *data;//动态分配 ,需要申请空间 int length; }Lis ...
- Java实现链式存储的二叉查找树(递归方法)
二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字: 2. 若右子树非空,则右子树上所有节点关键字值 ...
- Java实现链式存储的二叉树
二叉树的定义: 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要 ...
- 线性表->链式存储->双向链表
文字描述 之前的链表(单链表.循环链表)的链式存储结构中只有一个指示直接后继的指针域.由此,从某个结点出发只能顺指针往后寻查其他结点.若要寻查结点的直接前驱,则需从表头指针出发.即单链表中,NextE ...
随机推荐
- Mac 10.12安装Office 2011
说明:由于WPS没有Mac版的,所以只能装这个,这个版本是2011的,基本够用了. 下载: (链接: https://pan.baidu.com/s/1pKGvXBP 密码: irw9)
- 全面解析C#中参数传递
一.引言 对于一些初学者(包括工作几年的人在内)来说,有时候对于方法之间的参数传递的问题感觉比较困惑的,因为之前在面试的过程也经常遇到参数传递的基础面试题,这样的面试题主要考察的开发人员基础是否扎实, ...
- Java学习之路(六):集合
集合的由来 数组的长度是固定的,当添加的元素超过了数组的长度,就需要对数组重新定义 java内部给我们提供的集合类,能存储任意对象,长度是可以改变的.随着元素的增加而增加,随着元素的减少而减少 数组和 ...
- jquery mobile 动态加载标签时,无法正常展示样式
原因 在chrome中审查元素,发现其增加了很多没有直接写在页面上的标签和样式.页面标签首先经过jquery.mobile-1.4.5.min.js的处理,添加了许多标签,然后再用css布局 解决方案 ...
- 穆里尼奥:曼联没有在今夏尝试过签下C罗
在曼联结束的本个夏季首场友谊赛中,球队5-2战胜了洛杉矶银河,在赛后穆里尼奥出席了赛后的新闻发布会,并且回答了记者的提问.其中他表示曼联在今年夏季从来没有尝试回签C罗,因为这是“不可能完成的任务”. ...
- 前端工程师的mysql笔记
背景 最近常参与后台php项目,虽说刚毕业时自学过一阵子php和mysql,不过长时间没用也忘差不多了,于是把mysql再温习一遍,前端同学也可以一起学习下! mysql安装以及操作 安装 brew ...
- [转]Grunt 新手一日入门
本文转自:http://yujiangshui.com/grunt-basic-tutorial/ 当时学习 Grunt 的时候,真是很头疼.分了两个时间段,学习了两次才硬啃下来,之后才能用在项目中. ...
- Python——如何搭建Python的环境
最近在学Python,只知道python一般是用来写爬虫的,以前看过一个朋友用Python做的爬虫从妹子图网站上下载图片,觉得很有趣,自己也想学一学. 俗话说,万事开头难,首先第一步就是搭建Pytho ...
- HTML5--(3)过渡+动画+颜色+文本
一.过渡transition transition-property指定属性名称 (如width.height.background-color.内外边距) all 所有属性都将获得过渡效果(默认) ...
- OpenFileDialog 打开文件对话框
InitialDirectory 对话框的初始目录 Filter 要在对话框中显示的文件筛选器,例如,"文本文件(*.txt)|*.txt|所有文件(*.*)||*.*" Filt ...