C语言实现双向循环链表
本篇是关于双向循环链表的简单实现,有错漏的地方请在评论区评价。
双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。
结构体
/* 线性表的双向链表存储结构 */
typedef struct DulNode{
ElemType data; //数据域
struct DulNode *prior; //直接前驱指针
struct DulNode *next; //直接后继指针
}DulNode, *DulLinkList;
简单实现
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define FALSE 0
#define TRUE 1
typedef int ElemType;
typedef int Status;
/* 双向循环链表 */
typedef struct DulNode{
ElemType data; //数据域
struct DulNode* prior; //直接前驱
struct DulNode* next; //直接后继
}DulNode;
typedef struct DulNode* LinkList;
/* 遍历方法 */
void display(LinkList list){
LinkList p = list;
int i=1;
while(p->next != list){
p = p->next;
printf("第%d个数值是%d\n",i++,p->data);
}
}
/* 初始化函数 */
LinkList initList(int n){
int i;
LinkList list = (LinkList)malloc(sizeof(DulNode)); //头结点
list->data = 0;
list->prior = list;
list->next = list;
LinkList p = list;
printf("输入%d个数值(空格分隔):\n",n);
for(i=0;i<n;i++){
LinkList newNode = (LinkList)malloc(sizeof(DulNode)); //生成新结点
scanf("%d",&newNode->data);
newNode->prior = p;
newNode->next = NULL;
p->next = newNode;
p = p->next;
}
p->next = list;
list->prior = p;
list->data = n;
return list;
}
/* 插入 */
Status insertElem(LinkList list, int n ,ElemType e){
if(n<1){
printf("索引值小于1,非法插入!\n");
return ERROR;
}else if(n>list->data+1){
printf("索引值大于链表最大长度,非法插入!\n");
return ERROR;
}else{
LinkList p = list;
while(--n){
p = p->next;
}
LinkList newNode = (LinkList)malloc(sizeof(DulNode)); //新结点
newNode->data = e;
p->next->prior = newNode;
newNode->next = p->next;
newNode->prior = p;
p->next = newNode;
list->data++;
return TRUE;
}
}
//查找
Status searchElem(LinkList list, int n, ElemType *e){
if(n<1){
printf("索引值小于1,非法查询!\n");
return ERROR;
}else if(n>list->data){
printf("索引值大于链表长度,非法查询!\n");
return ERROR;
}else{
LinkList p = list;
while(n--){
p = p->next;
}
*e = p->data;
return TRUE;
}
}
/* 单个删除 */
Status deleteElem(LinkList list, int n){
if(n<1){
printf("索引值小于1,非法删除!\n");
return ERROR;
}else if(n>list->data){
printf("索引值大于链表长度,非法删除!\n");
return ERROR;
}else{
LinkList p = list;
while(--n){
p = p->next;
}
LinkList q = p->next;
q->next->prior = p;
q->prior = NULL;
p->next = q->next;
free(q);
list->data--;
return TRUE;
}
}
/* 全表删除 */
void deleteList(LinkList list){
LinkList p;
while(list->next != list){
p = list->next;
list->next = p->next;
p->prior = NULL;
p->next->prior = list;
p->next = NULL;
free(p);
}
list->data = 0;
}
/* 主函数 */
int main(){
int initNum; //初始化个数
LinkList list;
int insertSize,insertNum; //输入的位置和数值
int searchSize,searchNum; //查找的位置
ElemType *e = &searchNum;
int deleteSize; //删除位置
Status continueFlag = TRUE; //默认循环
int functionNum; //功能号
while(continueFlag){
printf("---------菜单----------\n");
printf("--------1.初始化-------\n");
printf("--------2.查找---------\n");
printf("--------3.插入---------\n");
printf("--------4.单个删除-----\n");
printf("--------5.全表删除-----\n");
printf("--------6.退出---------\n");
printf("输入选择的功能号:\n");
scanf("%d",&functionNum);
switch(functionNum){
case 1:
printf("输入要输入的数值个数:\n");
scanf("%d",&initNum);
list = initList(initNum);
display(list);
break;
case 2:
//查找
printf("输入要查找的元素位置:\n");
scanf("%d",&searchSize);
if(searchElem(list,searchSize,e)){
printf("查找成功!第%d个数值是%d\n",searchSize,*e);
}
break;
case 3:
//插入
printf("输入要插入的位置和数值(空格分隔):\n");
scanf("%d %d",&insertSize,&insertNum);
if(insertElem(list,insertSize,insertNum)){
printf("插入成功!\n");
display(list);
}
break;
case 4:
//单个删除
printf("输入要删除的数值位置:\n");
scanf("%d",&deleteSize);
if(deleteElem(list,deleteSize)){
printf("删除成功!\n");
display(list);
}
break;
case 5:
//整表删除
deleteList(list);
printf("删除成功!\n");
printf("当前链表长度:%d\n",list->data);
break;
case 6:
continueFlag = FALSE;
printf("感谢使用!");
break;
default:
printf("功能号不存在!\n");
break;
}
}
return 0;
}
简单总结
双向链表相对于单链表来说,要更复杂一些,因为多了prior指针,对于插入和删除,需要格外小心。另外它由于每个结点都需要记录两份指针,所以在空间上是要占用多一点空间的。不过,由于它有良好的对称性,使得对某个结点的前后结点的操作,带来了方便,可以有效提高算法的时间性能。说白了,就是用空间换时间。