两个有序线性结构的合并

数组合并


把两个有序数组合并到一个新的数组中。
最直观的想法,全部放进去,然后排序……。简单粗暴的做法。
当然,所谓的考试,不能这么写。
int main () {

	//
	// 建立2个数组
	const int N1 = 6;
	const int N2 = 5;
	int iArray1[N1] = {1,3,5,6,8,10};
	int iArray2[N2] = {2,4,7,9,11};

	const int N = N1+N2;
	int iArray[N] ;

	int i = 0,j = 0;
	int k = 0; // 在iArray中的下标

	// while结束,只会有一个走到头
	while (i<N1 && j<N2) // 一个结束以后,就把另一个剩下的往后排
	{
		int i1 = iArray1[i];
		int j1 = iArray2[j];
		if (i1<=j1)
		{
			iArray[k] = i1;
			i++;
		} else {
			iArray[k] = j1;
			j++;
		}
		k++;
	}

	if (i==N1 && j<N2) // L1走到头了,L2还没有(其实如果j<N2,那么i一定是等于N1的)
	{
		// 要用循环,不是if… if只会操作一个元素
		while (j<N2)
		{
			iArray[k] = iArray2[j];
			k++;
			j++;
		}
	} else if (j==N2 && i<N1) {

		while (i<N1) // 还有循环
		{
			iArray[k] = iArray1[i];
			k++;
			i++;
		}
	}
// 合并完毕
	for (int i = 0;i<N;i++)
	{
		cout<<iArray[i]<<" ";
	}

	cout<<"\n";

	system("pause");
	return 0;
}
1 2 3 4 5 6 7 8 9 10 11

链表合并


两个链表的合并,与对数组合并的写法不一样的地方是,数组需要一个元素一个元素地拷贝。
(假设是要把现有的2个数组合并到一个新数组中)
链表的合并,只要根据顺序,把链拆了,接上就行。

非递归解法


非递归实现,就要用循环。第一步是把2个链表中的其中一个走到头。第二步是把剩下的没有走完的链表再接上。
保险起见,特殊处理第一个节点。
/************************************************************************/
/* 
两个有序链表的合并
L1 = {1,3,5,6,8,10};
L2 = {2,4,7,9,11,12};
L = L1+L2; --> {1,2,3,4,5,6,7,8,9,10,11,12};

*/
/************************************************************************/

#include <iostream>
using namespace std;

typedef struct LNode
{
	int data;
	LNode * next;
} LNode, * Linklist ;

void printList(Linklist list) {

	while (list)
	{
		cout<<list->data<<" ";
		list = list->next;
	}
	cout<<"\n";
}

// 不用递归
Linklist merge(Linklist l1, Linklist l2) {

	// 合法性检查
	if (l1==nullptr && l2==nullptr)
	{
		return nullptr;
	} else if(l1 ==nullptr && l2!=nullptr) {
		return l2;
	} else if(l2==nullptr && l1!=nullptr) {
		return l1;
	} else {

		Linklist newHead,p;

		Linklist p1 = l1;
		Linklist p2 = l2;

		// 头 第一个
		if (p1->data<=p2->data)
		{
			newHead = p1;
			p1 = p1->next;
		} else {
			newHead = p2;
			p2 = p2->next;
		}

		p = newHead;

		// 直到某一个走到头
		while (p1!=nullptr && p2!=nullptr)
		{
			if (p1->data<=p2->data)
			{
				p->next = p1;
				p1 = p1->next;
			} else {
				p->next = p2;
				p2 = p2->next;
			}
			p = p->next;
		}

		// 判断是哪一条走到头了
		if (p1==nullptr && p2!=nullptr) // p2还剩
		{ // 链表接上就是了,不用用while
			// p = p2->next; // 不是写p = p2->next; 这样只会再接一个
			p->next = p2;
		} else if (p2==nullptr && p1!=nullptr) { // p1还有剩余
			// p = p1->next;
			p->next = p1;
		}

		return newHead;
	}
}

