java数据接口之链表_03数据结构和算法(Java描述)~单链表

03数据结构和算法(Java描述)~单链表

本文是上一篇文章的后续,详情点击该链接

链表

单链表

单链表的定义

单链表采用的是链式存储结构,使用一组地址任意的存储单元来存放数据元素。在单链表中, 存储的每一条数据都是以节点来表示的,每个节点的构成为:元素(存储数据的存储单元) + 指 针(存储下一个节点的地址值),单链表的节点结构如下图所示:

787599153d499dcaea9f74d0c9097f9c.png

另外,单链表中的开始节点,我们又称之为首节点;单链表中的终端节点,我们又称之为尾节 点。如下图所示:

8c00379e92dd0d20cf1e617a9d6eafe2.png

根据序号获取结点的操作:

在线性表中,每个节点都有一个唯一的序号,该序号是从 0开始递增的。通过序号获取单链表 的节点时,我们需要从单链表的首节点开始,从前往后循环遍历,直到遇到查询序号所对应的节点 时为止。

以下图为例,我们需要获得序号为2的节点,那么就需要依次遍历获得“节点 11”和“节点 22”, 然后才能获得序号为 2的节点,也就是“节点 33”。

097b7b29df5063c57b3336836273e7b2.png

因此,在链表中通过序号获得节点的操作效率是非常低的,查询的时间复杂度为 O(n)。

根据序号删除节点的操作

根据序号删除节点的操作,我们首先应该根据序号获得需要删除的节点,然后让“删除节点的 前一个节点”指向“删除节点的后一个节点”,这样就实现了节点的删除操作。

以下图为例,我们需要删除序号为2的节点,那么就让“节点22”指向“节点44”即可,这样 就删除了序号为 2的节点,也就是删除了“节点 33”。

9df923a31d80074a21c17f0f4d880e33.png

通过序号来插入节点,时间主要浪费在找正确的删除位置上,故时间复杂度为 O(n)。但是,单 论删除的操作,也就是无需考虑定位到删除节点的位置,那么删除操作的时间复杂度就是 O(1)。

根据序号插入节点的操作

根据序号插入节点的操作,我们首先应该根据序号找到插入的节点位置,然后让“插入位置的 上一个节点”指向“新插入的节点”,然后再让“新插入的节点”指向“插入位置的节点”,这样 就实现了节点的插入操作。

以下图为例,我们需要在序号为 2 的位置插入元素值“00”,首先先把字符串“00”封装为一 个节点对象,然后就让“节点 22”指向“新节点 00”,最后再让“节点00”指向“节点 33”,这 样就插入了一个新节点。

0b0090189e8947dbb0655d59f5d55173.png

通过序号来插入节点,时间主要浪费在找正确的插入位置上,故时间复杂度为 O(n)。但是,单 论插入的操作,也就是无需考虑定位到插入节点的位置,那么插入操作的时间复杂度就是 O(1)。

顺序表和单链表的比较

bbcb7c4ec00efa4b967a6e9ee5eddc5f.png

存储方式比较

顺序表采用一组地址连续的存储单元依次存放数据元素,通过元素之间的先后顺序来确定元素 之间的位置,因此存储空间的利用率较高

单链表采用一组地址任意的存储单元来存放数据元素,通过存储下一个节点的地址值来确定节 点之间的位置,因此存储空间的利用率较低。

存储方式比较

顺序表查找的时间复杂度为 O(1),插入和删除需要移动元素,因此时间复杂度为 O(n)。若是需 要频繁的执行查找操作,但是很少进行插入和删除操作,那么建议使用顺序表。

单链表查找的时间复杂度为 O(n),插入和删除无需移动元素,因此时间复杂度为 O(1)。若是需 要频繁的执行插入和删除操作,但是很少进行查找操作,那么建议使用链表。

补充:根据序号来插入和删除节点,需要通过序号来找到插入和删除节点的位置,那么整体的 时间复杂度为 O(n)。因此,单链表适合数据量较小时的插入和删除操作,如果存储的数据量较大, 那么就建议使用别的数据结构,例如使用二叉树来实现。

空间性能比较

顺序表需要预先分配一定长度的存储空间,如果事先不知道需要存储元素的个数,分配空间过 大就会造成存储空间的浪费,分配空间过小则需要执行耗时的扩容操作。

单链表不需要固定长度的存储空间,可根据需求来进行临时分配,只要有内存足够就可以分配, 在链表中存储元素的个数是没有限制的,无需考虑扩容操作。

代码实现

定义List接口

public interface List {

int size();

void add(Object element);

Object get(int index);

void remove(int index);

void add(int index, Object element);

String toString();

}

SingleLinkedList实现类

