单向循环链表
单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环。和单链表相比,循环单链表的长处是从链尾到链头比较方便。当要处理的数据元素序列具有环型结构特点时,适合于采用循环单链表。
与单链表相同,循环单链表也有带头结点结构和不带头结点结构两种,带头结点的循环单链表实现插入和删除操作时,算法实现较为方便。
带头结点的循环单链表结构如下:
带头结点的循环单链表的操作实现方法和带头结点的单链表的操作实现方法类同,差别仅在于:
(1)在构造函数中,要加一条head.next = head 语句,把初始时的带头结点的循环单链表设计成图2-11 (a)所示的状态。
(2)在index(i)成员函数中,把循环结束判断条件current != null改为current != head。(见上篇博客)
public interface List {
// 获得线性表长度
public int size();
// 判断线性表是否为空
public boolean isEmpty();
// 插入元素
public void add(int index, Object obj) throws Exception;
// 删除元素
public void delete(int index) throws Exception;
// 获取指定位置的元素
public Object get(int index) throws Exception;
}
//节点类 public class Node { Object element; // 数据域 Node next; // 指针域 //头结点的构造方法 public Node(Node nextval){ this.next=nextval; } //非头结点的构造方法 public Node(Object obj,Node nextval){ this.element=obj; this.next=nextval; } public Object getElement() { return element; } public void setElement(Object element) { this.element = element; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } public String toString() { return this.element.toString(); } }
public class CycleLinkedList implements List {
Node head; // 头指针
Node current;// 当前结点对象
int size;// 结点个数
public CycleLinkedList(){
//初始化头结点,让头指针指向头结点。并且让当前结点对象等于头结点。
this.head = current = new Node(null);
this.size =0;//单向链表,初始长度为零。
this.head.next =this.head;
}
//定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点。
public void index(int index) throws Exception{
if(index<-1||index>this.size-1){
throw new Exception("参数错误!");
}
if(index==-1){
return;
}
this.current=this.head.next;
int j=0;
while(this.current!=this.head&&j<index){
this.current=this.current.next;
j++;
}
}
@Override
public int size() {
// TODO Auto-generated method stub
return this.size;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return this.size == 0;
}
@Override
public void add(int index, Object obj) throws Exception {
// TODO Auto-generated method stub
if (index < 0 || index > this.size) {
throw new Exception("参数错误!");
}
this.index(index - 1);
this.current.setNext(new Node(obj, this.current.next));
this.size++;
}
@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("参数错误!");
}
this.index(index-1);
this.current.setNext(this.current.next.next);
this.size--;
}
@Override
public Object get(int index) throws Exception {
// TODO Auto-generated method stub
if(index <-1 || index >size-1)
{
throw new Exception("参数非法!");
}
this.index(index);
return this.current.getElement();
}
}
双向循环链表
双向链表是每个结点除后继指针外还有一个前驱指针。和单链表类同,双向链表也有带头结点结构和不带头结点结构两种,带头结点的双向链表更为常用;另外,双向链表也可以有循环和非循环两种结构,循环结构的双向链表更为常用。
在双向链表中,每个结点包括三个域,分别是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表示要插入结点的位置,①、②表示实现删除过程的步骤。
public interface List {
// 获得线性表长度
public int size();
// 判断线性表是否为空
public boolean isEmpty();
// 插入元素
public void add(int index, Object obj) throws Exception;
// 删除元素
public void delete(int index) throws Exception;
// 获取指定位置的元素
public Object get(int index) throws Exception;
}
public class Node {
Object element; // 数据域
Node next; // 后继指针域
Node prior; // 前驱指针域
Node(Node nextval){
this.next=nextval;
}
Node(Object obj,Node nextval){
this.next=nextval;
this.element=obj;
}
// 获得当前结点的后继结点
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();
}
}
public class DoubleCycleLinkedList implements List {
Node head; // 头指针
Node current;// 当前结点对象
int size;// 结点个数
public DoubleCycleLinkedList(){
//初始化头结点,让头指针指向头结点。并且让当前结点对象等于头结点。
this.head = current = new Node(null);
this.size =0;
this.head.next =this.head;
this.head.prior=this.head;
}
//定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点。
public void index(int index) throws Exception{
if(index<-1||index>this.size-1){
throw new Exception("参数错误!");
}
if(index==-1){
return;
}
this.current=this.head.next;
int j=0;
while(this.current!=this.head&&j<index){
this.current=this.current.next;
j++;
}
}
@Override
public int size() {
// TODO Auto-generated method stub
return this.size;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return this.size == 0;
}
@Override
public void add(int index, Object obj) throws Exception {
// TODO Auto-generated method stub
if (index < 0 || index > this.size) {
throw new Exception("参数错误!");
}
this.index(index - 1);
this.current.setNext(new Node(obj, this.current.next));
this.current.next.setPrior(this.current);
this.current.next.next.setPrior(this.current.next);
this.size++;
}
@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("参数错误!");
}
this.index(index-1);
this.current.setNext(this.current.next.next);
this.current.next.setPrior(current);
this.size--;
}
@Override
public Object get(int index) throws Exception {
// TODO Auto-generated method stub
if(index <-1 || index >size-1)
{
throw new Exception("参数非法!");
}
this.index(index);
return this.current.getElement();
}
}
循环链表应用
游戏规则:N个人围成一个圈,从第一个人开始传花,当数到M时,该人退出游戏,直到剩下最后一个人。
//游戏类
public class Game {
//单向循环链表
CycleLinkedList list = new CycleLinkedList();
//总人数
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.add(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)+" ");
}
}
}