循环单链表和普通单链表的代码操作上差别并不大,差别最大的地方在于普通单链表遍历结束的标志是标记指针为空,而循环单链表遍历结束的条件是标记指针不等于头指针。
以下是头文件,其中包括对节点类型额定义,以及循环单链表的函数声明:
#ifndef CLIST_H_
#define CLIST_H_
typedef struct CNode /*这个结构体类型用于定义循环单链表的节点*/
{
int iData;
struct CNode *pNext;
}CNode, *CList;
typedef struct DulNode{ /*这个类型定义用来定义双向链表的节点*/
int data;
struct DulNode *prior;
struct DulNode *next;
}DulNode, *DuLinkList;
CNode* BuyNode(int val);
void InitList(CList plist); //初始化链表
void HeadInsert(CList plist, int val); //头插法
void TailInsert(CList plist, int val); //尾插法
bool Delete(CList plist, int key); //删除
void PrintList(CList plist); //打印
int GetLength(CList plist); //获取链表长度
CNode *Search(CList plist, int key); //查找
void Traverse(CList plist); //倒置
void Destroy(CList plist); //摧毁
#endif
下面是循环单链表的一些操作的具体实现代码:
1. 首先必须有函数来生成一个新的节点,并返回指向它的指针:
具体的思路是: 先用函数参数接受一个数据i.
再新开辟一个节点,并且把数据i存入节点,
最后把节点的指针返回。
CList BuyNode(int vail){
/* 这个函数用来申请一个节点,并返回其地址*/
CList i = (CList)malloc(sizeof(CNode));
i->iData = vail;
i->pNext = NULL;
return i;
}
2. 初始化操作,使得节点收尾相连。
思路: 将节点的后继指针指向自己。
void InitList(CList p){
/* 这个函数是用来初始化列表的,是列表成环*/
if(p == NULL){
printf("该列表不存在!!");
}
p->pNext = p;
}
3. 头插法,不断插入新的节点进入循环单链表。
思路: 先接受一个数据i和一个链表的头指针。
使用数据开辟一个新的节点,
将新的节点的后继指针指向头指针的后继指针,
将头指针的后继指针指向新节点。
void HeadInsert(CList p, int x){
/*这个函数用来在循环列表的开头处插入新的节点*/
if(p == NULL){
printf("这个循环列表是空的!");
}
CList newPlist = BuyNode(x);
newPlist->pNext = p->pNext;
p->pNext = newPlist;
}
4. 尾插法,不断插入新的节点到结尾处。
思路: 先接收一个数据i,
使用数据i创建一个新的节点,
用一个新的指针指向头指针的后继指针,并不断循环遍历,
遍历到最后一个时停住这个标记指针,
将这个标记指针的后继指针指向新节点,
再将新节点的后继指针指向头结点,结束。
void TailInsert(CList p, int x){
/*这个函数用于在循环列表的尾部插入新的节点*/
if(p == NULL){
printf("这个循环列表不存在!!");
}
CList newPlist = BuyNode(x);
CList pRear = p->pNext;
for(;pRear->pNext != p;pRear = pRear->pNext){
NULL;
}
newPlist->pNext = p;
pRear->pNext = newPlist;
pRear = NULL;
}
5. 删除操作,删除掉存有指定数据的节点。
思路: 使用函数参数接受一个链表头指针p和一个关键数据i,
使用一个新的指针作为标记指针指向头结点的后继节点,
再使用一个后缀指针跟着标记指针(始终跟在标记指针后面),
通过遍历使标记指针指向要删除的目标节点,后缀指针紧随其后。
将后缀的指针的后继节点指向标记指针的后继节点,
释放标记指针。
void DeletList(CList p, int key){
/*这个函数用来删除值为key的节点*/
if(p == NULL){
printf("这个列表不存在!");
}
CList pPrior = p;
CList pCur = p->pNext;
for(;pCur->pNext != p;pCur = pCur->pNext){
if(pCur->iData == key){
pPrior->pNext = pCur->pNext;
free(pCur);
printf("删除成功!!");
break;
}
pPrior = pCur;
}
printf("没有找到要删除的点!!");
}
6. 打印操作,将循环链表内的内容遍历式的打印出来。
思路: 函数参数接受一个链表的头指针,
使用一个标记指针指向头指针的后继指针,
通过遍历的方式打印出每一个节点的数据。
void printList(CList p){
/* 这个函数是用来打印循环列表的*/
if(NULL == p){
printf("该列表不存在,固无法打印!!");
}
CList r = p->pNext;
int i = 1;
for(;r!=p;r=r->pNext){
printf("第%d个节点的数据是: %d\n",i,r->iData);
i++;
}
}
7. 查询操作,用于查找关键数据的节点。
思路: 函数参数接收一个数据i,
使用一个标记指针指向头结点的后继指针,
标记指针不断遍历,
当出现符合关键数据的节点时,返回该节点。
CList SearchList(CList plist, int key){
if(plist == NULL){
printf("该链表为空白!!");
}
CList p_pos = plist->pNext;
while(p_pos != plist){
if(p_pos->iData == key){
printf("查找成功!!");
return p_pos;
}
}
printf("不存在需要查找的目标节点!!");
}
----------------------------------------------------------下面是双向链表 ---------------------------------------------------
普通单链表和双向链表在一些操作上没什么区别,比如查询操作,打印操作,获取节点数据操作,这些操作只需要单方向的进行遍历,所以跟单链表差别不大,而像删除和插入操作,由于引进了前级指针,所以具体的操作上有了一些变化。
以下是双向链表的插入和删除操作:
bool DeletDuNode(DuLinkList p, int i){
/*该函数使用来删除值为i的节点*/
if(p == NULL){
printf("该列表不存在!!");
return false;
}
DuLinkList k = p->next;
for(;k!=NULL;k=k->next){
if(k->data == i){
k->prior->next = k->next;
k->next->prior = k->prior;
free(k);
printf("值为i的节点删除成功!!");
return true;
}
}
printf("删除失败!!");
return false;
}
bool InsertDuNode(DuLinkList p, int i){
/*该函数用来插入指定的数字到头部*/
if(p == NULL){
printf("该链表为空!!");
return false;
}
DuLinkList newNode = creatOne(3);
newNode->prior = p;
newNode->next = p->next;
p->next->prior = newNode;
p->next = newNode;
printf("节点插入成功!!");
return true;
}