数据结构-双链表基本操作(C语言实现)

4 篇文章 0 订阅
2 篇文章 0 订阅

参考书:王道考研数据结构

(此贴为博主学习408的笔记,因博主也是学习者,个人总结如有错误欢迎指正。如有侵权请告知,马上删除致歉)​​

一:双链表含义

        双链表是在单链表的基础上增加了一个指向前驱结点的prior指针。以此解决了单链表只可以找到后继而无法找到前驱结点的痛点问题,因为单链表只有一个指向后继的next指针,使得单链表只能从头到尾依次顺序的向后遍历。

        双链表的按值查找和按位查找与单链表相同,但是增加与删除操作上与单链表不同,不仅要修改后继指针,也要修改其prior前驱指针,在此过程中应注意不可断链!!

 二:双链表的插入操作演示


核心操作:

①s->next = p->next;

②p->next->prior = s;

③s->prior = p;

④p->next = s;

三:双链表删除 

核心操作:

1.p->next = q->next;

2.q->next->prior = p;

四:其余操作与单链表操作基本大同小异。

 

#include <stdio.h>
#include <stdlib.h>

/**********************************************
制作人:祝星。
项目名称:数据结构-双链表的基本操作(C语言实现)
完成时间:2022年7月0日
完成内容:双链表的创建,修改,增加,删除,销毁。
具体内容,尾插法,按位查找,按位删除,求表长,遍历输出,销毁双链表
运行环境:win10
程序环境:VC++
文件语言:C语言
文件类型:.cpp
注:1.VC++的.cpp环境,&为取地址。
	2.LNode *等价于LinkList,前者强调这是结点,后者强调这是链表,合适的地方使用合适的名字,代码可读性更高。

  注意:剩余操作与单链表一致,无非是多一步操作,在next指针指向后,修改prior指针,并且保证不断链。
		建议读者在学习链表等数据结构时画图理解更直观。
***********************************************/

#define Max 10  

/**定义一个具有前驱指针和后继指针的双链表的结构体**/
typedef struct DNode{
	int data;	//数据域
	struct DNode *prior,*next;		//前驱指针和后继指针
}DNode,*DLinkList;         //前者强调这是结点,后者强调这是链表

/**
	初始化一个双链表,其前驱指针和后继指针指向NULL
	除非连接为循环链表,否则prior指针永远指向NULL
**/
DLinkList InitDLinkList(DLinkList &L){
	int x; //输入data值
	L = (DLinkList)malloc(sizeof(DNode)); //申请链表的头结点
	DNode *s,*r = L;//初始化两个结点指针指向头结点
	if(L==NULL)			//没有申请到内存空间则返回false
		return false;
	L->prior=NULL;
	L->next=NULL;

	printf("输入(9999结束)");
	scanf("%d",&x);//输入插入的值,输入9999结束
	while(x!=9999){
		s = (DNode*)malloc(sizeof(DNode));//动态申请一个存放插入值的结点
		s->data=x;//将输入的值赋值给新申请结点
		r->next=s;//将指向头结点的r的next指针指向s
		s->prior=r;//将新节点的前驱指针指向上一个
		r=s;//将r的指针指向s新节点
		printf("输入(9999结束)");
		scanf("%d",&x);
	}
	r->next=NULL;//r的next指针指向NULL
	return L;
}

/**
	双链表的插入操作
**/
bool InsertNextDNode(DNode *p,DNode *s){
	if(p==NULL || s==NULL)
		return false;
	s->next=p->next;
	if(p->next !=NULL)
		p->next->prior = s;   
	s->prior = p;
	p->next = s;
	return true;
}

/*******************************
函数名:按位查找
参数:L,i
返回值:返回结点指针值
*******************************/
DNode *GetElem(DLinkList L,int i){
	int j=1;//因为有头结点,所以从1开始遍历
	if(i<1){//判断查找的位置是否合法
		printf("数值非法");
	}
	DNode *p = L->next;//初始化结点指针
	while(p!=NULL&&j<i){
		p=p->next;
		j++;
	}
	return p;
}

