1. 程式人生 > >數據結構與算法 - 鏈表

數據結構與算法 - 鏈表

nlog () 數據結構 所有 program 效率 ase 固定 插入

鏈表

題型1:數組和鏈表的區別是什麽?

數組和鏈表的區別主要表現在以下幾個方面:

1)邏輯結構。數組必須事先定義固定的長度,不能適應數據動態地增減。當數組中插入、刪除數據項時,需要移動其他數據項。而鏈表采用動態分配內存的形式實現,可以適應數據動態第增減的情況,需要時可以用new/malloc分配內存空間,不需要時使用delete/free將已分配的空間釋放,插入和刪除元素不需要移動數據項。

2)內存結構。數組從棧中分配空間,鏈表從堆中分配空間。

3)數組中的數據在內存中是順序存儲的,而鏈表是隨機存儲的。數組的隨機訪問效率很高,可以直接定位,但插入、刪除操作的效率比較低。鏈表的插入、刪除操作不需要移動元素。

4)鏈表不存在越界的問題,數組有越界的問題。

題型2:對單鏈表進行排序

方法1:使用冒泡排序

方法2:使用直接插入排序

方法3:使用歸並排序

當我們需要對鏈表進行排序時,由於不能對它的元素進行隨機訪問,所以更適合使用歸並排序,大名鼎鼎的快速排序用到鏈表上,效率也很低,原因還是在於不能對鏈表中的元素進行隨機訪問,同理,采用堆排序更是不可能的事情。

算法具體實現時需要一個指向頭節點(鏈表的第一個節點,鏈表中不包含額外的一個節點來作頭節點)的指針,這是因為在算法實現的時候,不大可能第一個節點正好就是所有元素中最小的一個,則鏈表的頭節點會改變,因此我們需要一個指向頭節點的指針來存儲不斷變化的頭節點。

算法思想:

MergeSort(headRef)
1) If head is NULL or there is only one element in the Linked List
    then return.
2) Else divide the linked list into two halves.
      FrontBackSplit(head, &a, &b); /* a and b are two halves */
3) Sort the two halves a and b.
      MergeSort(a);
      MergeSort(b);
4) Merge the sorted a and b (using SortedMerge() discussed here)
   and update the head pointer using headRef.
     *headRef = SortedMerge(a, b);
代碼示例:
技術分享圖片
#include <stdio.h>  
#include <stdlib.h>  
  
/*Link list node*/  
struct node  
{  
    int data;  
    struct node* next;  
};  
  
/*function prototype */  
struct node* SortedMerge(struct node* a, struct node* b);  
void FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef);  
  
/*sorts the linked list by changing next pointers(not data) */  
void MergeSort(struct node** headRef)  
{  
    struct node* head = *headRef;  
    struct node* a;  
    struct node* b;  
  
    /*base case-- length 0 or 1 */  
    if((head == NULL) || (head->next == NULL))  
    {  
        return;  
    }  
      
    /*Split head into ‘a‘ and ‘b‘ sublists */  
    FrontBackSplit(head, &a, &b);  
  
    /*Recursively sort the sublists */  
    MergeSort(&a);  
    MergeSort(&b);  
  
    /* answer = merge the two sorted lists together */  
    *headRef = SortedMerge(a, b);  
}  
  
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 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);  
}  
  
/* UTILITY FUNCTIONS */  
/* Split the nodes of the given list into front and back halves, 
    and return the two lists using the references parameters. 
    If the length is odd, the extra node shold go in the front list. 
    Uses the fast/slow pointer strategy. */  
void FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef)  
{  
    struct node* fast;  
    struct node* slow;  
  
    if(source == NULL || source->next == NULL)  
    {  
        *frontRef = source;  
        *backRef = NULL;  
    }  
    else  
    {  
        slow = source;  
        fast = source->next;  
  
        /* Advance ‘fast‘ two nodes, and advance ‘slow‘ one node */   
        while(fast != NULL)  
        {  
            fast = fast->next;  
            if( fast != NULL )  
            {  
                slow = slow->next;  
                fast = fast->next;  
            }  
        }  
  
        *frontRef = source;  
        *backRef = slow->next;  
        slow->next = NULL;  
    }  
}  
      
/*Function to print nodes in a given linked list*/  
void printList(struct node* node)  
{  
    while( node != NULL )  
    {  
        printf("%d  ", node->data);  
        node = 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;  
}  
      
/* Drier program to test above functions*/  
int main()  
{  
    /* Start with the empty list */  
    struct node* res = NULL;  
    struct node* a = NULL;  
  
    /* Let us create a unsorted linked lists to test the functions 
       Created lists shall be a: 2->3->20->5->10->15 */  
    push(&a, 15);  
    push(&a, 10);  
    push(&a, 5);  
    push(&a, 20);  
    push(&a, 3);  
    push(&a, 2);   
  
    /* Sort the above created Linked List */  
    MergeSort(&a);  
  
    printf("\n Sorted Linked List is: \n");  
    printList(a);             
  
    return 0;  
}  
技術分享圖片

時間復雜度為O(nLogn)。
貌似MergeSort的時間復雜度為O(nLogn),Split的時間復雜度也為O(nLogn)?當然了,總的時間復雜度還是O(nLogn),但是肯定沒有對數組進行歸並排序快。

數據結構與算法 - 鏈表