1.3 双链表

这一章是有关双链表的算法分析和代码实现。
双链表的结点数据结构:

/*双链表结点的数据结构*/
typedef struct DNode {
	ElemType data;						//数据域
	struct DNode* prior;				//前驱指针
	struct DNode* next;					//后继指针
}DNode, *DLinkList;

主要有以下实现功能的函数:

  1. DLinkList CreateListbyHeadInsert(DLinkList& L)) 功能:头插法建立双链表(逆向建立双链表);
    参数:L:链表;
    时间复杂度:O(n);

  2. DLinkList CreateListbyTailInsert(DLinkList& L) 功能:尾插法建立双链表(正向建立双链表);
    参数:L:链表;
    时间复杂度:O(n);

  3. DNode* GetNode(DLinkList& L, int i) 功能:按位查找,拿到第i个位置的结点并返回;
    参数:L:链表 , i:要查找的结点的位置;
    时间复杂度:O(n);

  4. DNode* LocateNode(DLinkList L, ElemType e) 功能:按值查找,查找第一个数据域和e相等第结点并返回;
    参数:L:链表,e:要查找的结点的值;
    时间复杂度:O(n);

  5. bool DNodeInsert(DLinkList& L, int i, ElemType e) 功能:插入结点,在第i个位置插入一个结点;
    参数:L:链表 ,i:插入的位置,e:插入结点的值
    时间复杂度:O(n);

  6. bool DNodeDelete(DLinkList& L, int i) 功能:删除结点,删除第i个结点;
    参数:L:链表,i:删除结点的位置;
    时间复杂度:O(n);

  7. bool DNodeRevise(DLinkList& L, int i, ElemType e) 功能:修改结点,将第i个结点的数据修改为e;
    参数:L:链表,i:要修改的结点位置,e:要改成的值;
    时间复杂度:O(n);

  8. int LinkLength(DLinkList L) 功能:求表长;
    参数:L:链表;
    时间复杂度:O(n);

  9. void PrintList(DLinkList L) 功能:输出所有结点的值;
    参数:L:链表;
    时间复杂度:O(n);

图解:
(1)双链表初始化
双链表初始化
(2)头插法建立双链表
在头插法建立双链表中,p指向头结点,s指向新插入的结点。每次插入新结点都要进行的步骤有:

 		s->next = p->next;				
		if(p->next)						
			p->next->prior = s;
		s->prior = p;
		p->next = s;

这里需要注意的是在执行“p->next->prior = s”前要进对p->next是否存在进行判断,若不存在可能会导致NULL->prior的空指针错误。
比如下图中,插入第一个结点时p->next就不存在,自然就不执行“p->next->prior = s”了。
头插法建立双链表
运行结果

(3)尾插法建立双链表
在尾插法建立双链表中,r指向表尾结点,s指向新插入的结点。每次插入新结点都要进行的步骤有:

s->prior = r;
s->next = NULL;
r->next = s;
r = s;

需要注意的是不要忘了将s->next指向NULL。以及每次最后一步都要移动表尾结点指针r;
尾插法建立双链表
运行结果

完整代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>

#define ElemType int

/*--------------------------------------------------------数据结构部分--------------------------------------------------------*/

/*双链表结点的数据结构*/
typedef struct DNode {
	ElemType data;						//数据域
	struct DNode* prior;				//前驱指针
	struct DNode* next;					//后继指针
}DNode, *DLinkList;

/*初始化双链表*/
void InitList(DLinkList& L) {
	L = new DNode;
	L->next = NULL;
	L->prior = NULL;
}

/*头插法建立双链表*/
DLinkList CreateListbyHeadInsert(DLinkList& L) {
	InitList(L);
	int x;
	DNode* s;
	DNode* p = L;
	scanf("%d", &x);
	while (x!=9999)
	{
		s = new DNode;
		s->data = x;
		s->next = p->next;				//注意这里的顺序
		if(p->next)						//判断p->next是否为空,若为空则不用执行下一步代码,否则NULL->prior会出错
			p->next->prior = s;
		s->prior = p;
		p->next = s;
		scanf("%d", &x);
	}
	return L;
}

/*尾插法建立双链表*/
DLinkList CreateListbyTailInsert(DLinkList& L) {
	InitList(L);						//初始化
	int x;
	DNode* s, * r = L;					//r为表尾指针,指向双链表最后一个结点
	scanf("%d", &x);
	while (x != 9999)					//输入9999结束
	{
		s = new DNode;
		s->data = x;
		s->prior = r;
		s->next = NULL;
		r->next = s;
		r = s;
		scanf("%d", &x);
	}
	return L;
}

/*按位查找,拿到第i个位置的结点并返回*/
DNode* GetNode(DLinkList& L, int i) {
	int j = 1;								//计数,初始为1
	DNode* p = L->next;						//头结点赋值给p
	if (i == 0)								//若i == 0 则返回头结点
		return L;
	if (i < 1)								//若i无效则返回NULL
		return NULL;
	while (p && j < i)						//从第一个结点开始找,找到第i个结点
	{										//在p == NULL(表遍历结束)或j == i(已找到第i个结点)时跳出循环
		p = p->next;
		j++;
	}
	return p;								//返回第i个结点的指针,若i大于表长返回NULL
}

/*按值查找,查找第一个数据域和e相等第结点并返回*/
DNode* LocateNode(DLinkList L, ElemType e) {
	DNode* p = L->next;
	while (p != NULL && p->data != e)		//从第1个结点开始查找data为e的结点
	{
		p = p->next;						//移动结点指针
	}
	return p;								//找到后返回该节点的指针,否则返回NULL
}

