当前位置: 首页 > news >正文

C++ 顺序表和单链表的二路归并思想(详解+示例代码)

CSDN话题挑战赛第2期
参赛话题:学习笔记

文章目录

  • 有序顺序表的二路归并
    • 合并相同的元素
    • 查找第k小的元素
  • 单链表的二路归并
  • 例题:求多项式的和

有序顺序表的二路归并

我们有两个顺序表A和B,他们都是有序递增的,我们想要把这两个顺序表保持原有的递增顺序合成一个新的顺序表C

void merge_list(sqList& list1,sqList& list2,sqList& DstList)
{
	int A=0;
	int B=0;
	int Dst=0;    //合并后的顺序表的下标位置
	while (A<list1.length && B<list2.length)
	{
		if (list1.data[A]<list2.data[B])
		{
			//当A表元素比B表元素小 时,把这个元素添加到C表上面,并且递增A表的位置
			DstList.data[Dst++]=list1.data[A];
			A++;
		}
		else
		{
			//当B表的元素比A表的元素大或者等于 时,把这个元素添加到C表上,并且递增B表的位置
			DstList.data[Dst++]=list2.data[B];
			B++;
		}
	}
	//当遍历完A表或者B表后,一定还有一个表没有遍历完毕,接着在连接剩下来的元素
	while (A<list1.length)
	{
		DstList.data[Dst++]=list1.data[A++];
	}
	while (B<list2.length)
	{
		DstList.data[Dst++]=list2.data[B++];
	}
	DstList.length=Dst++;
}

本算法的空间复杂度为O(1),时间复杂度是O(n+m),n和m分别是A表和B表的长度。

测试:
在这里插入图片描述

合并相同的元素

与上一题的思路一致,使用二路归并思想找到二表中相同的元素,把此元素添加到C表上面。

//两个顺序表的公共元素组成新顺序表
void CommElem_list(sqList& list1,sqList& list2,sqList& DstList)
{
	int A = 0;
	int B = 0;
	int Dst = 0;
	while (A < list1.length && B < list2.length)
	{
		if (list1.data[A] < list2.data[B])
		{
			A++;
		}
		else if (list1.data[A] > list2.data[B])
		{
			B++;
		}
		else
		{
			//两个元素相同,可以放入到新表中,最后,两个表的位置同时往后面移动
			DstList.data[Dst++] = list1.data[A];
			A++;
			B++;
		}
	}
	DstList.length = Dst;
}

测试:
在这里插入图片描述

查找第k小的元素

描述:有两个顺序表A和B,他们都是有序递增的,分别含有n和m个元素,并且A表和B表的元素互不相同,设计一个算法找出n+m中第k小的元素,并用参数e表示找到的那个第k小的元素。

//找出第pos小的元素
void Find_Pos_Little_Elem(sqList& list1,sqList& list2,int pos,Element& elem)
{
#define INF 32767
	int A=0,B=0;
	//pos越界
	if (pos<1 || pos>list1.length+list2.length)
	{
		cout<<"没有找到!\n"<<endl;
		return;
	}
#if 1
	while (A < list1.length && B < list2.length)
	{
		pos--;
		if (list1.data[A] < list2.data[B])
		{
			if (pos == 0)
			{
				elem = list1.data[A];
				return;
			}
			A++;
		}
		else
		{
			if (pos == 0)
			{
				elem = list2.data[B];
				return;
			}
			B++;
		}
	}
	//如果遍历完A或者B表后没有找到元素,则说明第pos个位置的元素不在A或B表中,则根据位置关系可以直接确定此位置元素
	// A: 1 2 6 8 10
	// B: 3 4 5 7 11  
	/*
	pos=6  B: 0+1-1
	*/
   //已经找完了一个表,不在这个表里面,则必在另一个表里面
	if (A < list1.length)
	{
		elem = list1.data[A + pos - 1];
	}
	if (B < list2.length)
	{
		elem = list2.data[B + pos - 1];
	}
}

测试:
在这里插入图片描述


另一种简化的做法:我们注意到两个表肯定会有一个先遍历完,然后再去遍历另一个表,或者就只遍历一个表,我们可以想办法让他们都在一个地方完成,不用再出去完成了。

我们使用了INT_MAX ,如果某一个表已经遍历完成了,但是还没有结束,我们直接遍历另一个表就行了,怎么使用呢,直接把INT_MAX与另一个表比较,这样每一次都会比较我们还没有遍历完的表:

/*
简化版本
*/
	while (true)
	{
		pos--;
		//遍历完A表后没有找到,则直接让A表的元素大于B表的所有元素,这样就可以直接进入else继续判断
		int _list1=((A<list1.length)?list1.data[A]:INF);
		//遍历完B表后没有找到,则直接让B表的元素大于A表的所有元素,这样就可以直接进入if继续判断
		int _list2=((B<list2.length)?list2.data[B]:INF);
		if (_list1<_list2)
		{
			if (!pos)
			{
				elem=_list1;
				return;
			}
			A++;
		}
		else
		{
			if (!pos)
			{
				elem=_list2;
				return;
			}
			B++;
		}
	}

这两种做法的时间复杂度都是O(n),空间复杂度复杂度为O(1)


单链表的二路归并

把两个递增的单链表合并成一个递增的有序单链表C,要求:不能销毁原来的单链表结构:


