数据结构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----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
随机推荐
- PHP学习笔记:等比例缩放图片
直接上代码,imgzip($src,$newwid,$newhei)这个函数带进去的分别是原图片.缩放要求的宽度.缩放的长度.代码都备注了,不懂可以留言哈哈 <?php //压缩图片 缩略图 $ ...
- Java中的继承
我们在以前的学习中,我们会了C#中的继承,今天我们来了解了解Java中的继承,其实都大同小异啦! 1.语法 修饰符 SubClass extends SuperClass(){ //类定义部分 } e ...
- Javascript中void操作符
Javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. void操作符用法格式如下:1.javascript:void (expression)2.javascript ...
- ASP.NET Web API 特性
ASP.NET MVC 4 包含了 ASP.NET Web API, 这是一个创建可以连接包括浏览器.移动设备等多种客户端的 Http 服务的新框架, ASP.NET Web API 也是构建 RES ...
- asp.net webform 与mvc 共享session
公司内部系统最早是用.net webform模式开发的,现新项目用.net mvc 开发,现存在的问题就是如何保持原有.net webform的登录状态不变,而在mvc中能够验证用户的登录状态,也就是 ...
- CSS Hack(转)
做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况.基于此,某些情况我们会极不情愿的使用这个不太友好的方式来达到大家要求的页面表现.我个人是不太推荐使用hack的,要知道 ...
- 由于无法在数据库 'TestNonContainedDB' 上放置锁 ALTER DATABASE 失败
Error: 消息5601,级别16,状态1,第1行,由于无法在数据库 'TestNonContainedDB' 上放置锁,ALTER DATABASE 失败.请稍后再试.消息5069,级别16,状态 ...
- 【读书笔记】iOS网络-运行循环
运行循环是由类NSRunLoop表示的,有些线程可以让操作系统唤醒睡眠的线程以管理到来的事件,而运行循环则是这些线程的基本组件.运行循环是这样一种循环,可以在一个周期内调度任务并处理到来的事件.iOS ...
- 题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。
一.第一种写法 package com.pb.demo1; import java.util.Scanner; /** * 题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字. ...
- IOS开发之网络编程--文件压缩和解压缩
前言: QQ表情包就用到了解压缩,从网络下载的那么多表情文件格式并不是一个一个图片文件,而是多个图片压缩而成的表情压缩包.下面介绍的是iOS开发中会用到的压缩和解压缩的第三方框架的使用. 注意: 这个 ...