数据结构-单向循环链表(初始化、插入结点、删除结点等操作)
单向循环链表
如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链表,通俗讲就是把尾节点的下一跳指向头结点。
注:
- 我这个代码头节点即为首节点
- 通过判断当前结点的next结点是否与head结点相等,即可判断是否对单向循环链表遍历完成;
#include <stdio.h>
#include <stdlib.h>
/ 表示是一个节点
typedef struct Node
{
int data; // 代表节点中的数据
struct Node* pnext; // 代表下一个节点的起始地址
}NODE;
//创建一个节点
NODE* creatlist(int num)
{
NODE* pnode= (NODE*)malloc(sizeof(NODE));
if(pnode != NULL)
{
pnode->data=num;
pnode->pnext=NULL;
}
return pnode;
}
//查找尾节点
NODE* findTail(NODE* phead)
{
if(phead==NULL)
{
return NULL;
}
//NODE* p=(NODE*)malloc(sizeof(NODE));
//p = phead;
NODE* p1=phead;
while(p1->pnext!=phead)
{
p1=p1->pnext;
}
return p1;
}
//头插法
NODE* insertHead(NODE* phead,int num)
{
NODE* pnew=creatlist(num);
NODE* ptail=findTail(phead);//查找尾节点,
//用于将尾节点的pnext指向新的头结点
if(pnew == NULL)
{
return phead;
}
if(phead == NULL)
{
pnew->pnext=pnew; 自己指向自己
phead=pnew; 头指针指向新节点
}
else
{
ptail->pnext=pnew; 将尾节点的pnext指向新的头结点
pnew->pnext=phead; 链接新节点和头节点
phead= pnew; 头指针指向新节点
}
return phead;
}
//尾插法
NODE* insertTail(NODE* phead,int num)
{
NODE* pnew = creatlist(num);
NODE* ptail = findTail(phead);
if(pnew == NULL)
{
return phead;
}
if(ptail == NULL)
{
pnew->pnext=pnew;
phead=pnew;
}
else
{
ptail->pnext=pnew; //尾节点的pnext指向新节点
pnew->pnext=phead; //新节点的pnext指向头节点
}
return phead;
}
//计链表节点数
int countNode(NODE* phead)
{
int n=0;
if(phead == NULL)
{
return 0;
}
NODE* p1=phead;
do
{
n =n+1;
p1=p1->pnext; p1代替头节点偏移,偏移多少次,计数多少次
}while(phead!=p1);
return n;
}
//获取对应节点之前的节点
NODE* getNode(NODE* phead,int npos)
{
NODE* p = phead;
if(p== NULL)
{
return NULL;
}
for(int i=0;i<npos-1;i++) //对应节点之前的节点,所以为npos-1
{
p= p->pnext; //此时p指向偏移i次以后的节点
}
return p;
}
//任意插法
NODE* insertNode(NODE* phead,int npos,int num)
{
int l = countNode(phead);
if(npos<0 || npos>l)
{
printf("insert pos %d is valid!\n",npos);
return phead;
}
else if(npos==0) //插入位从0位开始数,所以头插法应为npos=0
{
phead = insertHead(phead,num);
}
else if(npos==l)
{
phead = insertTail(phead,num);
}
else if(npos>0 && npos<l)
{
NODE* pnew = creatlist(num);
if(pnew == NULL)
{
return phead;
}
if(phead == NULL)
{
pnew->pnext=pnew; //空链表时,新节点的pnext指向自己
phead=pnew; //头指针指向pnew
return phead;
}
NODE* p=getNode(phead,npos); //获取对应节点之前的节点
NODE* pa=p->pnext; //原节点待插入位置的pnext节点
p->pnext=pnew;
pnew->pnext=pa;
}
return phead;
}
//删除头结点
NODE* deleteHead(NODE* phead)
{
NODE* p1=phead->pnext;
if (phead == NULL)
{
return NULL;
}
NODE* ptail=findTail(phead); ///查找尾节点的函数一定要放在phead上
phead=p1;
ptail->pnext=phead;
return phead;
}
//删除尾节点
NODE* deleteTail(NODE* phead)
{
int npos=countNode(phead);
NODE* pa=getNode(phead,npos-1);
NODE* ptail=findTail(phead);
pa->pnext=phead;
free(ptail);
return phead;
}
//删除任意节点
NODE* deleteNode(NODE* phead,int npos)
{
int len=countNode(phead);
if(npos<0 || npos>len)
{
printf("删除失败,位置不存在\n");
}
else if(npos==0) //删除位从0位开始数,所以删除头结点应为npos=0
{
phead = deleteHead(phead);
}
else if(npos==len)
{
phead = deleteTail(phead);
}
else
{
NODE* pa=getNode(phead,npos); ///a,b,c
NODE* pb=pa->pnext;
pa->pnext=pb->pnext;
free(pb); /释放堆内存
}
return phead;
}
打印当前链表
void display(NODE* phead)
{
if(phead == NULL)
{
printf("list is NULL.\n");
}
NODE* p1=phead;
do
{
printf("%d ",p1->data);
p1 = p1->pnext;
}while(phead!=p1);
printf("\n");
}
int main(int argc,char *argv[])
{
NODE* phead = (NODE*)malloc(sizeof(NODE));
phead = creatlist(2);
phead->pnext=phead;
display(phead);
printf("头插法后输出:\n");
phead = insertHead(phead,1);
display(phead);
printf("尾插法后输出:\n");
phead = insertTail(phead,3);
display(phead);
printf("任意插法后输出:\n");
phead = insertNode(phead,2,4);
phead = insertNode(phead,1,8);
display(phead);
/*/
printf("删除头节点后输出:\n");
phead = deleteHead(phead);
display(phead);
printf("删除尾节点后输出:\n");
phead = deleteTail(phead);
display(phead);
/*/
printf("删除任意节点后输出:\n");
phead = deleteNode(phead,4);
//phead = deleteNode(phead,3); 删除位从0位开始数
display(phead);
return 0;
}