双向循环链表基本操作
存储结构类型定义
typedef struct DuLNode
{
ElemType data; //数据域
struct DuLNode *prior; //直接前驱
struct DuLNode *next; //直接后继
}DuLNode,*DuLinkList;
头插法
//头插法
void CreateList_H(DuLinkList *LHead,int n)
{
int i;
*LHead = (DuLinkList)malloc(sizeof(DuLNode)); //头结点
DuLNode * LTail = (DuLinkList)malloc(sizeof(DuLNode)); //尾结点
DuLNode *tmp_node; //临时结点(新插入结点)
(*LHead)->next = LTail; //头结点next指向尾结点
(*LHead)->prior = LTail; //头结点prior指向尾结点
LTail->next = *LHead; //尾结点next指向头结点
LTail->prior = *LHead; //尾结点指向头结点 进而首位相连形成一个双向循环
printf("头插法输入%d个元素: ",n);
scanf("%d",<ail->data); //循环外为尾结点输入数据先
for(i=0;i<n-1;i++)
{
tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //动态分配新节点空间单元
scanf("%d",&tmp_node->data); // 为新结点输入数据
tmp_node->next = (*LHead)->next; //新结点next指向后继结点
tmp_node->prior = (*LHead)->next->prior; //尾结点一直都在动,不能用之作为参考点,所以用头结点LHead指向自己
// (*LHead)->next = tmp_node; //头结点next指针重置,指向新结点
(*LHead)->next->prior = tmp_node; //原后继结点prior(使用固定的头结点表示)重置,指向新结点
(*LHead)->next = tmp_node; //此处做个致命错误标志,这两步不能乱
}
}
注意插入元素时指针域修改的先后顺序,例如:这里的倒数两行代码,先修改(*LHead)->next->prior = tmp_node; 再修改(*LHead)->next = tmp_node; 它们之间的关系prior指针需要头结点原来的next获得,如果先改了next就将出现指向错误和断链的现象。
尾插法
//尾插法
void CreateList_R(DuLinkList *LHead,int n)
{
int i;
*LHead = (DuLinkList)malloc(sizeof(DuLNode)); //开辟头结点内存单元
DuLNode *tmp_node; //定义一个临时结点指针(新插入结点指针)
(*LHead)->next = *LHead; //头结点next指向自己
(*LHead)->prior = *LHead; //头结点prior指向自己
printf("尾插法输入%d个元素: ",n);
for(i=0;i<n;i++)
{
tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //为新结点动态开辟空间,让新结点指针指向它
tmp_node->next = *LHead; //新插入结点next与头结点进行连接
tmp_node->prior = (*LHead)->prior; //新插入结点prior与原链尾连接 这两步其实就是将新插入结点的next和prior都连接到链尾
scanf("%d",&tmp_node->data); //为新结点输入数据
(*LHead)->prior->next = tmp_node; //使用头结点prior引出原链尾结点,并让它next指向新结点
(*LHead)->prior = tmp_node; //头结点prior指向新结点
}
}
遍历
//遍历
Status print(DuLinkList LHead)
{
DuLNode *tmp_node = LHead; //一开始指向头结点
if(LHead->next == LHead) return ERROR; //空链表返回ERROR
printf("当前链表:");
while(LHead != tmp_node->next) //尾结点的next域不等于头结点指针时循环继续
{
tmp_node = tmp_node->next; //循环体内每移动一项立即输出data,然后再判断就不会漏了
printf("%d ",tmp_node->data);
}
printf("\n");
return OK;
}
链表合并
//合并 A链合并到B链
Status Union(DuLinkList *LHead_a,DuLinkList *LHead_b)
{
if(((*LHead_a)->next == *LHead_a) || ((*LHead_b)->next == *LHead_b)) return ERROR;
(*LHead_a)->prior->next = (*LHead_b)->next; //A链链尾next指向B链首元结点
(*LHead_b)->next->prior = (*LHead_a)->prior; //B链链头prior与A链链尾相连
(*LHead_a)->prior = (*LHead_b)->prior; //A链头结点prior与B链链尾相连
(*LHead_b)->prior->next = *LHead_a; //B链尾结点next指向A链头结点
free(*LHead_b); //释放B链头结点内存单元
free(LHead_b); //释放B链头指针
}
插入结点
图解
头插
//插入(头插法)
void ListInsert_H(DuLinkList *LHead,ElemType e) //LHead头指针 指针
{
DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //新结点储存单元空间指针
tmp_node->data = e; //新结点输入数据
tmp_node->next = (*LHead)->next; //新结点next指向头结点指向的下一个结点
tmp_node->prior = (*LHead)->next->prior; //新结点prior指向头结点(也可以把*LHead->next->prior替换为*LHead)
(*LHead)->next->prior = tmp_node; //将原首元结点的prior指向换为新结点
(*LHead)->next = tmp_node; //将头结点next指向新结点
printf("元素%d 成功插头~\n",e);
}
尾插
//插入(尾插法)
void ListInsert_R(DuLinkList *LHead,ElemType e) //LHead头指针 指针
{
DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //开辟空间单元给新结点
tmp_node->data = e; //为新结点输入数据
tmp_node->next = *LHead; //新结点next指向头结点
tmp_node->prior = (*LHead)->prior; //新结点prior指向上一个元素(使用头结点的prior替代)
(*LHead)->prior->next = tmp_node; //原链尾next指向新结点
(*LHead)->prior = tmp_node; //头结点prior指向新结点
printf("元素%d 成功插尾~\n",e);
}
中间插
//插入到表中间
Status ListInsert_C(DuLinkList LHead,int n,ElemType e)
{
DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //新界结点指针并为之开辟空间
DuLNode *p = GetElem(LHead,n); ///按位序找指针
// printf("%d",p->data);
if(!p) return ERROR; //如果返回指针有误就结束插入操作
tmp_node->data = e; //新结点输入数据
tmp_node->next = p; //新结点next指针指向查找原位置指针p
tmp_node->prior = p->prior; //新结点prior指向原位置结点的prior的指针
p->prior->next = tmp_node; //原位p结点的上一个结点的next指向新结点
p->prior = tmp_node; //原位置结点的prior指向新结点
printf("元素%d 成功插入中间~\n",e);
return OK;
}
整合到同一个函数
实现一个函数插三个位置。
//插入 整合了头尾中间
Status ListInsert(DuLinkList *LHead,int n,ElemType e)
{
DuLNode *tmp_node; //新结点指针
if(n==1) ListInsert_H(LHead,e); //首先判断插入位置为1则是查到链头,所以用插头ListInsert_H
tmp_node = GetElem(*LHead,n); //按位找该位置结点指针
if(!tmp_node) return ERROR; //返回结点有误则结束
if(tmp_node->next == *LHead) ListInsert_R(LHead,e); //如果该位置是链尾直接用插尾函数插入
ListInsert_C(*LHead,n,e); //‘剩下的就是插中间
return OK;
}
按结点位置查找
//按位查找
DuLNode *GetElem(DuLinkList LHead,int n)
{
int j=1; //这里从首元结点开始查找,j设置为1
DuLNode *tmp_node = LHead->next; //指向首元结点
while(tmp_node!=LHead && j<n) //循环条件:链表非空——next域不指向自己,取值位置合理
{
tmp_node = tmp_node->next; //指针指向下一个
j++; //位置计数加一
}
if(tmp_node==LHead && j>=n) return ERROR; //空链以及取值位置不合理返回ERROR
return tmp_node; //返回查找位置 指针
}
删除
//删除
Status ListDetele(DuLinkList *LHead,int n)
{
int i;
DuLNode *tmp_node;
tmp_node = (*LHead)->next; //指向首元结点
for(i=1;i<n;i++)
tmp_node = tmp_node->next; //for循环遍历查找第n个结点
tmp_node->next->prior = tmp_node->prior; //第n个结点后一个的prior指向第n前一个结点指针
tmp_node->prior->next = tmp_node->next; //同理,第n个前一个next指针指向第n个结点后一个
free(tmp_node); //释放第n结点的内存单元空间
printf("成功删除位置%d的元素~\n",n);
}
代码整合进行测试
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;
typedef struct DuLNode
{
ElemType data; //数据域
struct DuLNode *prior; //直接前驱
struct DuLNode *next; //直接后继
}DuLNode,*DuLinkList;
//头插法
void CreateList_H(DuLinkList *LHead,int n)
{
int i;
*LHead = (DuLinkList)malloc(sizeof(DuLNode)); //头结点
DuLNode * LTail = (DuLinkList)malloc(sizeof(DuLNode)); //尾结点
DuLNode *tmp_node; //临时结点(新插入结点)
(*LHead)->next = LTail; //头结点next指向尾结点
(*LHead)->prior = LTail; //头结点prior指向尾结点
LTail->next = *LHead; //尾结点next指向头结点
LTail->prior = *LHead; //尾结点指向头结点 进而首位相连形成一个双向循环
printf("头插法输入%d个元素: ",n);
scanf("%d",<ail->data); //循环外为尾结点输入数据先
for(i=0;i<n-1;i++)
{
tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //动态分配新节点空间单元
scanf("%d",&tmp_node->data); // 为新结点输入数据
tmp_node->next = (*LHead)->next; //新结点next指向后继结点
tmp_node->prior = (*LHead)->next->prior; //尾结点一直都在动,不能用之作为参考点,所以用头结点LHead指向自己
// (*LHead)->next = tmp_node; //头结点next指针重置,指向新结点
(*LHead)->next->prior = tmp_node; //原后继结点prior(使用固定的头结点表示)重置,指向新结点
(*LHead)->next = tmp_node; //此处做个致命错误标志,这两步不能乱
}
}
//尾插法
void CreateList_R(DuLinkList *LHead,int n)
{
int i;
*LHead = (DuLinkList)malloc(sizeof(DuLNode)); //开辟头结点内存单元
DuLNode *tmp_node; //定义一个临时结点指针(新插入结点指针)
(*LHead)->next = *LHead; //头结点next指向自己
(*LHead)->prior = *LHead; //头结点prior指向自己
printf("尾插法输入%d个元素: ",n);
for(i=0;i<n;i++)
{
tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //为新结点动态开辟空间,让新结点指针指向它
tmp_node->next = *LHead; //新插入结点next与头结点进行连接
tmp_node->prior = (*LHead)->prior; //新插入结点prior与原链尾连接 这两步其实就是将新插入结点的next和prior都连接到链尾
scanf("%d",&tmp_node->data); //为新结点输入数据
(*LHead)->prior->next = tmp_node; //使用头结点prior引出原链尾结点,并让它next指向新结点
(*LHead)->prior = tmp_node; //头结点prior指向新结点
}
}
//遍历
Status print(DuLinkList LHead)
{
DuLNode *tmp_node = LHead; //一开始指向头结点
if(LHead->next == LHead) return ERROR; //空链表返回ERROR
printf("当前链表:");
while(LHead != tmp_node->next) //尾结点的next域不等于头结点指针时循环继续
{
tmp_node = tmp_node->next; //循环体内每移动一项立即输出data,然后再判断就不会漏了
printf("%d ",tmp_node->data);
}
printf("\n");
return OK;
}
//按位查找
DuLNode *GetElem(DuLinkList LHead,int n)
{
int j=1; //这里从首元结点开始查找,j设置为1
DuLNode *tmp_node = LHead->next; //指向首元结点
while(tmp_node!=LHead && j<n) //循环条件:链表非空——next域不指向自己,取值位置合理
{
tmp_node = tmp_node->next; //指针指向下一个
j++; //位置计数加一
}
if(tmp_node==LHead && j>=n) return ERROR; //空链以及取值位置不合理返回ERROR
return tmp_node; //返回查找位置 指针
}
//插入(尾插法)
void ListInsert_R(DuLinkList *LHead,ElemType e) //LHead头指针 指针
{
DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //开辟空间单元给新结点
tmp_node->data = e; //为新结点输入数据
tmp_node->next = *LHead; //新结点next指向头结点
tmp_node->prior = (*LHead)->prior; //新结点prior指向上一个元素(使用头结点的prior替代)
(*LHead)->prior->next = tmp_node; //原链尾next指向新结点
(*LHead)->prior = tmp_node; //头结点prior指向新结点
printf("元素%d 成功插尾~\n",e);
}
//插入(头插法)
void ListInsert_H(DuLinkList *LHead,ElemType e) //LHead头指针 指针
{
DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //新结点储存单元空间指针
tmp_node->data = e; //新结点输入数据
tmp_node->next = (*LHead)->next; //新结点next指向头结点指向的下一个结点
tmp_node->prior = (*LHead)->next->prior; //新结点prior指向头结点(也可以把*LHead->next->prior替换为*LHead)
(*LHead)->next->prior = tmp_node; //将原首元结点的prior指向换为新结点
(*LHead)->next = tmp_node; //将头结点next指向新结点
printf("元素%d 成功插头~\n",e);
}
//插入到表中间
Status ListInsert_C(DuLinkList LHead,int n,ElemType e)
{
DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode)); //新界结点指针并为之开辟空间
DuLNode *p = GetElem(LHead,n); ///按位序找指针
// printf("%d",p->data);
if(!p) return ERROR; //如果返回指针有误就结束插入操作
tmp_node->data = e; //新结点输入数据
tmp_node->next = p; //新结点next指针指向查找原位置指针p
tmp_node->prior = p->prior; //新结点prior指向原位置结点的prior的指针
p->prior->next = tmp_node; //原位p结点的上一个结点的next指向新结点
p->prior = tmp_node; //原位置结点的prior指向新结点
printf("元素%d 成功插入中间~\n",e);
return OK;
}
//插入 整合了头尾中间
Status ListInsert(DuLinkList *LHead,int n,ElemType e)
{
DuLNode *tmp_node; //新结点指针
if(n==1) ListInsert_H(LHead,e); //首先判断插入位置为1则是查到链头,所以用插头ListInsert_H
tmp_node = GetElem(*LHead,n); //按位找该位置结点指针
if(!tmp_node) return ERROR; //返回结点有误则结束
if(tmp_node->next == *LHead) ListInsert_R(LHead,e); //如果该位置是链尾直接用插尾函数插入
ListInsert_C(*LHead,n,e); //‘剩下的就是插中间
return OK;
}
//删除
Status ListDetele(DuLinkList *LHead,int n)
{
int i;
DuLNode *tmp_node;
tmp_node = (*LHead)->next; //指向首元结点
for(i=1;i<n;i++)
tmp_node = tmp_node->next; //for循环遍历查找第n个结点
tmp_node->next->prior = tmp_node->prior; //第n个结点后一个的prior指向第n前一个结点指针
tmp_node->prior->next = tmp_node->next; //同理,第n个前一个next指针指向第n个结点后一个
free(tmp_node); //释放第n结点的内存单元空间
printf("成功删除位置%d的元素~\n",n);
}
//合并 A链合并到B链
Status Union(DuLinkList *LHead_a,DuLinkList *LHead_b)
{
if(((*LHead_a)->next == *LHead_a) || ((*LHead_b)->next == *LHead_b)) return ERROR;
(*LHead_a)->prior->next = (*LHead_b)->next; //A链链尾next指向B链首元结点
(*LHead_b)->next->prior = (*LHead_a)->prior; //B链链头prior与A链链尾相连
(*LHead_a)->prior = (*LHead_b)->prior; //A链头结点prior与B链链尾相连
(*LHead_b)->prior->next = *LHead_a; //B链尾结点next指向A链头结点
free(*LHead_b); //释放B链头结点内存单元
free(LHead_b); //释放B链头指针
}
//测试
int main()
{
DuLinkList LHead_a,LHead_b; //定义两条链的头指针
printf("------头插法---------\n");
CreateList_H(&LHead_a,3);
print(LHead_a);
printf("\n------尾插法--------\n");
CreateList_R(&LHead_b,3);
print(LHead_b);
printf("\n------链表合并------\n");
Union(&LHead_a,&LHead_b);
print(LHead_a);
printf("\n---头插法插入 10---\n");
ListInsert_H(&LHead_a,10);
print(LHead_a);
printf("\n---尾插法插 5---\n");
ListInsert_R(&LHead_a,5);
print(LHead_a);
printf("\n------按位查找------\n");
DuLNode *p = GetElem(LHead_a,2);
printf("第二个结点数据: %d\n",p->data);
printf("\n---插入 100 到链表中间---\n");
ListInsert(&LHead_a,4,100);
print(LHead_a);
printf("\n-----链表删除--------\n");
ListDetele(&LHead_a,2);
print(LHead_a);
}