public class SingleLinkedList implements List{

// 用于保存单链表中的首节点

private Node headNode;

// 用于保存单链表中的尾节点

private Node lastNode;

// 用于保存单链表中节点的个数

private int size;

// 获取单链表中节点的个数

public int size() {

return this.size;

}

/**

* 添加元素

* @param element 需要添加的数据

*/

public void add(Object element) {

// 1.把需要添加的数据封装成节点对象

Node node = new Node(element);

// 2.处理单链表为空表的情况

if(headNode == null) {

// 2.1把node节点设置为单链表的首节点

headNode = node;

// 2.2把node节点设置为单链表的尾节点

lastNode = node;

}

// 3.处理单链表不是空表的情况

else {

// 3.1让lastNode指向node节点

lastNode.next = node;

// 3.2更新lastNode的值

lastNode = node;

}

// 4.更新size的值

size++;

}

/**

* 根据序号获取元素

* @param index 序号

* @return 序号所对应节点的数据值

*/

public Object get(int index) {

// 1.判断序号是否合法,合法取值范围:[0, size - 1]

if(index < 0 || index >= size) {

throw new IndexOutOfBoundsException("序号不合法,index:" + index);

}

// 2.根据序号获得对应的节点对象

Node node = node(index);

// 3.获取并返回node节点的数据值

return node.data;

}

/**

* 根据序号删除元素

* @param index 序号

*/

public void remove(int index) {

// 1.判断序号是否合法,合法取值范围:[0, size - 1]

if (index < 0 || index >= size) {

throw new IndexOutOfBoundsException("序号不合法,index:" + index);

}

// 2.处理删除节点在开头的情况

if (index == 0) {

// 2.1获得删除节点的后一个节点

Node nextNode = headNode.next;

// 2.2设置headNode的next值为null

headNode.next = null;

// 2.3设置nextNode为单链表的首节点

headNode = nextNode;

}

// 3.处理删除节点在末尾的情况

else if (index == size - 1) {

// 3.1获得删除节点的前一个节点

Node preNode = node(index - 1);

// 3.2设置preNode的next值为null

preNode.next = null;

// 3.3设置preNode为单链表的尾节点

lastNode = preNode;

}

// 4.处理删除节点在中间的情况

else {

// 4.1获得index-1所对应的节点对象

Node preNode = node(index - 1);

// 4.2获得index+1所对应的节点对象

Node nextNode = preNode.next.next;

// 4.3获得删除节点并设置next值为null

preNode.next.next = null;

// 4.4设置preNode的next值为nextNode

preNode.next = nextNode;

}

// 5.更新size的值

size--;

}

/**

* 根据序号插入元素

* @param index 序号

* @param element 需要插入的数据

*/

public void add(int index, Object element) {

// 1.判断序号是否合法,合法取值范围:[0, size]

if(index < 0 || index > size) {

throw new IndexOutOfBoundsException("序号不合法,index:" + index);

}

// 2.把需要添加的数据封装成节点对象

Node node = new Node(element);

// 3.处理插入节点在开头位置的情况

if(index == 0) {

// 3.1设置node的next值为headNode

node.next = headNode;

// 3.2设置node节点为单链表的首节点

headNode = node;

}

// 4.处理插入节点在末尾位置的情况

else if(index == size) {

// 4.1设置lastNode的next值为node

lastNode.next = node;

// 4.2设置node节点为单链表的尾节点

lastNode = node;

}

// 5.处理插入节点在中间位置的情况

else {

// 5.1获得index-1所对应的节点对象

Node preNode = node(index - 1);

// 5.2获得index所对应的节点对象

Node curNode = preNode.next;

// 5.3设置preNode的next为node

preNode.next = node;

// 5.4设置node的next为curNode

node.next = curNode;

}

// 6.更新size的值

size++;

}

/**

* 根据序号获得对应的节点对象

* @param index 序号

* @return 序号对应的节点对象

*/

private Node node(int index) {

// 1.定义一个零时节点,用于辅助单链表的遍历操作

Node tempNode = headNode;

// 2.定义一个循环,用于获取index对应的节点对象

for(int i = 0; i < index; i++) {

// 3.更新tempNode的值

tempNode = tempNode.next;

}

// 4.返回index对应的节点对象

return tempNode;

}

// 节点类

private static class Node {

/**

* 用于保存节点中的数据

*/

private Object data;

/**

* 用于保存指向下一个节点的地址值

*/

private Node next;

/**

* 构造方法

* @param data

*/

public Node(Object data) {

this.data = data;

}

}

public String toString() {

//判断是否为空,如果为空就直接返回 []

if(headNode == null){

return "[]";

}

StringBuilder stringBuilder = new StringBuilder("[");

Node p = headNode.next;

while(p != null){

stringBuilder.append(p.data + ",");

p = p.next;

}

//最后一个逗号删掉

stringBuilder.deleteCharAt(stringBuilder.length()-1);

stringBuilder.append("]");

return stringBuilder.toString();

}

}

Test

public class Test {

public static void main(String[] args) {

// 1.创建一个对象

List list = new SingleLinkedList();

// 2.添加元素

list.add("11"); // 0

list.add("22"); // 1

list.add("33"); // 2

list.add("44"); // 3

list.add("55"); // 4

//删除

list.remove(0);

//序号添加

list.add(2, "00");

//测试get

for(int i = 0; i < list.size(); i++) {

System.out.print(list.get(i) + " ");

}

//toString

System.out.println(list.toString());

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值