• 单向循环链表
  • 双向循环链表
  • 仿真链表

一、单向循环链表:

1、概念:

单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个

和单链表相比,循环单链表的长处是从链尾到链头比较方便。当要处理的数据元素序列具有环型结构特点时,适合于采用循环单链表。

和单链表相同,循环单链表也有带头结点结构和不带头结点结构两种,带头结点的循环单链表实现插入和删除操作时,算法实现较为方便。

带头结点的循环单链表的操作实现方法和带头结点的单链表的操作实现方法类同,差别仅在于:

(1)在构造函数中,要加一条head.next = head 语句,把初始时的带头结点的循环单链表设计成上图中(a)所示的状态。

(2)在index(i)成员函数中,把循环结束判断条件current != null改为current != head。

2、单链表的代码实现:

先回顾上一篇文章,定位到第三段“三、单项链表的代码实现”,我们是需要修改这段里面的(3)LinkList.java代码,(1)和(2)的代码不变。

(3)LinkList.java:单项循环链表类:(核心代码)

 //单向循环链表类
public class CycleLinkList implements List { Node head; //头指针
Node current;//当前结点对象
int size;//结点个数 //初始化一个空链表
public CycleLinkList()
{
//初始化头结点,让头指针指向头结点。并且让当前结点对象等于头结点。
this.head = current = new Node(null);
this.size =0;//单向链表,初始长度为零。
this.head.next = this.head;
} //定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点。
//比如我们要在a2这个节点之前进行插入操作,那就先要把当前节点对象定位到a1这个节点,然后修改a1节点的指针域
public void index(int index) throws Exception
{
if(index <-1 || index > size -1)
{
throw new Exception("参数错误!");
}
//说明在头结点之后操作。
if(index==-1) //因为第一个数据元素结点的下标是0,那么头结点的下标自然就是-1了。
return;
current = head.next;
int j=0;//循环变量
while(current != head&&j<index)
{
current = current.next;
j++;
} } @Override
public void delete(int index) throws Exception {
// TODO Auto-generated method stub
//判断链表是否为空
if(isEmpty())
{
throw new Exception("链表为空,无法删除!");
}
if(index <0 ||index >size)
{
throw new Exception("参数错误!");
}
index(index-1);//定位到要操作结点的前一个结点对象。
current.setNext(current.next.next);
size--;
} @Override
public Object get(int index) throws Exception {
// TODO Auto-generated method stub
if(index <-1 || index >size-1)
{
throw new Exception("参数非法!");
}
index(index); return current.getElement();
} @Override
public void insert(int index, Object obj) throws Exception {
// TODO Auto-generated method stub
if(index <0 ||index >size)
{
throw new Exception("参数错误!");
}
index(index-1);//定位到要操作结点的前一个结点对象。
current.setNext(new Node(obj,current.next));
size++;
} @Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return size==0;
}
@Override
public int size() {
// TODO Auto-generated method stub
return this.size;
} }

14行是添加的代码,30行是修改的代码。

3、单项循环链表的应用举例:

编写击鼓传花小游戏。

游戏规则:N个人围成一个圈,从第一个人开始传花,当数到M时,该人退出游戏,直到剩下最后一个人。

代码实现:

(4)Game.java:

 //游戏类
public class Game { //单向循环链表
CycleLinkList list = new CycleLinkList();
//总人数
int num;
//数到几退出
int key; //游戏初始化方法
public Game(int num,int key)
{
this.num = num;
this.key = key;
} public void play() throws Exception
{
for(int i=0;i<num;i++)
{
list.insert(i, i);
} System.out.println("\n-------游戏开始之前---------\n");
for(int i=0;i<list.size;i++)
{
System.out.print(list.get(i)+" ");
}
System.out.println("\n-------游戏开始---------\n");
int iCount=num; //开始等于总人数num
int j=0; //累加器,计算是否能被key整除。 Node node = list.head;
while(iCount!=1)
{
if(node.getElement()!=null&& Integer.parseInt(node.getElement().toString())!=-1)
{
j++;
if(j%key==0)
{
node.setElement(-1);
iCount--;
System.out.println();
for(int i=0;i<list.size;i++)
{
System.out.print(list.get(i)+" ");
}
}
}
node = node.next;
}
System.out.println("\n-------游戏结束---------\n");
for(int i=0;i<list.size;i++)
{
System.out.print(list.get(i)+" ");
}
} }

(5)Test.java:

 public class Test {

     /**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
/*
CycleLinkList list = new CycleLinkList();
for(int i=0;i<10;i++)
{
int temp = ((int)(Math.random()*100))%100;
list.insert(i, temp);
System.out.print(temp+" ");
}
list.delete(4);
System.out.println("\n------删除第五个元素之后-------");
for(int i=0;i<list.size;i++)
{
System.out.print(list.get(i)+" ");
}*/ Game game = new Game(10,3);
game.play(); }
}

二、双向循环链表:

