1. 程式人生 > >將兩個有序連結串列合併成一個有序連結串列

將兩個有序連結串列合併成一個有序連結串列

問題定義:

        寫一個函式SortedMerge函式,該函式有兩個引數,都是遞增的連結串列,函式的功能就是合併這兩個遞增的連結串列為一個遞增的連結串列,SortedMerge的返回值是新的連結串列。新連結串列由前兩個連結串列按元素遞增順序合併而成,也就是說它不會建立新的元素。

比如:這裡有兩個連結串列,分別是

list1: 5->10->15

list2: 2->3->20

SortedMerge函式返回一個指向新連結串列的指標,新連結串列應該是如下這樣的:2->3->5->10->15->20

    程式需要考慮如下情況:兩個連結串列(函式引數)都有可能為空,也可能其中一個連結串列已經遍歷完了,另一個連結串列還有很多元素。

方法1(虛擬節點) 

        這種方法用一個虛擬節點(dummy node)作為結果連結串列的起始節點,為了方便在連結串列尾部插入節點,還需要用一個尾指標指向連結串列的尾節點。

        初始時,結果連結串列為空的時候,尾指標指向的是虛擬節點。因為虛擬節點是一個在棧上分配的臨時變數,所以對它的操作都是非常高效的。在迴圈迭代中,每次從a或b中取一個節點插入到結果連結串列的尾部,迴圈結束時,虛擬節點的next域就是結果連結串列的地址,也就是我們期望的返回值。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/*Link list node*/
struct node
{
	int data;
	struct node* next;
};

/*Function to insert a node at the begining of the linked list*/
void push(struct node** head_ref, int new_data)
{
	/* allocate node*/
	struct node* new_node = (struct node*)malloc(sizeof(struct node));
	
	/* put in the data*/
	new_node->data = new_data;
	
	/*link the old list off the new node*/
	new_node->next = (*head_ref);

	/*move the head to point to the new node*/
	(*head_ref) = new_node;
}

/* Function to print nodes in a given linked list */
void printList(struct node* node)
{
	while(node != NULL)
	{
		printf("%d ", node->data);
		node = node->next;
	}
	printf("\n");
}

/*pull off the front node of the source and put it in dest*/
/* MoveNode() function takes the node from the front of the source, and move it to 
the front of the dest.
It is an error to call this with the source list empty.

	Before calling MoveNode():
	source == {1, 2, 3}
	dest == {1, 2, 3}
	
	After calling MoveNode():
	source == {2, 3}
	dest == {1, 1, 2, 3}
*/

void MoveNode(struct node** destRef, struct node** sourceRef)
{
	/* the front source node */
	struct node* newNode = *sourceRef;
	assert(newNode != NULL);
	
	/*Advance the source pointer */
	*sourceRef = newNode->next;
	
	/* Link th eold dest off the new node */
	newNode->next = *destRef;

	/*Move dest to point to the new node */
	*destRef = newNode;
}

/*Takes two lists sorted in creasing order, and splices their nodes together to 
make ont big sorted list which is returned. */
struct node* SortedMerge(struct node* a, struct node* b)
{
	/* a dummy first node to hang the result on */
	struct node dummy;
	
	/* tail points to the last result node */
	struct node* tail = &dummy;
	
	/*so tail->next is the places to add new nodes to the result*/
	dummy.next = NULL;
	while(1)
	{
		if(a == NULL)
		{
			tail->next = b;
			break;
		}
		else if(b == NULL)
		{
			tail->next = a;
			break;
		}

		if(a->data <= b->data)
		{
			MoveNode(&(tail->next), &a);
		}
		else
		{
			MoveNode(&(tail->next), &b);
		}
		tail = tail->next;
	}	
	return (dummy.next);
}
/*Drier program to test above functions */
int main(int argc, char* argv[])
{

	/*start with the empty list */
	struct node* res = NULL;
	struct node* a = NULL;
	struct node* b = NULL;

	/*Let us create two sorted linked lists to test the functions 
	Created lists shall be a:5->10->15, b:2->3->20 */

	push(&a, 15);
	push(&a, 10);
	push(&a, 5);

	push(&b, 20);
	push(&b, 3);
	push(&b, 2);

	res = SortedMerge(a, b);
	printf("\nMerged Linked List is:\n");
	printList(res);
	return 0;
}

方法2(區域性引用)

        這種方法與上一種方法非常相似。這種方法避免使用虛擬節點(dummy node),而是使用一個指向指標的指標,struct node** lastPtrRef,這個指標指向結果連結串列的最後一個節點。在這個方法中,所有由虛擬節點完成的工作都有lastPtrRef完成。

/* method2 Using local References */
struct node* SortedMerge(struct node* a, struct node* b)
{
	struct node* result = NULL;

	/*point to the last result pointer */
	struct node** lastPtrRef = &result;

	while(1)
	{
		if(a == NULL)
		{
			*lastPtrRef = b;
			break;
		}
		else if(b == NULL)
		{
			*lastPtrRef = a;
			break;
		}
		if(a->data <= b->data)
		{
			MoveNode(lastPtrRef, &a);
		}
		else
		{
			MoveNode(lastPtrRef, &b);
		}
		/*tricky:advance to point to the next ".next" field */
		lastPtrRef = &((*lastPtrRef)->next);
	}
	return (result);
}


方法3(遞迴)

        合併操作是非常適合用遞迴來完成的一類操作,遞迴實現將會比迭代實現更加清晰且易於理解。儘管如此,你可能也不願意使用遞迴來實現這個操作,因為遞迴方法所使用的棧空間與連結串列的長度成正比。

/*Using Recursion*/
struct node* SortedMerge(struct node* a, struct node* b)
{
	struct node* result = NULL;
	
	/*Base cases*/
	if(a == NULL)
		return (b);
	else if(b == NULL)
		return (a);

	/*Pick either a or b, and recur */
	if(a->data <= b->data)
	{
		result = a;
		result->next = SortedMerge(a->next, b);
	}
	else
	{
		result = b;
		result->next = SortedMerge(a, b->next);
	}
	return (result);
}
原文網址:http://www.geeksforgeeks.org/archives/3622