数据结构Java实现04----循环链表、仿真链表
- 单向循环链表
- 双向循环链表
- 仿真链表
一、单向循环链表:
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----循环链表、仿真链表的更多相关文章
- 数据结构java(一)数组链表
链表是数据结构中最基础的内容,链表在存储结构上分成两种:数组形式储存,链式存储. 相比c语言需要的结构体,在java中由于有了面向对象编程,将指针‘藏’了起来,不需要分配内存. 所以只需要创建一个对象 ...
- 纯数据结构Java实现(5/11)(Set&Map)
纯数据结构Java实现(5/11)(Set&Map) Set 和 Map 都是抽象或者高级数据结构,至于底层是采用树还是散列则根据需要而定. 可以细想一下 TreeMap/HashMap, T ...
- [Java 教程 04] Java基础语法
在上一篇文章中我们已经运行了个简单的java程序,但是没有给大家讲解代码部分的内容与含义.学习,我们要做到知其然而知其所以然,所以本篇文章我们就来讲解java程序的基本语法,学完这篇文章你再回头看上篇 ...
- 024 01 Android 零基础入门 01 Java基础语法 03 Java运算符 04 关系运算符
024 01 Android 零基础入门 01 Java基础语法 03 Java运算符 04 关系运算符 本文知识点:Java中的关系运算符 关系运算符
- 004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构
004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构 Java程序的结构 Java程序外层--类 程序外层,如下面的代码,是一个类的定义. c ...
- 111 01 Android 零基础入门 02 Java面向对象 04 Java继承(上)02 继承的实现 01 继承的实现
111 01 Android 零基础入门 02 Java面向对象 04 Java继承(上)02 继承的实现 01 继承的实现 本文知识点: 继承的实现 说明:因为时间紧张,本人写博客过程中只是对知识点 ...
- java数据结构和算法04(链表)
前面我们看了数组,栈和队列,大概就会这些数据结构有了一些基本的认识,首先回顾一下之前的东西: 在数组中,其实是分为有序数组和无序数组,我简单实现了无序数组,为什么呢?因为有序数组的实现就是将无序数组进 ...
- 数据结构Java实现03----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
- 数据结构Java实现02----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
随机推荐
- 静态导入Static import
静态导入Static import 要使用静态成员(方法和变量)我们必须给出提供这个静态成员的类. 使用静态导入可以使被导入类的静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名 ...
- 【GPU编解码】GPU硬解码---CUVID
问题描述:项目中,需要对高清监控视频分析处理,经测试,其解码过程所占CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈. 解决思路: 利用GPU解码高清视频,降低解码所占用CPU资源,加速解 ...
- NYOJ 42 一笔画问题
一笔画问题 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...
- swift学习笔记之-构造过程
//构造过程 import UIKit /* 构造过程(Initialization): 1.构造过程是使用类.结构体或枚举类型的一个实例的准备过程.在新实例可用前必须执行这个过程,具体操作包括设置实 ...
- 基础学习day05---面向对象一类,对象、封装
一.面向对象 1.1.面向对象的概念 很经典的一句话----------万物皆对象 定义:面向对象一相对面向过程而言的 面向对象和面向过程都是一种思想 面向过程-- ...
- CocoaPods的使用
一.安装Cocoapods步骤:(在终端安装) 1.在安装前,需确保已经安装了Ruby环境 mac 下安装Ruby环境步骤: (1).安装RVM $ curl -L https://get.rvm.i ...
- 走着官方的教程入门Material Design(一)
又到期末了,学习下Google的材料设计.写下此文记录自己的同时,分享给需要的同学,若发现文中有什么问题和不对,欢迎指出 使用 Material Design 创建新应用 首先需要使用材料主题 如果是 ...
- iOS开发笔记8:Remote Notification远程消息推送处理
远程消息推送处理场景有三种:分别是app还没有运行.app在前台运行以及app在后台运行,下面介绍相关流程及三种场景下处理步骤 1.流程 (1)注册通知 首先是在注册远程消息推送,需要注意的是iOS8 ...
- 配置JDK环境变量
•配置JDK环境变量<Windows系统下> 点击我的电脑右键----->属性------>高级------>环境变量-------> 新建(建议在系统变量中新建 ...
- HTTPS连接的前几毫秒发生了什么——Amazon HTTPS案例分析
转自: http://blog.jobbole.com/48369/ 提示:英文原文写于2009年,当时的Firefox和最新版的Firefox,界面也有很大改动.以下是正文. 花了数小时阅读了如潮的 ...