线性表的Java实现--链式存储(单向链表)
单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。
链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素。由于不需要按顺序存储,链表在插入、删除数据元素时比顺序存储要快,但是在查找一个节点时则要比顺序存储要慢
使用链式存储可以克服顺序线性表需要预先知道数据大小的缺点,链表结构可以充分利用内存空间,实现灵活的内存动态管理。但是链式存储失去了数组随机存取的特点,同时增加了节点的指针域,空间开销较大。
下图就是最简单最一般的单向链表:

新增节点:
将值为element的新节点插入到第index的位置上。
首先要先找到索引为index-1的节点,然后生成一个数据为element的新节点newNode,并令index-1处节点的next指向新节点,新节点的next指向原来index处的节点。
删除节点:
删除第index个节点,第index节点是由index-1出的节点引用的,因此删除index的节点要先获取index-1处的节点,然后让index-1出节点的next引用到原index+1处的节点,并释放index处节点即可。

单向链表的Java实现
下面的程序分别实现了线性表的初始化、获取线性表长度、获取指定索引处元素、根据值查找、插入、删除、清空等操作。
package com.liuhao.algorithm;
public class LinkList<T> {
// 定义一个内部类Node,代表链表的节点
private class Node {
private T data;// 保存数据
private Node next;// 指向下个节点的引用
// 无参构造器
public Node() {
}
// 初始化全部属性的构造器
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
private Node header;// 保存头结点
private Node tail;// 保存尾节点
private int size;// 保存已含有的节点数
// 创建空链表
public LinkList() {
header = null;
tail = null;
}
// 已指定数据元素创建链表,只有一个元素
public LinkList(T element) {
header = new Node(element, 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("索引超出线性表范围");
}
Node current = header;//从header开始遍历
for(int i=0; i<size && current!=null; i++,current=current.next){
if(i == index){
return current;
}
}
return null;
}
//按值查找所在位置
public int locate(T element){
Node current = header;
for(int i=0; i<size && current!=null; i++, current=current.next){
if(current.data.equals(element)){
return i;
}
}
return -1;
}
//指定位置插入元素
public void insert(T element, int index){
if(index < 0 || index > size){
throw new IndexOutOfBoundsException("索引超出线性表范围");
}
//如果是空链表
if(header == null){
add(element);
}
else{
//当index为0时,即在链表头处插入
if(0 == index){
addAtHead(element);
}
else{
Node prev = getNodeByIndex(index - 1);//获取前一个节点
//让prev的next指向新节点,新节点的next指向原来prev的下一个节点
prev.next = new Node(element, prev.next);
size++;
}
}
}
//在尾部插入元素
public void add(T element) {
//如果链表是空的
if(header == null){
header = new Node(element, null);
//只有一个节点,headwe,tail都该指向该节点
tail = header;
}
else{
Node newNode = new Node(element, null);//创建新节点
tail.next = newNode;//尾节点的next指向新节点
tail = newNode;//将新节点作为尾节点
}
size++;
}
//头部插入
public void addAtHead(T element){
//创建新节点,让新节点的next指向header
//并以新节点作为新的header
Node newNode = new Node(element, null);
newNode.next = header;
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;
}
else{
Node prev = getNodeByIndex(index - 1);//获取待删除节点的前一个节点
del = prev.next;//获取待删除节点
prev.next = del.next;
del.next = null;//将被删除节点的next引用置为空
}
size--;
return del.data;
}
//删除最后一个元素
public T remove(){
return delete(size - 1);
}
//判断线性表是否为空
public boolean isEmpty(){
return size == 0;
}
//清空线性表
public void clear(){
//将header,tail置为null
header = null;
tail = null;
size = 0;
}
public String toString(){
if(isEmpty()){
return "[]";
}
else{
StringBuilder sb = new StringBuilder("[");
for(Node current = header; current != null; current = current.next){
sb.append(current.data.toString() + ", ");
}
int len = sb.length();
return sb.delete(len-2, len).append("]").toString();
}
}
}
测试代码
package com.liuhao.test;
import org.junit.Test;
import com.liuhao.algorithm.LinkList;
public class LinkListTest {
@Test
public void test() {
// 测试构造函数
LinkList<String> list = new LinkList("好");
System.out.println(list);
// 测试添加元素
list.add("ni");
list.add("没");
System.out.println(list);
// 在头部添加
list.addAtHead("五月");
System.out.println(list);
// 在指定位置添加
list.insert("摩卡", 2);
System.out.println(list);
// 获取指定位置处的元素
System.out.println("第2个元素是(从0开始计数):" + list.get(2));
// 返回元素索引
System.out.println("摩卡在的位置是:" + list.locate("摩卡"));
System.out.println("moka所在的位置:" + list.locate("moka"));
// 获取长度
System.out.println("当前线性表的长度:" + list.length());
// 判断是否为空
System.out.println(list.isEmpty());
// 删除最后一个元素
list.remove();
System.out.println("调用remove()后:" + list);
// 获取长度
System.out.println("当前线性表的长度:" + list.length());
// 删除指定位置处元素
list.delete(3);
System.out.println("删除第4个元素后:" + list);
// 获取长度
System.out.println("当前线性表的长度:" + list.length());
// 清空
list.clear();
System.out.println(list);
// 判断是否为空
System.out.println(list.isEmpty());
}
}
线性表的Java实现--链式存储(单向链表)的更多相关文章
- 线性表的Java实现--链式存储(双向链表)
有了单向链表的基础,双向链表的实现就容易多了. 双向链表的一般情况: 增加节点: 删除节点: 双向链表的Java实现: package com.liuhao.algorithm; publi ...
- 数据结构导论 四 线性表的顺序存储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),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要 ...
- Java实现线性表-顺序表示和链式表示
顺序表示和链式表示的比较: 1.读写方式:顺序表可以顺序存取,也可以随机存取:链表只能从表头顺序存取元素: 2.逻辑结构与物理结构:顺序存储时,逻辑上相邻的元素其对应的物理存储位置也相邻:链式存储时, ...
随机推荐
- POJ-1258 Agri-Net(最小生成树)
Description Farmer John has been elected mayor of his town! One of his campaign promises was to brin ...
- Mac 10.12打开任何来源选项
在终端中输入: sudo spctl --master-disable 然后输入密码即可.最终会出现如下图所示:
- (转)搞个这样的APP要多久?心酸啊。
这是一个“如有雷同,纯属巧合”的故事,外加一些废话,大家请勿对号入座.开始了…… 我有些尴尬地拿着水杯,正对面坐着来访的王总,他是在别处打拼的人,这几年据说收获颇丰,见移动互联网如火如荼,自然也想着要 ...
- oauth2.0的授权流程详解
授权模式 1)oauth2.0 提供了四种授权模式,开发者可以根据自己的业务情况自由选择. 授权码授权模式(Authorization Code Grant) 隐式授权模式(简化模式)(Implici ...
- ubuntu设置root权限默认密码
1.默认root密码是随机的,即每次开机都有一个新的root密码.我们可以在终端输入命令 sudo passwd,然后输入当前用户的密码2.终端会提示我们输入新的密码并确认,此时的密码就是root新密 ...
- "Calendars" and "DateFormats" should not be static
参见:Call to method of static java.text.DateFormat not advisable? 级别:bug, multi-threading Not all cl ...
- 【c++】类中的const成员
const成员变量 举个例子 #include <iostream> using namespace std; class A { public: A(int size) : SIZE(s ...
- ios 点击失效、闪屏问题解决方案
一.点击失效 描述:将点击事件(click)委派在document或者body上,且目标元素为默认不可点击的元素时(非<a>. <button>而是<span>等) ...
- Linux du查询文件大小
#查询磁盘当前容量信息 $df -h #查询当前目录下所有文件的大小 $du -m . #两种方式查询 仅当前目录下的子文件(文件夹)大小 $du -sh /cloud/* $du -h ...
- 阿里巴巴Java开发规约插件使用
10月14日上午9:00 阿里巴巴于在杭州云栖大会<研发效能峰会>上,正式发布<阿里巴巴Java开发手册>扫描插件,该插件在扫描代码后,将不符合<手册>的代码按Bl ...