数据结构(C/C++)课程阶段总结(五)

写在前面

课程采用教材《数据结构(C语言版)》严蔚敏,吴伟民,清华大学出版社。
本系列博文用于自我学习总结和期末复习使用,同时也希望能够帮助到有需要的同学。如果有知识性的错误,麻烦评论指出。
本次实现单链表的有序合并,以及循环链表的部分基本操作。

单链表有序合并

目标:归并La和Lb得到新的单链表Lc,且Lc的元素按值非递增排列。
具体过程:
pa和pb沿着La和Lb两条链表逐个往后移动,判断pa和pb的元素大小,小的先头插入到Lc中,大的需要与后面的再比较,寻找合适的插入位置。当某一结点移动到末尾时,较长的单链表需要头插入到Lc中。
分析算法可知,若两条单链表的表长分别为m,n,则不难发现,两条链表都需要被完全遍历一次,故时间复杂度为O(m+n)。
函数声明与实现:
单链表定义及操作仍然采用上一篇博文里的头文件LinkListH.h。

Status MergeList(LinkList& La, LinkList& Lb, LinkList& Lc);
// 初始条件:单链表La,Lb存在,且元素按值非递减排列。
// 操作结果:归并La和Lb得到新的单链表Lc,Lc的元素也按值非递减排列。
Status MergeList(LinkList& La, LinkList& Lb, LinkList& Lc) {
	// 单链表La,Lb存在,且元素按值非递减排列。
	// 归并La和Lb得到新的单链表Lc,且Lc的元素按值非递增排列。
	if (!La || !Lb)return ERROR;
	LinkList pa = La->next; LinkList pb = Lb->next;// 创建La,Lb的移动结点指针
	Lc = new LNode; Lc->next = NULL;// 创建Lc的头结点
	LinkList pc = new LNode;// 创建Lc的插入结点
	while (pa && pb)
	{// pa和pb沿各自链表后移,直到某个结点移动到表尾之后
		if (pa->data <= pb->data)// 如果La当前结点的元素值小于等于Lb的
		{// 则让Lc的插入结点等于La当前结点
			pc = pa;
			pa = pa->next;// 并让La的当前结点后移
		}
		else// 如果La当前结点的元素值大于Lb的
		{// 则让Lc的插入结点等于Lb当前结点
			pc = pb;
			pb = pb->next;// 并让Lb的当前结点后移
		}
		pc->next = Lc->next;// 仿照头插入的方法,形成倒序表
		Lc->next = pc;
	}
	pc = pa ? pa : pb;// 把剩余的链表插入到Lc中
	LinkList in = new LNode;// 插入结点
	while (pc)// 头插入
	{
		in = pc;
		pc = pc->next;
		in->next = Lc->next;
		Lc->next = in;
	}
	delete Lb;
	return OK;
}// MergeList

主函数内容;

int main()
{
	cout << "-----测试单链表有序合并-----\n";
	LinkListClass La,Lb,Lc;
	cout << "请输入La十个元素:\n";
	La.CreateListTail(La.L, 10);// 测试数据:1 3 5 7 9 11 13 15 17 19
	cout << "请输入Lb五个元素:\n";
	Lb.CreateListTail(Lb.L, 5);// 测试数据:4 6 10 14 16
	MergeList(La.L, Lb.L, Lc.L);
	Lc.PrintList(Lc.L);
	return 0;
}

运行结果
合并

双向链表

双向链表的基本操作部分可直接移植自单链表,比如输出和表长获取,其他基本操作也可从单链表处获得启发。比如双向链表的建立,基本上与单链表相同,不过要在尾接插入结点的同时建立起插入结点与前驱结点的联系,头结点的前驱可为NULL。

双向链表的定义

//线性表的双向链表存储结构
struct DuLNode {
	ElemType data;
	DuLNode* prior;
	DuLNode* next;
};
typedef DuLNode* DuLinkList;

双向链表插入

目标:在带头结点的双向链表L中第i个位置之前插入元素e。
具体过程:
先获取双向链表插入位置的结点,如果为尾结点,直接在尾结点之后接插入结点,并让插入结点后接NULL;否则,让插入结点的前驱指向插入位置结点的前驱,插入位置结点前驱的后继变更为插入结点,插入结点的后继接插入位置结点,插入位置结点的前驱变更为插入结点,完成插入。
函数声明与实现:

Status ListInsert(DuLinkList &L, int i, ElemType e);
// 初始条件:双向链表L存在,且1≤i≤表长+1。
// 操作结果:在带头结点的双向链表L中第i个位置之前插入元素e。
Status ListInsert(DuLinkList& L, int i, ElemType e) {
	// 在带头结点的双向链表L中第i个位置之前插入元素e。
	DuLinkList p, s;// p为移动结点,s为插入结点
	if (!(s = new DuLNode))return ERROR;
	s->data = e;// 建立插入结点
	if ((p = GetElem(L, i)))// 如果1≤i≤表长
	{// 执行插入操作
		s->prior = p->prior; p->prior->next = s;// 注意钩链顺序
		s->next = p; p->prior = s;
	}
	else if (p = GetElem(L, i-1))// 如果i=表长+1
	{// 执行表尾后街操作
		s->prior = p; p->next = s;
		s->next = NULL;// 直接插入结点指空
	}
	else return ERROR;// i不合法
	return OK;
}// ListInsert

双向链表删除

目标:删除带头结点的双向链表L的第i个元素,并用e返回其值。
具体操作:
先获取删除位置结点,并传出其数据元素值。然后,把删除结点的前驱结点指向后继结点,如果删除结点为尾结点,后继结点为NULL,没有前驱;否则,删除结点后继结点的前驱需要指向删除结点的前驱。
函数声明与实现:

Status ListDelete(DuLinkList& L, int i, ElemType& e);
// 初始条件:双向链表L存在,且1≤i≤表长。
// 操作条件:删除带头结点的双向链表L的第i个元素,并用e返回其值。
Status ListDelete(DuLinkList& L, int i, ElemType& e) {
	// 删除带头结点的双向链表L的第i个元素,并用e返回其值。
	DuLinkList p, s;
	if (!(s = new DuLNode))return ERROR;
	if (!(p = GetElem(L, i)))return ERROR;
	e = p->data;// 取删除结点值
	p->prior->next = p->next;// 删除结点的前驱结点指向后继结点
	if(p->next)// 如果p->next不为空,需要把p->next的前驱指向p的前驱
		p->next->prior = p->prior;
	delete p;
	return OK;
}// ListDelete

主函数

int main()
{
	DuLinkList L;
	ElemType e;
	cout << "请输入十个元素:\n";
	CreateListTail(L, 10);//测试数据:1 2 3 4 5 6 7 8 9 10
	PrintList(L);
	cout << "-----测试插入-----\n";
	cout << "在第11位插入11\n";
	ListInsert(L, 11, 11);
	PrintList(L);
	cout << "在第1位插入0\n";
	ListInsert(L, 1, 0);
	PrintList(L);
	cout << "-----测试删除-----\n";
	cout << "删除第1位元素\n";
	ListDelete(L, 1, e);
	cout << "删除元素为:" << e << "\n";
	PrintList(L);
	cout << "删除第11位元素\n";
	ListDelete(L, 11, e);
	cout << "删除元素为:" << e << "\n";
	PrintList(L);

	return 0;
}

运行结果

双向链表

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kFkPkDkN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值