您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

数据结构Java实现:循环链表和双向链表

时间:2019-07-08 09:46:15  来源:  作者:
数据结构Java实现:循环链表和双向链表

 

上篇教程给大家分享了单链表的概念,以及如何用 JAVA 实现一个单链表的结构。单链表是最基础的一种链表结构,在实际开发中的使用有一些局限性,比如只能单向往后查找节点,如果需要找到某元素的前驱节点,单链表是无法实现的,今天来给大家分享另外两个复杂一些的链表结构:循环链表和双向链表。

循环链表

循环链表本质上就是一种单链表,两者的不同之处在于链表中最后一个节点的指针指向哪里,在单链表中,末尾节点的指针指向空,如下图所示。

 

数据结构Java实现:循环链表和双向链表

 

第一个元素 a 的指针指向 1008,即第二个元素 b 的首地址,b 的指针指向第三个元素 c 的内存地址 1020,没有第四个元素,所以 c 的指针指向空。

而在循环链表中,末尾节点的指针指向首节点,形成一个闭环,所以它叫循环链表,应该很好理解,如下图所示。

数据结构Java实现:循环链表和双向链表

 

接下来用 Java 实现一个循环链表的结构,只需要在原有单链表的基础上稍作修改即可,如下所示。

package com.southwind.link;
public class Node {
 //数据
 public Object data;
 //下一个节点
 public Node next;
 public Node(Object data){
 this.data = data;
 }
}
package com.southwind.link;
public class LoopLinkedList {
 public int size;
 public Node head;
 /**
 * 添加元素
 * @param obj
 * @return
 */
 public Node add(Object obj){
 Node newNode = new Node(obj);
 if(size == 0){
 head = newNode;
 head.next = head;
 }else{
 Node target = head;
 while(target.next!=head){
 target = target.next;
 }
 target.next = newNode;
 newNode.next = head;
 }
 size++;
 return newNode;
 }
 /**
 * 在指定位置插入元素
 * @return
 */
 public Node insert(int index,Object obj){
 if(index >= size){
 return null;
 }
 Node newNode = new Node(obj);
 if(index == 0){
 newNode.next = head;
 head = newNode;
 }else{
 Node target = head;
 Node previous = head;
 int pos = 0;
 while(pos != index){
 previous = target;
 target = target.next;
 pos++;
 }
 previous.next = newNode;
 newNode.next = target;
 }
 size++;
 return newNode;
 }
 /**
 * 删除链表头部元素
 * @return
 */
 public Node removeHead(){
 if(size > 0){
 Node node = head;
 Node target = head;
 while(target.next!=head){
 target = target.next;
 }
 head = head.next;
 target.next = head;
 size--;
 return node;
 }else{
 return null;
 }
 }
 /**
 * 删除指定位置元素
 * @return
 */
 public Node remove(int index){
 if(index >= size){
 return null;
 }
 Node result = head;
 if(index == 0){
 head = head.next;
 }else{
 Node target = head;
 Node previous = head;
 int pos = 0;
 while(pos != index){
 previous = target;
 target = target.next;
 pos++;
 }
 previous.next = target.next;
 result = target;
 }
 size--;
 return result;
 }
 /**
 * 删除指定元素
 * @return
 */
 public Node removeNode(Object obj){
 Node target = head;
 Node previoust = head;
 if(obj.equals(target.data)){
 head = head.next;
 size--;
 }else{
 while(target.next!=null){
 if(obj.equals(target.next.data)){
 previoust = target;
 target = target.next;
 size--;
 break;
 }else{
 target = target.next;
 previoust = previoust.next;
 }
 }
 previoust.next = target.next;
 }
 return target;
 }
 /**
 * 返回指定元素
 * @return
 */
 public Node findNode(Object obj){
 Node target = head;
 while(target.next!=null){
 if(obj.equals(target.data)){
 return target;
 }else{
 target = target.next;
 }
 }
 return null;
 }
 /**
 * 输出链表元素
 */
 public void show(){
 if(size > 0){
 Node node = head;
 int length = size;
 System.out.print("[");
 while(length > 0){
 if(length == 1){
 System.out.print(node.data);
 }else{
 System.out.print(node.data+",");
 }
 node = node.next;
 length--;
 }
 System.out.println("]");
 }else{
 System.out.println("[]");
 }
 }
}

