倆個有序鏈表的合併

問題定義:
寫一個函數SortedMerge函數,該函數有兩個參數,都是遞增的鏈表,函數的功能就是合併這兩個遞增的鏈表爲一個遞增的鏈表,SortedMerge的返回值是新的鏈表。新鏈表由前兩個鏈表按元素遞增順序合併而成,也就是說它不會創建新的元素。
比如:這裏有兩個鏈表,分別是
list1: 5->10->13->15
list2: 2->3->16->20
SortedMerge函數返回一個指向新鏈表的指針,新鏈表應該是如下這樣的:2->3->5->10->13->15->16->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, 13); 
    push(&a, 10);  
    push(&a, 5);  

    push(&b, 20);
    push(&b, 16);
    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);  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章