双向链表:

  双向链表是每个结点除后继指针外还有一个前驱指针。和单链表类同,双向链表也有带头结点结构和不带头结点结构两种,带头结点的双向链表更为常用;另外,双向链表也可以有循环和非循环两种结构,循环结构的双向链表更为常用。

双向循环链表:

  在双向链表中,每个结点包括三个域,分别是element域、next域和prior域,其中element域为数据元素域,next域为指向后继结点的对象引用,prior域为指向前驱结点的对象引用。下图为双向链表结点的图示结构:

如下图是带头结点的双向循环链表的图示结构。双向循环链表的next和prior各自构成自己的单向循环链表:

在双向链表中,有如下关系:设对象引用p表示双向链表中的第i个结点,则p.next表示第i+1个结点,p.next.prior仍表示第i个结点,即p.next.prior == p;同样地,p.prior表示第i-1个结点,p.prior.next仍表示第i个结点,即p.prior.next == p。下图是双向链表上述关系的图示:

双向循环链表的插入过程:

  下图中的指针p表示要插入结点的位置,s表示要插入的结点,①、②、③、④表示实现插入过程的步骤:

循环双向链表的删除过程:

  下图中的指针p表示要插入结点的位置,①、②表示实现删除过程的步骤:

2、双向循环链表的代码实现:

(1)List.java:

 //线性表接口
public interface List {
//获得线性表长度
public int size(); //判断线性表是否为空
public boolean isEmpty(); //插入元素
public void insert(int index, Object obj) throws Exception; //删除元素
public void delete(int index) throws Exception; //获取指定位置的元素
public Object get(int index) throws Exception;
}

(2)Node.java:

 //结点类
public class Node { Object element; //数据域
Node next; //后继指针域
Node prior; //前驱指针域 //头结点的构造方法
public Node(Node nextval) {
this.next = nextval;
} //非头结点的构造方法
public Node(Object obj, Node nextval) {
this.element = obj;
this.next = nextval;
} //获得当前结点的后继结点
public Node getNext() {
return this.next;
} //获得当前结点的前驱结点
public Node getPrior() {
return this.prior;
} //获得当前的数据域的值
public Object getElement() {
return this.element;
} //设置当前结点的后继指针域
public void setNext(Node nextval) {
this.next = nextval;
} //设置当前结点的前驱指针域
public void setPrior(Node priorval) {
this.prior = priorval;
} //设置当前结点的数据域
public void setElement(Object obj) {
this.element = obj;
} public String toString() {
return this.element.toString();
}
}

(3)DoubleCycleLinkList.java:

 //单向链表类
public class DoubleCycleLinkList implements List { Node head; //头指针
Node current;//当前结点对象
int size;//结点个数 //初始化一个空链表
public DoubleCycleLinkList() {
//初始化头结点,让头指针指向头结点。并且让当前结点对象等于头结点。
this.head = current = new Node(null);
this.size = 0;//单向链表,初始长度为零。
this.head.next = head;
this.head.prior = head;
} //定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点。
public void index(int index) throws Exception {
if (index < -1 || index > size - 1) {
throw new Exception("参数错误!");
}
//说明在头结点之后操作。
if (index == -1)
return;
current = head.next;
int j = 0;//循环变量
while (current != head && j < index) {
current = current.next;
j++;
} } @Override
public void delete(int index) throws Exception {
// TODO Auto-generated method stub
//判断链表是否为空
if (isEmpty()) {
throw new Exception("链表为空,无法删除!");
}
if (index < 0 || index > size) {
throw new Exception("参数错误!");
}
index(index - 1);//定位到要操作结点的前一个结点对象。
current.setNext(current.next.next);
current.next.setPrior(current);
size--;
} @Override
public Object get(int index) throws Exception {
// TODO Auto-generated method stub
if (index < -1 || index > size - 1) {
throw new Exception("参数非法!");
}
index(index); return current.getElement();
} @Override
public void insert(int index, Object obj) throws Exception {
// TODO Auto-generated method stub
if (index < 0 || index > size) {
throw new Exception("参数错误!");
}
index(index - 1);//定位到要操作结点的前一个结点对象。
current.setNext(new Node(obj, current.next));
current.next.setPrior(current);
current.next.next.setPrior(current.next); size++;
} @Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return size == 0;
} @Override
public int size() {
// TODO Auto-generated method stub
return this.size;
} }

