数据结构与算法——链表

简介: 链表 题型1:数组和链表的区别是什么? 数组和链表的区别主要表现在以下几个方面: 1)逻辑结构。数组必须事先定义固定的长度,不能适应数据动态地增减。当数组中插入、删除数据项时,需要移动其他数据项。

链表

题型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),但是肯定没有对数组进行归并排序快。
转载:http://blog.csdn.net/lalor/article/details/7430624



相关文章
|
2月前
|
算法
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
69 1
|
2月前
|
算法 索引
❤️算法笔记❤️-(每日一刷-141、环形链表)
❤️算法笔记❤️-(每日一刷-141、环形链表)
50 0
|
2月前
|
算法
【❤️算法笔记❤️】-(每日一刷-876、单链表的中点)
【❤️算法笔记❤️】-(每日一刷-876、单链表的中点)
44 0
|
12天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
30 5
|
1月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
61 4
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
2月前
|
存储 缓存 算法
经典算法之链表篇(三)
经典算法之链表篇(三)
|
2月前
|
算法
经典算法之链表篇(二)
经典算法之链表篇(二)
|
2月前
|
算法 索引
经典算法之链表篇
经典算法之链表篇