int main () {

	//
	// 建立2个链表
	const int N1 = 6;
	const int N2 = 6;
	int iArray1[N1] = {1,3,5,6,8,10};
	int iArray2[N2] = {2,4,7,9,11,12};
	
	Linklist list1, list2;
	Linklist p;

	LNode * node = new LNode;
	node->data = iArray1[0];
	node->next = nullptr;
	list1 = node;
	p = list1;

 	for (int i = 1;i<N1;i++)
 	{
 		LNode * node = new LNode;
 		node->data = iArray1[i];
 		node->next = nullptr;
 		p->next = node;
 		p = p->next;
 	}
	printList(list1);

	LNode * node2 = new LNode;
	node2->data = iArray2[0];
	node2->next = nullptr;
	list2 = node2;
	p = list2;

	for (int i = 1;i<N2;i++)
	{
		LNode * node = new LNode;
		node->data = iArray2[i];
		node->next = nullptr;
		p->next = node;
		p = p->next;
	}

	printList(list2);
	//
	// 建立2个链表 End

	// 合并!
	Linklist list_merged_1 = merge(list1,list2);
	printList(list_merged_1);

	system("pause");
	return 0;
}
1 3 5 6 8 10
2 4 7 9 11 12
1 2 3 4 5 6 7 8 9 10 11 12
但有个问题就是,为什么再调一次merge这个函数,会出问题呢???

递归解法


递归写起来太神奇了。
// 链表合并,递归做法
Linklist mergeRecursion(Linklist l1, Linklist l2) {

	// 1. 如果有一个“走到底”,1和2肯定必须会发生一个。没有第三种情况
	// 1.1 如果l1走到底,就返回l2(作为剩下部分的头指针)
	if (l1==nullptr)
	{
		return l2;
	}

	// 1.2 如果l2走到底,就返回l1(作为剩下部分的头指针)
	if (l2==nullptr)
	{
		return l1;
	}
	
	// 2. 都还没有走到底
	// 2.1 定义一个头指针
	Linklist newHead = nullptr;
	if (l1->data<l2->data)
	{
		// 2.2 比谁大谁小,(1)头指针指向小的,(2)头指针的next指向新的合并结果
		newHead = l1;
		newHead->next = mergeRecursion(l1->next,l2);
	} else {
		newHead = l2;
		newHead->next = mergeRecursion(l1,l2->next);
	}

	// 返回头指针
	return newHead;

}
1. 如果一个走到底了,(另一个肯定还没有走到底!)不可能两个都到尾!一个到底,就把另一个节点返回;
2. 如果另一个走到底了,就把这个结点返回;
3. 如果都没有到底,特殊处理第一个节点(新的头节点),newHead等于谁,newHead的next是谁。newHead的next会是下一次合并以后的头节点!

e.g.

// 链表合并,递归做法
Linklist mergeRecursion(Linklist l1, Linklist l2) {
// ...
Linklist newHead = nullptr;
if (l1->data<l2->data)
{
// 2.2 比谁大谁小,(1)头指针指向小的,(2)头指针的next指向新的合并结果
newHead = l1;
newHead->next = mergeRecursion(l1->next,l2); // 合并以后的
}
// ...

// 返回头指针
return newHead; // 这个头指针,会返回给上一个newHead的next!就接起来了。
}

递归:
砍掉头,是不是一样的?(本例)
砍掉尾,是不是一样的?
从中间剖开,左右是不是一样的?(e.g. 快速排序)

更简洁的写法
 // 链表合并,递归做法
 Linklist mergeRecursion(Linklist l1, Linklist l2) {
 
 	// 1. 如果有一个“走到底”,1和2肯定必须会发生一个。没有第三种情况
 	// 1.1 如果l1走到底,就返回l2
 	if (l1==nullptr)
 	{
 		return l2;
 	}
 
 	// 1.2 如果l2走到底,就返回l1
 	if (l2==nullptr)
 	{
 		return l1;
 	}
 	
 	// 2. 都还没有走到底
 	if (l1->data<l2->data)
 	{
 		// 2.2 比谁大谁小,(1)头指针指向小的,(2)头指针的next指向新的合并结果
		l1->next = mergeRecursion(l1->next,l2);
		return l1;
 	} else {
		l2->next = mergeRecursion(l1,l2->next);
		return l2;
 	}
 }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qcyfred

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

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

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

打赏作者

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

抵扣说明:

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

余额充值