(4)Test.java:

 public class Test {

     /**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
DoubleCycleLinkList list = new DoubleCycleLinkList();
for (int i = 0; i < 10; i++) {
int temp = ((int) (Math.random() * 100)) % 100;
list.insert(i, temp);
System.out.print(temp + " ");
}
list.delete(4);
System.out.println("\n------删除第五个元素之后-------");
for (int i = 0; i < list.size; i++) {
System.out.print(list.get(i) + " ");
}
} }

运行效果:

三、仿真链表:

在链式存储结构中,我们实现数据元素之间的次序关系依靠指针。我们也可以用数组来构造仿真链表。方法是在数组中增加一个(或两个)int类型的变量域,这些变量用来表示后一个(或前一个)数据元素在数组中的下标。我们把这些int类型变量构造的指针称为仿真指针。这样,就可以用仿真指针构造仿真的单链表(或仿真的双向链表)。

数据结构Java实现04----循环链表、仿真链表的更多相关文章

  1. 数据结构java(一)数组链表

    链表是数据结构中最基础的内容,链表在存储结构上分成两种:数组形式储存,链式存储. 相比c语言需要的结构体,在java中由于有了面向对象编程,将指针‘藏’了起来,不需要分配内存. 所以只需要创建一个对象 ...

  2. 纯数据结构Java实现(5/11)(Set&Map)

    纯数据结构Java实现(5/11)(Set&Map) Set 和 Map 都是抽象或者高级数据结构,至于底层是采用树还是散列则根据需要而定. 可以细想一下 TreeMap/HashMap, T ...

  3. [Java 教程 04] Java基础语法

    在上一篇文章中我们已经运行了个简单的java程序,但是没有给大家讲解代码部分的内容与含义.学习,我们要做到知其然而知其所以然,所以本篇文章我们就来讲解java程序的基本语法,学完这篇文章你再回头看上篇 ...

  4. 024 01 Android 零基础入门 01 Java基础语法 03 Java运算符 04 关系运算符

    024 01 Android 零基础入门 01 Java基础语法 03 Java运算符 04 关系运算符 本文知识点:Java中的关系运算符 关系运算符

  5. 004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构

    004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构 Java程序的结构 Java程序外层--类 程序外层,如下面的代码,是一个类的定义. c ...

  6. 111 01 Android 零基础入门 02 Java面向对象 04 Java继承(上)02 继承的实现 01 继承的实现

    111 01 Android 零基础入门 02 Java面向对象 04 Java继承(上)02 继承的实现 01 继承的实现 本文知识点: 继承的实现 说明:因为时间紧张,本人写博客过程中只是对知识点 ...

  7. java数据结构和算法04(链表)

    前面我们看了数组,栈和队列,大概就会这些数据结构有了一些基本的认识,首先回顾一下之前的东西: 在数组中,其实是分为有序数组和无序数组,我简单实现了无序数组,为什么呢?因为有序数组的实现就是将无序数组进 ...

  8. 数据结构Java实现03----单向链表的插入和删除

    文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定)            概念: 链式存储结构是基于指针实现的.我们把一个数据 ...

  9. 数据结构Java实现02----单向链表的插入和删除

    文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定)            概念: 链式存储结构是基于指针实现的.我们把一个数据 ...

随机推荐

  1. C#中的接口实现多态

    我们都知道虚方法实现多态,抽象方法实现多态等,我们今天来看看如何使用接口实现多态 1.首先我们先要来了解了解什么是接口,它存在的意识 01.接口就是为了约束方法的格式(参数和返回值类型)而存在的 02 ...

  2. 小白初学ABP框架,着实累啊

    这几天在学习ABP相关的知识AutoMapper ,AngularJS,Less,DI(dependencyInjection),EntityFramework code first以及相关NuGet ...

  3. 使django与数据库保持长连接

    最近遇到一个很蛋疼的问题,写了一个后台管理系统, 由于是后台管理系统,所以使用频率不是很高,当django程序在闲置一段时间后,再次打开后台系统,就变得很慢,然后又好了.查了很多方面,从模板引擎到请求 ...

  4. MySQL数据库中字符集的问题

    今天在做Hibernate案例,往mysql中写记录的时候,出现ERROR: Incorrect string value: '\xE5\x8A\xA0\xE5\x86\x85...' for col ...

  5. 转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因

    转:http://www.cnblogs.com/tider1999/p/4308440.html NLog的安装请百度,我安装的是3.2.NLog可以向文件,数据库,邮件等写日志,想了解请百度,这里 ...

  6. R语言学习笔记:向量

    向量是R语言最基本的数据类型. 单个数值(标量)其实没有单独的数据类型,它只不过是只有一个元素的向量. x <- c(1, 2, 4, 9) x <- c(x[1:3], 88, x[4] ...

  7. Android项目实战(十五):自定义不可滑动的ListView和GridView

    不可滑动的ListView (RecyclweView类似) public class NoScrollListView extends ListView { public NoScrollListV ...

  8. Emacs常用命令汇总

    注意:以下命令中标注的按键,大写的C代表Control,在键盘上通常是Ctrl键,而M代表Meta,在键盘上通常是Alt键,S则代表Shift,在键盘上通常是Shift键,也就是 C Control ...

  9. STL--向量(vector)

    STL的组成 标准模板库STL关注的重点是泛型数据结构和算法,其关键组成部分是容器(containers).算法(algorithms).迭代器(iterators).函数对象(Function Ob ...

  10. 认识Runtime1

    认识Runtime1 什么是id? id在objc.h中的定义如下: typedef struct objc_object *id; 那么什么是objc_object呢? objc_object在ob ...