/*******************************
函数名:按位删除结点
参数:L,i
返回值:返回结点指针值
*******************************/
DLinkList DeleteNode(DLinkList &L,int i){
	DNode *p;
	p=GetElem(L,i);//调用按位查找函数
	if(p==NULL)
		return false;
	if(p->next!=NULL){
		p->prior->next = p->next;//p的前驱的后继指针指向p的后继
		p->next->prior=p->prior;//p的后继的前驱指针指向p的前驱
		free(p);	//释放
	}
	return L;
	/*
	第二种方法,和单链表一样找到P的前驱结点,也就是i-1,此方法是王道书中的删除核心操作
	DNode *p,*q;
	p=GetElem(L,i-1);//调用按位查找函数
	if(p==NULL)
		return false;
	q=p->next;
	if(q!=NULL){
		p->next = q->next;
		q->next->prior = p;
		free(p);	//释放
	}
	return L;
	*/

}

/*指定结点的后插*/
DLinkList InsertNextNode(DLinkList &L,DNode *p,int e){
	if(p==NULL)
		return false;
	DNode *s = (DNode*)malloc(sizeof(DNode));//申请不到内存空间则返回false
	if(s==NULL)
		return false;
	s->data=e;
	s->next=p->next;
	p->next->prior = s;
	s->prior = p;
	p->next=s;
	return L;
}
/*指位序后插入*/
DLinkList InsertNNode(DLinkList &L,int i,int e)
{
	if(i<1)
		return false;
	DNode *s;
	int j=0;
	s=L;
	while(s!=NULL&&j<i)
	{
		s=s->next;
		j++;
	}
	if(s==NULL)
		return false;
	
	return InsertNextNode(L,s,e);
}
 

/*求表长*/
int Length(DLinkList L)
{
	int len=0;
	DNode *p=L;
	while (p->next!=NULL)
	{
		p=p->next;
		len++;
	}
	return len;
}

/*遍历输出*/
void Show(DLinkList L){
	printf("当前单链表的所有元素:【head】<=>");
	DNode *p;
	p =L->next;
	while(p!=NULL){
		printf("[%d]<=>",p->data);
		p=p->next;
	}
	printf("NULL");
	printf("\n");
	printf("当前表长为%d\n",Length(L));
	printf("-----------------------------------------------------------\n");
}
/***
	彻底销毁链表各个节点,并将销毁后的链表指向null
  */
void DestroyList(DLinkList &L)
{
	
	DNode *p ,*q;//初始化两个指针,p用来free释放,q用来在p释放后将p指针指向下一个
	p= L->next;//删除带头链表头结点不删除
	q=p;		
	while(p!=NULL){
		q=p->next;  //提前将q指向下一个
		free(p);	//释放p
		p = q;		//将p指向q所指向的下一个
	}
	L->next = NULL; //头结点指向NULL
	printf("\n销毁成功\n");
}

int main(){
	DLinkList(l);
	l = InitDLinkList(l);
	Show(l);
	DeleteNode(l,3);
	printf("删除第三个位置的元素后:");
	Show(l);
	printf("在第二个位置后插入99:");
	InsertNNode(l,2,99);
	Show(l);
	return 0;
}


  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,实现双向链表基本操作包括链表的初始化、创建、查询、删除和释放。链表的查询和删除操作可以按照值或者按照结点的序号进行。 对于链表的初始化,可以创建一个空链表,将头指针和尾指针都指向NULL,表示链表为空。 链表的创建可以通过不断插入新的结点来实现。可以从头部或者尾部插入新的结点,并更新头指针和尾指针。 链表的查询操作可以按照值进行,遍历链表,找到目标值所在的结点。也可以按照结点的序号进行,从头结点开始顺序遍历到目标序号的结点。 链表的删除操作也可以按照值进行,遍历链表,找到目标值所在的结点并删除。也可以按照结点的序号进行,从头结点开始顺序遍历到目标序号的结点并删除。 最后,链表的释放操作可以遍历链表,逐个释放每个结点所占用的内存空间。 需要注意的是,双向链表相对于单向链表来说,每个结点都有指向前一个结点和后一个结点的指针,使得在插入和删除操作上更加灵活和方便。 另外,在实际应用中,链表的结构可以根据需求选择不同的组合,如单向带头、不带头循环、非循环等。其中,带头双向循环链表是最常用的链表结构,因为它的结构复杂,但是在实现时具有很多优势。而无头单向非循环链表则更多用于其他数据结构的子结构,如哈希桶、图的邻接表等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [双向链表及其基本操作](https://download.csdn.net/download/qq_43355070/13074056)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C语言详解双向链表基本操作](https://blog.csdn.net/qq_61386381/article/details/124789295)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值