/*插入结点,在第i个位置插入一个结点*/
bool DNodeInsert(DLinkList& L, int i, ElemType e) {
	DNode* s;
	DLinkList p = L;
	p = GetNode(L, i - 1);					//查找插入位置的前驱结点
	if (!p)									//p == NULL未找到,返回false
		return false;
	s = new DNode;							//插入结点
	s->data = e;
	s->next = p->next;						//s->next指向原来p的下一个结点
	if (p->next)							//和头插法建立双链表同理
		p->next->prior = s;
	s->prior = p;							//s->prior指向它的前一个结点p
	p->next = s;							//p->next指向新增的s结点
	return true;
}

/*删除结点,删除第i个结点*/
bool DNodeDelete(DLinkList& L, int i) {
	DNode* q;
	DLinkList p = L;
	p = GetNode(L, i - 1);					//查找删除位置的前驱结点
	if (!p)									//未找到第i个位置的前驱结点,返回false
		return false;
	q = p->next;							//q指向被删除结点
	if (!q)									//边界判断:第i个位置有前驱结点,但是不存在第i个位置的结点,返回false
		return false;
	q = p->next;							//q指向被删除结点
	p->next = q->next;						//将*q结点从链中断开
	if(q->next)								//判断要删除的第i个结点有无后继结点
		q->next->prior = p;
	free(q);								//释放结点的存储空间
	return true;
}

/*修改结点,将第i个结点的数据修改为e*/
bool DNodeRevise(DLinkList& L, int i, ElemType e) {
	DLinkList p = L;
	p = GetNode(L, i);						//拿到要修改的结点
	if (!p || i == 0)						//结点不能为空,不能修改头结点
		return false;
	p->data = e;							//修改结点的值
	return true;
}

/*求表长并返回*/
int LinkLength(DLinkList L) {
	DNode* p = L->next;						//头节点不存放数据,不算长度
	int sum = 0;
	while (p)
	{
		p = p->next;						//移动结点指针
		sum++;
	}
	return sum;								//返回表长
}


/*打印表*/
void PrintList(DLinkList L) {
	DNode* p = L->next;					//判断是否表空
	if (!p)
		printf("表空\n");
	else {
		printf("双链表数据:\n");
		while (p) {
			printf("%d  ", p->data);
			p = p->next;
		}
		printf("\n共%d个元素\n", LinkLength(L));
	}
}

/*--------------------------------------------------------功能函数部分--------------------------------------------------------*/
/*插入结点的功能*/
void Insert(DLinkList& L) {
	int location;
	ElemType elem;
	printf("输入要插入的元素:");
	scanf("%d", &elem);
	printf("要插入的位置:");
	scanf("%d", &location);
	if (DNodeInsert(L, location, elem))
		printf("插入成功\n");
	else
		printf("插入失败\n");
}

/*删除结点的功能*/
void Delete(DLinkList& L) {
	int location;
	printf("输入要删除的元素的位置:");
	scanf("%d", &location);
	if (DNodeDelete(L, location))
		printf("删除成功\n");
	else
		printf("删除失败\n");
}

/*修改结点的功能*/
void Revise(DLinkList& L) {
	int i;
	ElemType e;
	printf("输入要修改的位置:");
	scanf("%d", &i);
	printf("修改为:");
	scanf("%d", &e);
	if (DNodeRevise(L, i, e))
		printf("修改成功\n");
	else
		printf("修改失败\n");
}

void Search(DLinkList L) {
	int searchChoice;
	printf("(1)按位查找\n");
	printf("(2)按值查找\n");
	printf("选择查找功能:\n");
	scanf("%d", &searchChoice);
	int i;
	ElemType e;
	DNode* p;
	switch (searchChoice)
	{
	case(1):
		printf("输入要查找的节点位置:");
		scanf("%d", &i);
		p = GetNode(L, i);
		if (p && i != 0)					//查找的结点不能为空,也不能查找头结点的值
			printf("第%d个结点的值为%d\n", i, p->data);
		else
			printf("查找失败\n");
		break;
	case(2):
		printf("输入要查找的结点的值:");
		scanf("%d", &e);
		p = LocateNode(L, e);
		if (p)
			printf("找到该元素,查找成功\n");
		else
			printf("找不到该元素,查找失败\n");
		break;
	default:
		break;
	}
}


void menu() {
	printf("\n\n");
	printf("①----------头插法建立双链表----------\n");
	printf("②----------尾插法建立双链表----------\n");
	printf("③----------     打印表     ----------\n");
	printf("④----------    插入结点    ----------\n");
	printf("⑤----------    删除结点    ----------\n");
	printf("⑥----------    修改结点    ----------\n");
	printf("⑦----------    查找结点    ----------\n");
	printf("\n\n");
}

int main() {
	DLinkList L;
	int choice;
	do
	{
		menu();
		scanf("%d", &choice);
		switch (choice)
		{
			case(1):
				printf("输入每个元素的值(输入9999停止建表)\n");
				CreateListbyHeadInsert(L);
				break;
			case(2):
				printf("输入每个元素的值(输入9999停止建表)\n");
				CreateListbyTailInsert(L);
				break;
			case(3):
				PrintList(L);
				break;
			case(4):
				Insert(L);
				break;
			case(5):
				Delete(L);
				break;
			case(6):
				Revise(L);
				break;
			case(7):
				Search(L);
				break;
			default:
				break;
		}

	} while (choice != 0);
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值