通常我们在一些比较大型公司面试中,可能会碰到数据结构的面试,而在面试中出现链表的频率很高,有的比较正常,考链表的常规操作,主要看基本功是否扎实,有些就比较难,难在思维的改变和是否能够想到对应的点。
我们来简单的看看这样的两个问题:
何为链表?
链式存储的线性表,简称链表。链表由多个链表元素组成,这些元素称为节点。结点之间通过逻辑连接,形成链式存储结构。存储结点的内存单元,可以是连续的也可以是不连续的。逻辑连接与物理存储次序没有关系。
小知识点的拓展:认识下链表的两个重要的域: 指针域与数据域。
指针域:用于存放结点的值
数据域:用于存放下一个结点的地址或位置

在基于我们在学习JAVASE中学习LinkedList基础之上,我们设计一个环形链表的数据结构。
我们知道在设计环形数据结构的时候,可以得到在设计环形链表需要包含以下几个点:
环形链表设计:
class Node {
object value //当前节点值
Node next; // 下一个节点元素
}
备注:因为环形链表是首尾相连,所以最后一个节点的next的存放的头节点的位置
首先我们设计一个链表的节点元素的设计
根据上述的描述我们节点元素设计模式:
Node 包含的重要的部分
Noded的设计模型:

当我们设计好数据结构的节点元素属性后,还需要完成对环形链表的数据结构的设计。
我们命名为 :RingList
我们设计环形链表的数据结构时候,包含以下几点:
RingList :
设计模型:

通过上述的设计模型我们可以看出的环形链表的空间设计模型。
那么接下来我们一起使用JAVA代码是如何实现环形链表。
下图展示了RingList 的属性结构示意图

针对于环型链表的设计,我们可以可以提供以下的两个简单的方式实现。分别是元素添加add() 与元素的获取 get();
首先如何实现环形链表的最主要还是在于add () 方法的实现.
在我们实现add () 方法我们需要完成以下步骤。
我在设计添加元素的采用栈模型的来设计,栈模型就是“先进后出”原则。所以添加元素在头部添加。
首先展示 获取元素方法 get()
备注: 根据上述方法的实现,我们 Node temp指向的位置在不断的变化,一直在移动至指定位置。此时这里temp 就是一个指针的概念。
测试结果展示

根据结果我们的得出环形链表的设计成功。
②在单链表中,从一已知结点出发,只能访问到该结点及其后续结点,无法找到该结点之前的其它结点。而在单循环链表中,从任一结点出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。
在我们所用的很多优秀的框架 设计数据存储的时候,都是了环形链表的设计。环形链表有多种设计模型。
如著名高性能队列 Disruptor, Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能
后续将会给大家带来Disruptor框架解析。
今天我们首先认识环形队列,下周将会给快慢指针的应用。请持续关注。
出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。
在我们所用的很多优秀的框架 设计数据存储的时候,都是了环形链表的设计。环形链表有多种设计模型。
如著名高性能队列 Disruptor, Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。
后续将会给大家带来Disruptor框架解析。
今天我们认识了环形队列,接下来大家可以去延伸阅读我另一文章快慢指针的应用。将有大的收获,请多关注。
我们来简单的看看这样的两个问题:
- 判断一个单向链表是否是环形链表?
- 给定一个未知的长度的单向环形链表,如何确定链表中间位置的节点元素?
1.认识:环形链表
1.1 链表
简单认识链表何为链表?
链式存储的线性表,简称链表。链表由多个链表元素组成,这些元素称为节点。结点之间通过逻辑连接,形成链式存储结构。存储结点的内存单元,可以是连续的也可以是不连续的。逻辑连接与物理存储次序没有关系。
小知识点的拓展:认识下链表的两个重要的域: 指针域与数据域。
指针域:用于存放结点的值
数据域:用于存放下一个结点的地址或位置
1.2 循环链表
循环链表,类似于单链表,也是一种链式存储结构,循环链表由单链表演化过来。单链表的最后一个结点的链域指向NULL,而循环链表的建立,不要专门的头结点,让最后一个结点的链域指向链表结点。 简单点说链表首位相连,组成环状数据结构。如下图结构:
在基于我们在学习JAVASE中学习LinkedList基础之上,我们设计一个环形链表的数据结构。
我们知道在设计环形数据结构的时候,可以得到在设计环形链表需要包含以下几个点:
环形链表设计:
- head链表头:记录链表的入口位置
- 容量:记录这个元素的个数
- Next: 下一个节点元素
class Node {
object value //当前节点值
Node next; // 下一个节点元素
}
备注:因为环形链表是首尾相连,所以最后一个节点的next的存放的头节点的位置
2. 实现环形链表
首先我们设计一个链表的节点元素的设计根据上述的描述我们节点元素设计模式:
Node 包含的重要的部分
- 数据域: 当前节点值 value 属性
- 指针域: 执行下一个节点内存位置
Noded的设计模型:

