C语言 有序双链表实现插入、删除、打印(正反)等简单操作

本文介绍了C语言实现有序双链表的插入、删除和正反向打印操作。讨论了双链表节点结构,包括根指针的特殊处理和四种不同插入情况。此外,优化了插入节点的代码,减少了冗余,并说明了删除节点的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.双链表与单链表的区别主要是在于双链表中,每个节点都包含两个指针——指向前一个节点的指针,和指向后一个节点的指针。这就便于我们从任何方向遍历整个链表。

下面是节点类型的说明:

typedef struct NODE{
	struct NODE *fwd;
	struct NODE *bwd;
	int value;
}Node;
构造了一个简单的链表节点,此时,我们需要两个根指针,一个指向链表的第一个节点,另一个指向链表的最后一个节点。

我们可以考虑为根指针声明一个完整的节点,但应该注意其value字段不被使用,我们仅仅是用该节点来表示根指针,其并不存在于链表之中,我们让rootp->fwd表示链表第一个节点,而rootp->指向链表最后一个节点,这就需要在main函数中为该节点分配空间,且初始化rootp->fwd以及rootp->bwd为NULL。

Node *rootp; 
rootp=(Node *)malloc(sizeof(Node)); //为根指针分配空间,并初始化链表第一部分为NULL 
	rootp->bwd=NULL;
	rootp->fwd=NULL;
这时可以在专门的free函数中释放该空间,也可以在main函数最后释放。


2.实现值的有序插入

这时需要考虑节点插入的位置,一般来讲有4种情况:

1.插入位置在链表中间段;

2.插入位置在链表初始段;

3.插入位置在链表尾部;

4.该链表为空链表;

需要分别考虑这4种不同情况下节点的fwd及bwd指针指向。

以下代码详细描述了这四种情况下的指针指向:

if(next!=NULL)              //四种情况 
	{                             //1.中间 2.起始 3.末尾 4.空 
		if(this!=rootp)           //1.中间 
		{
			newnode->fwd=next;
			this->fwd=newnode;
			next->bwd=newnode;
			newnode->bwd=this;
		}
		else{                   //2.初始 
			newnode->fwd=next;
			rootp->fwd=newnode;
			newnode->bwd=NULL;
			next->bwd=newnode;
		}
	}
	else{                      
		if(this!=rootp)         //末尾非空 
		{
			newnode->fwd=next;
			this->fwd=newnode;
			newnode->bwd=this;
			rootp->bwd=newnode;
		}
		else{                   //空 
			newnode->fwd=NULL;
			rootp->fwd=newnode;
			newnode->bwd=NULL;
			rootp->bwd=newnode;
		}
	}


同时,我们可以注意到以上单纯实现插入的代码段比较冗余,由于if语句内有功能相似或相同的代码,我们可以将其提取出现,减少代码量。

比如每个if语句中都有newnode->fwd=next,以及this->fwd=newnode.且在程序运行到该段时,newnode及this指针一定不为NULL,故可以直接提取出来。如下是改进后的完整的插入节点的代码段。

ps:注意到根指针rootp的存储空间是在main函数中通过malloc语句分配,存储在堆(heap)中,在做插入、删除等系列操作时,并不需要改变rootp的值,故在函数参数列表中并不需要像单链表头指针一样传入其地址(当然,传地址也是可以的)。

这里是直接将rootp的值传入到函数中。

int dll_insert(Node *rootp,int num)
{
	Node *next;     //插入位置的下一个 
	Node *this;    //插入位置的前一个 
	Node *newnode;
	
	//	for(this=rootp;(next=this->fwd)!=NULL;this=next)
	for(this=rootp,next=rootp->fwd;next;this=next,next=next->fwd) //寻找插入位置,从头开始 
	{
		if(next->value==num)   //插入不同数 
			return 0;
		else if(next->value>num)  //一旦找到比待插入数大的位置,跳出循环; 
			break;	
	}
	
	newnode=(Node *)malloc(sizeof(Node)); //为新插入的节点分配空间 
	if(newnode==NULL) 
		return -1;
	newnode->value=num;	
	newnode->fwd=next;        //以下是实现插入(简化版) 
	this->fwd=newnode;
	
	if(this!=rootp)          //非初始 
	{
		newnode->bwd=this;
	}
	else{                   //初始 
		newnode->bwd=NULL;
	}
	if(next!=NULL)          //非结尾 
	{
		next->bwd=newnode;
	}
	else{                   //结尾(新加入的节点在最后一个位置) 
		rootp->bwd=newnode;
	}
	
	return 1;
} 

3.删除节点

在删除某个节点时,需要注意删除的位置是在初始位置,中间位置,还是末尾位置,同时需注意删除之后链表是否为空。

void reduce(Node *rootp)  //删除 
{
	Node *q,*p,*t;   //t可以木有√ 
	int isFound=0;  //标记是否找到 
	int num;
	puts("Please enter the num to delete:");
	scanf("%d",&num);
	while(getchar()!='\n')
		continue;
		
	for(q=NULL,p=rootp->fwd;p;q=p,p=p->fwd)  //删除
	{
		if(p->value==num)   //如果找到了 
		{
			isFound=1;
			if(q)       
			{
				q->fwd=p->fwd; //
				if(p->fwd==NULL) //最后位置 
				{
					rootp->bwd=q; //表示q位置为最后一个,删除p; 
				}
				else{
				//	t=p->fwd; //p->fwd->bwd=q;
					p->fwd->bwd=q;   //不是最后一个,删除p,p的后一个的前一个为q 
				}
			}
			else{
				rootp->fwd=p->fwd;  //q为NULL,那么p是第一个,删除p,根指针指向第二个(可能为空,这样整个链表就是空的了),现在是第一个了 
				if(p->fwd==NULL)  //p是最后一个。说明删除之后就没有了 
					rootp->bwd=NULL;  //整个链表为空, rootp->bwd=NULL   rootp->fwd=p->fwd,而p->fwd为空 
				else{
					p->fwd->bwd=NULL;//这个意思是p后面还有,那么该节点为第一个了,其bwd要指向NULL; 
				} 
				 
			}
			free(p);
			break;
		}
	}
	if(!isFound)
	{
		printf("Not found.\n");
	}
}
3.正向打印及反向打印

这个操作比较简单,不多做介绍。

void print(Node *rootp)  //打印 
{
	Node *p;
	for(p=rootp->fwd;p;p=p->fwd)  //正向打印,以根指针的初始指向为开始,以链表初始节点开始 
	{
		printf("The num is %d.\n",p->value);
	}	
}

void bkprint(Node *rootp)  //反向打印 
{
	Node *p;
	for(p=rootp->bwd;p;p=p->bwd)  //反向打印,以根指针的bwd为始,指向的是链表最后一个节点 
	{
		printf("The num is %d.\n",p->value);
	}	
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值