//有序单链表的二路归并: 两个递增链表合成一个递增链表   销毁原链表
void Merge_But_Destroy(sqListNode* list1,sqListNode* list2, sqListNode*& listDst)
{
    sqListNode* p1=list1->Next,*p2=list2->Next,*pTail=nullptr;
    pTail = listDst;
    sqListNode* pNew = nullptr;

    while (p1 && p2)
    {
        if (p1->data < p2->data)
        {
            sqListNode* pNew = CreateNode(p1->data);
            pTail->Next = pNew;
            pTail = pNew;
            p1 = p1->Next;
        }
        else
        {
            sqListNode* pNew = CreateNode(p2->data);
            pTail->Next = pNew;
            pTail = pNew;
            p2 = p2->Next;
        }
    }
    while (p1)
    {
        //p1链表不为空
        pNew = CreateNode(p1->data);
        pTail->Next = pNew;
        pTail = pNew;
        p1 = p1->Next;
    }
    while (p2)
    {
        //p2链表不为空
        pNew = CreateNode(p2->data);
        pTail->Next = pNew;
        pTail = pNew;
        p2 = p2->Next;
    }
}

算法的时间复杂度:O(n+m),算法的空间复杂度为O(n+m)
当然了这是不销毁原链表的深度复制的做法,如果你想要空间复杂度为O(1),则只能借用A链表和B链表的节点来重新链表产生C节点,这样就不会有新产生的空间,不过由于A和B的节点被重构,再合成一个C表后,A链表和B链表也就被销毁了。


例题:求多项式的和

有两个多项式,例如:

5x^2 + 6x^5 - 8x^4 +4x^ 3
2x^2 + 9x^5 + 8x^4 +4x^ 3

计算他们和:

15x^5 + 8x^3 + 7x^2

这里也是用到了二路归并的思想,首先要确保链表一定是有序递增的,如果A表比B表的元素小,则A表上,A表递增;如果B表比A表的元素的元素小,则B表上,B表递增。

我们首先要创建多项式的链表,对于创建,我们要根据他们的指数和系数来创建链表,并要在Merge之前确保他们是已经递增的链表了,然后我们对其指数的大小进行二路归并,相同指数的多项式系数相加,然后再继续遍历多项式的下一项,直到最后某一个多项式处理完了,另一项还有剩余,再处理剩下的多项式的项 :

void Merge(LinkNode* ha, LinkNode* hb, LinkNode*& list)
{
	LinkNode* pA = ha->next;
	LinkNode* pB = hb->next;
	
	InitLink(list);
	LinkNode* pList = list;
	LinkNode* pNew = nullptr;
	//二路归并
	while (pA && pB)
	{
		if (pA->exp < pB->exp)
		{
			pNew = CreateLinkNode(pB->coef, pB->exp);
			//尾插法连接
			pList->next = pNew;
			pList = pNew;
			pB = pB->next;
		}
		else if (pA->exp > pB->exp)
		{
			pNew = CreateLinkNode(pA->coef, pA->exp);
			//尾插法连接
			pList->next = pNew;
			pList = pNew;
			pA = pA->next;
		}
		else
		{
			//相等:多项式相加
			coefType coef = pA->coef + pB->coef;
			if (coef)
			{
				pNew = CreateLinkNode(coef, pA->exp);
				pList->next = pNew;
				pList = pNew;
			}
			pA = pA->next;
			pB = pB->next;
		}
	}
	if (pB)
	{
		pA = pB;
	}
	while (pA)
	{
		pNew = CreateLinkNode(pA->coef, pA->exp);
		pList->next = pNew;
		pList = pNew;
		pA = pA->next;
	}
}

运行结果如下:
在这里插入图片描述

注意:这道题还有较多的细节与知识点,我就不多说了,重点是让大家理解二路归并的思路。


如果想获取本节的所有源码和单链表和顺序表的操作大全(共计千行,包含了所有的基本操作),还有多项式的操作源码,访问我们github免费获取:
Github 单链表顺序表的操作模板,直接套就行,多项式操作源码大全

祝大家学习愉快!!!!

相关文章:

  • T1071 菲波那契数(信息学一本通C++)
  • Android开发基础——广播实践
  • opencv 深度学习
  • Windows取证——基本网络命令
  • CDH 09Cloudera Manager Kerberos安装配置(markdown新版二)
  • 小米面试——案例总结
  • 电源硬件设计----升降压变换器(负压输出)基础
  • Nodejs系列之模块加载机制
  • MyBatis 查询数据库入门
  • LQ0026 修剪灌木【数学】
  • 重识Nginx - 02 手把手教你编译适合自己的nginx 1.22.0
  • Java泛型详解
  • opencv连通域标记 connectedComponentsWithStats()函数
  • 【C#在资源管理器中显示自定义文件格式的缩略图】
  • 【NLP】第2章 开始使用 Transformer 模型的架构
  • 77. Combinations
  • CSS实用技巧
  • Java,console输出实时的转向GUI textbox
  • JavaScript实现分页效果
  • Just for fun——迅速写完快速排序
  • Linux链接文件
  • Python_OOP
  • Python利用正则抓取网页内容保存到本地
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 分布式熔断降级平台aegis
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 入手阿里云新服务器的部署NODE
  • 06-01 点餐小程序前台界面搭建
  • ​Python 3 新特性:类型注解
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ​一些不规范的GTID使用场景
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • $L^p$ 调和函数恒为零
  • (1)(1.13) SiK无线电高级配置(六)
  • (2)nginx 安装、启停
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (黑马C++)L06 重载与继承
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (算法)前K大的和
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .Net接口调试与案例
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .Net中ListT 泛型转成DataTable、DataSet
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • /ThinkPHP/Library/Think/Storage/Driver/File.class.php  LINE: 48
  • @hook扩展分析
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • [04] Android逐帧动画(一)
  • [2018][note]用于超快偏振开关和动态光束分裂的all-optical有源THz超表——