| package com.shsxt.ring; /** * 环形链表节点元素 */ public class Node { // 当前节点值 private T value; // 下一个节点 private Node next; public Node() {} public Node(T value, Node next) { super(); this.value = value; this.next = next; } public T getValue() { return value; } public void setValue(T value) { this.value = value; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } } |
当我们设计好数据结构的节点元素属性后,还需要完成对环形链表的数据结构的设计。
我们命名为 :RingList
我们设计环形链表的数据结构时候,包含以下几点:
RingList :
- 头指针: head 链表的起始位置。
- 容量: size 链表容量。
设计模型:

通过上述的设计模型我们可以看出的环形链表的空间设计模型。
那么接下来我们一起使用JAVA代码是如何实现环形链表。
下图展示了RingList 的属性结构示意图

针对于环型链表的设计,我们可以可以提供以下的两个简单的方式实现。分别是元素添加add() 与元素的获取 get();
首先如何实现环形链表的最主要还是在于add () 方法的实现.
在我们实现add () 方法我们需要完成以下步骤。
我在设计添加元素的采用栈模型的来设计,栈模型就是“先进后出”原则。所以添加元素在头部添加。
- 创建新节点,设置节点数据域。
- 判断出节点添加的位置
- 在头部节点没有元素,新节点为第一个元素。
- 在头部节点添加新节点,并且将尾部节点的指针指向头部。
- 容量增加
2.1增加方法add
代码设计如下:| public void add(T t) { if (t == null) { throw new RuntimeException("节点元素为空"); } Node node = new Node(); node.setValue(t); // 如果头节点元素 if (head == null) { head = node; last = node; } else { // 我们按照栈的模式来设计 先进后出的模式 node.setNext(head); // 将原先的头放置 新元素 head = node; last.setNext(node);// 将最后的节点的指针指向头部 } size++; } |
2.2获取元素方法设计
在设计线性链表的获取线性位置,我们借助指针移动的概念设计。首先展示 获取元素方法 get()
| public T get(int index) { if (index < 0) { throw new RuntimeException("索引越界 index :" + index); } if (head != null) { Node temp = head;// 遍历的开始位置 for (int i = 0; i < index; i++) { temp = temp.getNext(); } return temp.getValue();//获取节点元素 } return null; } |
备注: 根据上述方法的实现,我们 Node temp指向的位置在不断的变化,一直在移动至指定位置。此时这里temp 就是一个指针的概念。
2.3环形链表完成的设计
| /** * 环形链表 * @param */ public class RingList { // 头指针 private Node head; private int size; private Node last; public RingList() {} public void add(T t) { if (t == null) { throw new RuntimeException("节点元素为空"); } Node node = new Node(); node.setValue(t); // 如果头节点元素 if (head == null) { head = node; last = node; } else { // 我们按照栈的模式来设计 先进后出的模式 node.setNext(head); // 将原先的头放置 新元素 head = node; last.setNext(node);// 将最后的节点的指针指向头部 } size++; } /** * 取值 * * @param index * @return */ public T get(int index) { if (index < 0) { throw new RuntimeException("索引越界 index :" + index); } if (head != null) { Node temp = head;// 遍历的开始位置 for (int i = 0; i < index; i++) { temp = temp.getNext(); } return temp.getValue();//获取节点元素 } return null; } public int size() { return size; } } |
3.测试环形链表代码
| public class App { public static void main(String[] args) { // 创建环形链表 RingList ring = new RingList<>(); // 组装数据 for (int i = 0; i < 20; i++) { ring.add("节点"+i); } System.out.println(ring.size()); // 获取值 for (int i = 0; i < 100; i++) { System.err.print(ring.get(i) + " , "); if (i%10 ==0) { System.out.println(); } } } } |
测试结果展示

根据结果我们的得出环形链表的设计成功。
4. 应用场景
循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。- 循环链表中没有NULL指针。涉及遍历操作时,其终止条件就不再是像非循环链表那样判别p或p->next是否为空,而是判别它们是否等于某一指定指针,如头指针或尾指针等。
②在单链表中,从一已知结点出发,只能访问到该结点及其后续结点,无法找到该结点之前的其它结点。而在单循环链表中,从任一结点出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。
在我们所用的很多优秀的框架 设计数据存储的时候,都是了环形链表的设计。环形链表有多种设计模型。
如著名高性能队列 Disruptor, Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能
后续将会给大家带来Disruptor框架解析。
今天我们首先认识环形队列,下周将会给快慢指针的应用。请持续关注。
出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。
在我们所用的很多优秀的框架 设计数据存储的时候,都是了环形链表的设计。环形链表有多种设计模型。
如著名高性能队列 Disruptor, Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。
后续将会给大家带来Disruptor框架解析。
今天我们认识了环形队列,接下来大家可以去延伸阅读我另一文章快慢指针的应用。将有大的收获,请多关注。