循环链表
一、定义
将单链表中终端结点的指针端由空指针改为指向链表的第一个结点,这就使单链表形成一个环,这种头尾相接的单链表称为循环链表(circular linked list)。循环链表相对于单向链表的最大优点在于:通过链表中任意的结点地址便可遍历整个链表。
二、代码(不含头结点)
#ifndef __CIRCULARLINKEDLIST_H
#define __CIRCULARLINKEDLIST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 30
#define OK 1
#define ERROR 0
typedef int states;
typedef struct {
int id;
char name[MAX_SIZE];
}ElementType;
/** 循环链表结点结构 */
typedef struct cirNode{
ElementType data; //数据域
struct cirNode *next; //指针域
}CirNode;
/** 循环链表表头结构 */
typedef struct {
CirNode *headptr;
int length;
}CircularLinkedList;
/** 初始化循环链表 */
states InitCircularLinkedList(CircularLinkedList *ptrl);
/** 向循环链表中插入元素(按位置下标) */
states InsertCircularLinkedList(CircularLinkedList *ptrl,int pos,ElementType x);
/** 从循环链表中删除元素(按照位置下标删除) */
states DeleteCirLinkedList(CircularLinkedList *ptrl,int pos,ElementType *x);
/** 获取循环链表结点个数 */
int GetCirLinkedListLength(CircularLinkedList *ptrl);
/** 清空循环链表 */
states ClearCirLinkedList(CircularLinkedList *ptrl);
/** 根据元素内容返回链表中相应结点地址 */
states GetCirNodePtr(CircularLinkedList *ptrl,ElementType x,CirNode **nodeptr);
/** 通过某一结点地址,遍历整个循环链表 */
states PrintfCirListByNodeptr(CircularLinkedList *ptrl,CirNode *nodeptr);
/** 打印循环链表 */
void PrintfCirLinkedList(CircularLinkedList *ptrl);
#endif /*__CIRCULARLINKEDLIST_H */
#include "circularlinkedlist.h"
/** 初始化循环链表 */
states InitCircularLinkedList(CircularLinkedList *ptrl)
{
ptrl->headptr = NULL;
ptrl->length = 0;
return OK;
}
/** 向循环链表中插入元素(按位置下标插入) */
states InsertCircularLinkedList(CircularLinkedList *ptrl,int pos,ElementType x)
{
int i;
CirNode *newNode, *tempNode;
//判断输入的pos范围是否正确
if(pos < 1 || pos > ptrl->length + 1)
return ERROR;
newNode = (CirNode *)malloc(sizeof(CirNode));
newNode->data = x;
newNode->next = NULL;
//分情况讨论
if(pos == 1){
//pos == 1时,需要考虑改变链表末尾结点next指针的指向
newNode->next = ptrl->headptr;
ptrl->headptr = newNode;
ptrl->length++;
tempNode = ptrl->headptr;
for(i = 1;i < ptrl->length;i++){
tempNode = tempNode->next;
}
tempNode->next = ptrl->headptr;
return OK;
}
tempNode = ptrl->headptr;
for(i = 1;i < pos - 1;i++){
tempNode = tempNode->next;
}
newNode->next = tempNode->next;
tempNode->next = newNode;
ptrl->length++;
return OK;
}
/** 从循环链表中删除元素(按照位置下标删除) */
states DeleteCirLinkedList(CircularLinkedList *ptrl,int pos,ElementType *x)
{
int i;
CirNode *delNode, *tempNode;
//判断循环链表是否为空
if(!ptrl->headptr || ptrl->length == 0)
return ERROR;
//判断输入的pos变量范围是否正确
if(pos < 1 || pos > ptrl->length)
return ERROR;
//分两种情况进行讨论
if(pos == 1){
//需要考虑改变链表中最后一个结点的next指针指向
delNode = ptrl->headptr;
ptrl->headptr = delNode->next;
ptrl->length--;
*x = delNode->data;
free(delNode);
tempNode = ptrl->headptr;
for(i = 1;i < ptrl->length;i++){
tempNode = tempNode->next;
}
tempNode->next = ptrl->headptr;
return OK;
}
tempNode = ptrl->headptr;
for(i = 1;i < pos - 1;i++){
tempNode = tempNode->next;
}
delNode = tempNode->next;
tempNode->next = delNode->next;
*x = delNode->data;
free(delNode);
ptrl->length--;
return OK;
}
/** 获取循环链表结点个数 */
int GetCirLinkedListLength(CircularLinkedList *ptrl)
{
return ptrl->length;
}
/** 清空循环链表 */
states ClearCirLinkedList(CircularLinkedList *ptrl)
{
CirNode *p,*q;
p = ptrl->headptr;
if(!p) //链表为空,退出
return ERROR;
while(p->next != ptrl->headptr){
q = p->next;
free(p);
p = q;
}
free(p);
ptrl->headptr =NULL;
ptrl->length = 0;
return OK;
}
/** 根据元素内容返回链表中相应结点地址 */
states GetCirNodePtr(CircularLinkedList *ptrl,ElementType x,CirNode **nodeptr)
{
int i;
CirNode *node = ptrl->headptr;
for(i = 0;i < ptrl->length;i++){
if(x.id == node->data.id)
{
if(strcmp(x.name,node->data.name) == 0)
{
*nodeptr = node;
return OK;
}
}
node = node->next;
}
return ERROR;
}
/** 通过某一结点地址,遍历整个循环链表 */
states PrintfCirListByNodeptr(CircularLinkedList *ptrl,CirNode *nodeptr)
{
CirNode *tempNode = nodeptr;
while(tempNode->next != nodeptr){
printf("[%d %s] ",tempNode->data.id,tempNode->data.name);
tempNode = tempNode->next;
}
printf("[%d %s] ",tempNode->data.id,tempNode->data.name);
return OK;
}
/** 打印循环链表 */
void PrintfCirLinkedList(CircularLinkedList *ptrl)
{
int i;
CirNode *node = ptrl->headptr;
for(i = 0;i < ptrl->length;i++){
printf("[%d %s] ",node->data.id,node->data.name);
node = node->next;
}
}
三、编程笔记
- 循环链表与单链表的代码的不同点
1、链表最后一个结点的指针域要指向链表的第一个结点地址。
2、循环链表遍历的结束条件改变。whie(node->next != ptrl->headptr); 跳出循环条件的为链表最后一个结点的地址。 - 向循环链表中插入元素(按位置下标插入)
1、判断输入的pos范围是否正确;(1 - ptrl->length + 1)
2、建立将要插入循环链表的结点。
3、分情况讨论
pos == 1时,插入的位置为1时,需要改变ptrl->headptr变量的值,又因为是循环链表,考虑链表的最后一个结点的指针域要指向链表的第一个结点地址。
pos > 1时,操作与单链表相同。找到位于pos - 1位置的结点地址。插入新建结点,注意结点指针域的改变顺序。 - 从循环链表中删除元素(按照位置下标删除)
1、判断链表是否为空。
2、判断输入的pos范围是否正确。(1 - ptrl->length)
3、分情况讨论
pos == 1时,删除第一个结点之后,ptrl->headptr 和链表最后结点的指针域要改变。并将删除结点的数据域取出。
pos > 1时,与单链表的删除操作相同。