数据结构与算法基础篇3-线性表-双向链表的基本操作C++

前言

单链表的局限性:无法逆向检索,有时候会不太方便,我们在单链表每个结点里增加一个指向其直接前驱的指针域prior,这样就形成了两条方向不同的链,故称为双向链表

 


双向链表的储存结构 

#include <stdio.h>
#define TRUE 1
#define FALSE 0
typedef int DataType;

//双向链表结点类型
typedef struct DNode
{
	DataType data;
	struct DNode* prior, * next;//前驱和后继指针
}DNode,*DLinkList;

初始化双向链表(带头节点)

//初始化双向链表(带头节点)
bool InitDLinkList(DLinkList& L)
{
	L = new DNode;
	if (L == NULL)//内存不足,分配失败
	{
		printf("内存不足,分配失败!\n");
		return FALSE;
	}
	L->prior = NULL;//头结点的prior始终指向NULL
	L->next = NULL;//没有后继结点
	return TRUE;
}

判断双链表是否为空(带头结点)

//判断双链表是否为空(带头结点)
bool Empty(DLinkList L)
{
	if (L->next == NULL)
		return TRUE;
	else
		return FALSE;
}

双向链表尾插法建表

//双向链表尾插法建表
void  DList_TailInsert(DLinkList& L)
{
	L = new DNode;
	L->next = NULL;
	L->prior = NULL;
	DNode* s, *r;
	r = L;
	int x;
	scanf("%d", &x);
	while (1)
	{
		if (x != 9999)
		{
			s = new DNode;
			s->data = x;
			r->next = s;
			s->prior = r;
			r = s;
			scanf("%d", &x);
		}
		if (x == 9999)
			break;
	}
	r->next = NULL;
	
}

双向链表头插法建表

//双向链表头插法建表
void DList_HeadInsert(DLinkList& L)
{
	L = new DNode;
	L->next = NULL;
	L->prior = NULL;
	DNode* s;
	int x;
	scanf("%d", &x);
	while (1)
	{
		if (x != 9999)
		{
			s = new DNode;
			s->data = x;
			if (L->next == NULL)//当插入第一个结点的时候
			{
				s->next = L->next;
				s->prior = L;
				L->next = s;
			}
			if (L->next != NULL)
			{
				s->next = L->next;
				L->next->prior = s;
				s->prior = L;
				L->next = s;
			}
			scanf("%d", &x);
		}
		if (x == 9999)
			break;
	}
}

双向链表的后插操作

bool InsertNextDNode(DNode* p, DNode* s)
{
	s->next = p->next;
	p->next->prior = s;
	p->next = s;
	s->prior = p;
}

此代码有一个问题,就是当p为最后一个结点的时候,p->next为NULL,程序中的第二行会出现空指针的错误,所以,为了避免这个问题,我么需要对插入的p结点的位置进行一个判断

优化代码如下:

bool InsertNextDNode(DNode* p, DataType e)
{
	DNode* s;
	if (p == NULL )
	{
		printf("插入位置不合法!\n");
		return FALSE;
	}
	s = new DNode;
	s->data = e;
	s->next = p->next;
	if (p->next != NULL)
		p->next->prior = s;
	p->next = s;
	s->prior = p;
	return TRUE;
}

这里我们可以发现,当我们双向链表完成后插操作后,我们按位序插入和前插操作就不用向之前那样麻烦了,只要找到需要插入的位置,直接就能通过prior指针访问到他的前驱结点,在对前驱结点完成后插操作就可以了


双向链表前插操作

//双向链表前插操作
bool InsertpriorDNode(DNode* p, DataType e)
{
	DNode* s;
	if (p == NULL)
	{
		printf("插入位置不合法!\n");
		return FALSE;
	}
	s = new DNode;
	if (s == NULL)
	{
		printf("内存申请失败!\n");
		return FALSE;
	}
	s->data = e;
	s->next = p->prior->next;
	p->prior = s;
	p->prior->next = s;
	s->prior = p->prior;
	return TRUE;
}

双链表按位序插入

//双链表按位序插入
bool InsertDLink(DLinkList& L, int i, DataType e)
{
	if (i < 1)
	{
		printf("插入位置不合法!\n");
		return FALSE;
	}
	DNode* s,*p;
	s = new DNode;
	s->data = e;
	p = L->next;
	int j = 1;
	while (p != NULL && j < i - 1)
	{
		p = p->next;
		j++;
	}
	if (p == NULL)
	{
		printf("插入位置不合法!\n");
		return FALSE;
	}
	s->next = p->next;
	p->next->prior = s;
	p->next = s;
	s->prior = p;
	return TRUE;
}

双链表带头结点的删除结点