双向链表

双向链表是相对单链表来讲的,区别在于单链表中只有一个指针指向下一个节点,是单向连接的,只能从当前节点访问它的后继节点。而双向链表顾名思义是双向连接的,既可以从当前节点访问到后继节点,也可以访问到前驱节点,所以在双向链表中有两个指针,一个叫做后继指针,指向下一个节点,另一个叫做前驱指针,指向它的上一个节点,双向链表的结构如下图所示。

数据结构Java实现:循环链表和双向链表

 

双向链表相比于单链表会占用更多的内存空间,因为多了一个指针来存储前驱节点的内存地址。虽然如此,但是在某些操作上,相比于单链表,双向链表可以极大地提升效率。

比如要删除链表中的某个节点,那么我们就需要获取到目标节点的前驱节点,然后将前驱节点的指针指向目标节点的下一个节点,如下图所示。

数据结构Java实现:循环链表和双向链表

 

如上所示,删除节点必须先找到其前驱节点,在单链表中是不会记录元素的前驱节点的,所以必须从头开始遍历链表,直到找到目标节点的前驱节点,时间复杂度为 O(n)。

如果是双向链表的结构,每一个节点都会记录其前驱节点,可直接获取,所以此时的时间复杂度为 O(1)。

数据结构Java实现:循环链表和双向链表

 

搞清楚了双向链表的概念,接下来我们用 Java 来实现双向链表的结构。

 

package com.southwind.link;
public class DoubleNode {
 //数据
 public Object data;
 //下一个节点
 public DoubleNode next;
 //上一个节点
 public DoubleNode previous;
 public DoubleNode(Object data){
 this.data = data;
 }
}
package com.southwind.link;
public class DoubleLinkedList {
 public int size;
 public DoubleNode head;
 /**
 * 添加元素
 * @param obj
 * @return
 */
 public DoubleNode add(Object obj){
 DoubleNode newNode = new DoubleNode(obj);
 if(size == 0){
 head = newNode;
 }else{
 DoubleNode target = head;
 while(target.next!=null){
 target = target.next;
 }
 target.next = newNode;
 newNode.previous = target;
 }
 size++;
 return newNode;
 }
 /**
 * 在指定位置插入元素
 * @return
 */
 public DoubleNode insert(int index,Object obj){
 if(index >= size){
 return null;
 }
 DoubleNode newNode = new DoubleNode(obj);
 if(index == 0){
 newNode.next = head;
 head.previous = newNode;
 head = newNode;
 }else{
 DoubleNode target = head;
 int pos = 0;
 while(pos != index){
 target = target.next;
 pos++;
 }
 newNode.previous = target.previous;
 newNode.previous.next = newNode;
 newNode.next = target;
 }
 size++;
 return newNode;
 }
 /**
 * 删除链表头部元素
 * @return
 */
 public DoubleNode removeHead(){
 if(size > 0){
 DoubleNode node = head;
 head = head.next;
 size--;
 return node;
 }else{
 return null;
 }
 }
 /**
 * 删除指定位置元素
 * @return
 */
 public DoubleNode remove(int index){
 if(index >= size){
 return null;
 }
 DoubleNode result = head;
 if(index == 0){
 DoubleNode node = head;
 head = head.next;
 }else{
 DoubleNode target = head;
 int pos = 0;
 while(pos != index){
 target = target.next;
 pos++;
 }
 target.previous.next = target.next;
 }
 size--;
 return result;
 }
 /**
 * 删除指定元素
 * @return
 */
 public DoubleNode removeNode(Object obj){
 DoubleNode target = head;
 if(obj.equals(target.data)){
 DoubleNode node = head;
 head = head.next;
 size--;
 }else{
 while(target.next!=null){
 if(obj.equals(target.data)){
 target.previous.next = target.next;
 size--;
 break;
 }
 target = target.next;
 }
 }
 return target;
 }
 /**
 * 返回指定元素
 * @return
 */
 public DoubleNode findNode(Object obj){
 DoubleNode target = head;
 while(target.next!=null){
 if(obj.equals(target.data)){
 return target;
 }else{
 target = target.next;
 }
 }
 if(target.next==null && obj.equals(target.data)){
 return target;
 }
 return null;
 }
 /**
 * 输出链表元素
 */
 public void show(){
 if(size > 0){
 DoubleNode node = head;
 int length = size;
 System.out.print("[");
 while(length > 0){
 if(length == 1){
 System.out.print(node.data);
 }else{
 System.out.print(node.data+",");
 }
 node = node.next;
 length--;
 }
 System.out.println("]");
 }else{
 System.out.println("[]");
 }
 }
}


