您的位置:首页 > 理论基础 > 数据结构算法

数据结构(3):两个有序线性结构的合并

2017-01-10 22:54 435 查看

数组合并

把两个有序数组合并到一个新的数组中。
最直观的想法,全部放进去,然后排序……。简单粗暴的做法。
当然,所谓的考试,不能这么写。
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
;

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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息