//双链表带头结点的删除结点
bool DeleteDNode(DLinkList& L,int i,DataType &e)
{
	if (i < 1)
	{
		printf("删除的位置不合法!\n");
		return FALSE;
	}
	DNode * p;
	int j = 0;
	p = L;
	while (p != NULL && j < i-1)//循环找到要删除的p结点
	{
		p = p->next;
		j++;
	}
	if (p == NULL)
	{
		printf("删除的位置不合法!\n");
		return FALSE;
	}
	if (p->next == NULL)//如果p为最后一个结点
	{
		p->prior->next = p->next;
	}
	if (p->next != NULL)//如果p不为最后一个结点
	{
		p->prior->next = p->next;
		p->next->prior = p->prior;
	}
	e = p->data;
	delete p;
	return TRUE;
}

删除指定结点

//删除指定结点
bool DeleteDNode(DNode* p)
{
	if(p==NULL)
	{
		printf("删除的位置不合法!\n");
		return FALSE;
	}
	if (p->next == NULL)//如果p为最后一个结点
	{
		p->prior->next = p->next;
	}
	if (p->next != NULL)//如果p不为最后一个结点
	{
		p->prior->next = p->next;
		p->next->prior = p->prior;
	}
	delete p;
	return TRUE;
}

带头结点的双链表按位查找,返回第i个元素

//带头结点的双链表按位查找,返回第i个元素
DNode* GetElem(DLinkList L, int i)
{
	if (i < 1)
	{
		printf("查找的位序不合法!\n");
		return FALSE;
	}
	DNode* p;
	p = L->next;
	int j = 0;
	while (p != NULL && j < i - 1)

	{
		p = p->next;
		j++;
	}
	if (p == NULL)
	{
		printf("查找的位序不合法!\n");
		return FALSE;
	}
	return p;
}

双链表按值查找

//双链表按值查找
int LocateDNode(DLinkList L, DataType e)
{
	int i = 1;
	DNode* p;
	p = L->next;
	while (p != NULL && p->data != e)
	{
		i++;
		p = p->next;
	}
	if (p == NULL)
	{
		printf("双链表中没有值为%d的元素\n", e);
		return FALSE;
	}
	else
		return i;
}

带头节点求双向链表的长度

//带头节点求双向链表的长度
int Length(DLinkList L)
{
	int i = 1;
	DNode* p;
	p = L;
	while (p != NULL)
	{
		p = p->next;
		i++;
	}
	return i-1;
}



显示双链表所有结点


//显示双链表所有结点
void OutPut(DLinkList L)
{
	DNode* p;
	p = L->next;
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

菜单

//菜单
void menu(void)
{
	printf("\n------------------------------------\n");
	printf("      欢迎使用双向链表操作演示!\n");
	printf("      1、双向链表初始化\n");
	printf("      2、创建双向链表\n");
	printf("      3、获取双向链表的长度\n");
	printf("      4、向双向链表插入结点\n");
	printf("      5、按值查找结点\n");
	printf("      6、按下标查找结点\n");
	printf("      7、按下标删除结点\n");
	printf("      8、退出操作演示!\n");
	printf("------------------------------------\n");

}

主函数

int main(void)
{
	
	DLinkList L=0;
	DNode* p;
	int n;
	int e;
	int i;
	while (1)
	{
		menu();
		printf("请选择想要进行的操作:");
		scanf("%d", &n);
		switch (n)
		{
		case 1: printf("双向链表初始化已完成!\n"); InitDLinkList(L); break;
		case 2:printf("请输入结点的值,输入9999结束建立\n"); DList_TailInsert(L); OutPut(L); break;
		case 3:printf("单链表的长度为:%d", Length(L)); break;
		case 4:printf("请输入需要插入的位置和数值:"); scanf("%d%d", &i, &e); InsertDLink(L, i, e); OutPut(L); break;
		case 5:printf("请输入需要查找结点的值:"); scanf("%d", &e);i= LocateDNode(L, e);
			if (i == 0) { printf("没有找到!\n"); }
			else { printf("找到了,下表是:%d\n", i); }break;
		case 6:printf("请输入需要查找的结点的下标:"); scanf("%d", &e);p= GetElem(L, e);
			if (p == NULL) { printf("没有找到!\n"); }
			else printf("找到了,值为:%d\n", p->data); break;
		case 7:printf("请输入需要删除的结点下标:"); scanf("%d", &e); p = GetElem(L, e); DeleteDNode(p); OutPut(L); break;
		case 8:printf("已退出操作演示!\n"); break;
		default:printf("输入不正确,请重新输入!\n");
		}
		if (n == 8)
			break;
	}
	return 0;
}

效果展示

 

 

 

 

 


感谢观看!码字不易,如果本篇文章对您有帮助,麻烦点赞支持一下!!!感谢^_^!!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值