Tags:数据结构   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  Tags: 数据结构  点击:(28)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  Tags: 数据结构  点击:(36)  评论:(0)  加入收藏
遵从所有教材以及各类数据结构相关的书书籍,我们先从线性表开始入门。今天这篇文章更偏概念,是关于有线性表的一个知识点的汇总。上文说过,物理结构是用于确定数据以何种方式存...【详细内容】
2021-07-19  Tags: 数据结构  点击:(94)  评论:(0)  加入收藏
1. 线性表线性表是一类最简单、最常用的数据结构。简单来说,一个线性表是n个元素的有限序列,其中n≥0,通常表示为(a1,a2,...,an)。其特点是,在非空的数据元素集合中:(1)存在唯一的一个...【详细内容】
2021-06-09  Tags: 数据结构  点击:(139)  评论:(0)  加入收藏
Mysql索引数据结构下面列举了常见的数据结构 二叉树 红黑树 Hash表 B-Tree(B树)Select * from t where t.col=5我们在执行一条查询的Sql语句时候,在数据量比较大又不加索引的情...【详细内容】
2021-06-07  Tags: 数据结构  点击:(90)  评论:(0)  加入收藏
Redis五种数据结构如下: 1.String 字符串类型是redis中最基本的数据类型,一个key对应一个value。String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,...【详细内容】
2021-04-14  Tags: 数据结构  点击:(208)  评论:(0)  加入收藏
一、栈栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈...【详细内容】
2021-04-06  Tags: 数据结构  点击:(266)  评论:(0)  加入收藏
序列是python中最基本的数据结构。所谓的序列,指的是可以连续存放多个值的内存空间,序列中的每个元素都会有一个数字,即它的位置或索引。通过这个索引就能找到序列中的元素 。...【详细内容】
2021-02-24  Tags: 数据结构  点击:(197)  评论:(0)  加入收藏
不要虚度每一天,你的经验就是这样积累出来,然后用在了你的事情上面。 一:摘要概述很多 redis 的使用者都可以清晰明白的道出Redis中常用的对象如string、list、hash、set、zset...【详细内容】
2021-01-18  Tags: 数据结构  点击:(167)  评论:(0)  加入收藏
速介绍8种常用数据结构数据结构是一种特殊的组织和存储数据的方式,可以使我们可以更高效地对存储的数据执行操作。数据结构在计算机科学和软件工程领域具有广泛而多样的用途...【详细内容】
2020-12-18  Tags: 数据结构  点击:(93)  评论:(0)  加入收藏
▌简易百科推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  顶顶架构师    Tags:面向对象   点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Java架构师之路    Tags:JAVA   点击:(5)  评论:(0)  加入收藏
大家好!我是老码农,一个喜欢技术、爱分享的同学,从今天开始和大家持续分享JVM调优方面的经验。JVM调优是个大话题,涉及的知识点很庞大 Java内存模型 垃圾回收机制 各种工具使用 ...【详细内容】
2021-12-23  小码匠和老码农    Tags:JVM调优   点击:(12)  评论:(0)  加入收藏
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  dingle    Tags:JDBC   点击:(13)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  JAVA小白    Tags:Java   点击:(11)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  小西学JAVA    Tags:JAVA并发   点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  唯一浩哥    Tags:Java基础   点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  软件老王    Tags:logback   点击:(19)  评论:(0)  加入收藏
本篇文章我们以AtomicInteger为例子,主要讲解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe对象。我们先从一个例子开始吧。假设现在我们...【详细内容】
2021-12-14  小西学JAVA    Tags:JAVA   点击:(22)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条