《顺序表和单链表的区别及应用场景+单链表经典例题》

来源:互联网 发布:php手机投票系统源码 编辑:程序博客网 时间:2024/06/05 17:15

前言:

    线性表可分为:顺序表与链表。其中,顺序表又可分为动态的和静态的两种,链表可分为单向链表、单向循环链表、双向链表、双向循环链表等。本篇文章主要讲解动态顺序表与单向链表的区别与应用场景以及关于链表的一些经典例题。

正文:


顺序表与单链表的区别:

  • 顺序表可以实现下标的快速访问,单链表则不可以,单链表必须从头依次遍历查找。
  • 顺序表在中间或者头部插入节点时必须依次挪动后面节点到下一个节点位置,然而单链表却不用,单链表插入、删除节点比较方便。
  • 顺序表每次增容固定大小的空间有可能造成空间浪费,但不用每次插入时都动态开辟空间,单链表每次插入节点时都必须动态开辟空间、删除节点时必须释放掉动态开辟的空间。
  • 由于计算机设计的多级缓存机制遵循局部性原理,所以连续访问顺序表时缓存命中率较高,而单链表本身存储比较分散,连续访问时缓存命中率较低还会造成缓存污染。

顺序表与单链表的应用场景:

  • 顺序表:
    • 尾插尾删较多使用顺序表。
  • 单链表:
    • 头部或者中间插入较多使用单链表。

单链表经典例题:

//1.从尾到头打印单链表//2.删除一个无头单链表的非尾节点//3.在无头单链表的一个节点前插入一个节点//4.单链表实现约瑟夫环//5.逆置 / 反转单链表//6.单链表排序(冒泡排序&快速排序)//7.合并两个有序链表, 合并后依然有序//8.查找单链表的中间节点,要求只能遍历一次链表//9.查找单链表的倒数第k个节点,要求只能遍历一次链表

基本函数:

#include<stdio.h>#include<assert.h>#include<stdlib.h>typedef int DataType;//节点中数据类型typedef struct ListNode//节点数据结构{    DataType data;    struct ListNode *next;} ListNode;ListNode *Find(ListNode *plist,DataType x)//查找函数,返回查找到节点的地址{    ListNode *cur = plist;    while (cur)    {        if ((cur->data) == x)        {            return cur;        }        cur = cur->next;    }    return cur;}ListNode *BuyNode(DataType x)//产生一个节点并把节点中的数据置数,返回节点地址{    ListNode *plist = (ListNode *)malloc(sizeof(ListNode));    if (plist != NULL)    {        plist->data = x;        plist->next = NULL;        return plist;    }    return NULL;}void PrintList(ListNode *plist)//打印单链表{    assert(plist != NULL);    while (plist)    {        printf("%d->",plist->data);        plist = plist->next;    }    printf("NULL\n");}void PushBuck(ListNode **pplist,DataType x)//在函数尾部插入节点{    ListNode *cur = NULL;    assert(pplist);    cur = *pplist;    if (*pplist == NULL)    {        *pplist = BuyNode(x);        return;    }    while ((cur->next) != NULL)    {        cur = cur->next;    }    cur->next = BuyNode(x);}

从尾到头打印单链表:

void PrintTailToHead(ListNode *plist)//从尾到头打印单链表(采用递归实现){    if (plist == NULL)        return;    PrintTailToHead(plist->next);    printf("%d->", plist->data);}

测试贴图:

这里写图片描述


删除一个无头单链表的非尾节点:

void EraseNonTail(ListNode *pos)//删除一个无头单链表的非尾节点{    ListNode *tmp = NULL;    assert(pos);    assert(pos->next);    pos->data = pos->next->data;    tmp = pos->next;    pos->next = pos->next->next;    free(tmp);    tmp = NULL;}

测试贴图:
这里写图片描述


在无头单链表的一个节点前插入一个节点:

void InsertBeforeNode(ListNode *pos,DataType x)//在无头单链表的一个节点前插入一个节点{    ListNode *tmp = NULL;    DataType ret = 0;    assert(pos);    tmp = BuyNode(x);    tmp->next = pos->next;    pos->next = tmp;    ret = pos->data;    pos->data = tmp->data;    tmp->data = ret;}

测试贴图:
这里写图片描述


单链表实现约瑟夫环:

ListNode *JosephRing(ListNode *plist,int k)//单链表实现约瑟夫环{    int count = 0;    ListNode *tmp = 0;    assert(plist);    while (plist != plist->next)    {        count = k;        while (--count)        {            plist = plist->next;        }        plist->data = plist->next->data;        tmp = plist->next;        plist->next = plist->next->next;        free(tmp);        tmp = NULL;    }    return plist;}

测试贴图:
这里写图片描述


逆置 / 反转单链表:

ListNode *Reverse(ListNode *plist)//逆置 / 反转单链表{    ListNode *phead = NULL;    ListNode *tmp = NULL;    if ((plist == NULL) || (plist->next == NULL))        return plist;    else    {        while (plist)        {            tmp = plist;            plist = plist->next;            if (phead == NULL)            {                phead = tmp;                tmp->next = NULL;            }            else            {                tmp->next = phead;                phead = tmp;            }        }        return phead;    }}

结果贴图:
这里写图片描述


单链表排序:

ListNode *Bubblesort(ListNode *plist)//单链表排序(冒泡排序&快速排序){    if ((plist == NULL) || (plist->next == NULL))    {    }    else    {        ListNode *cur = NULL;        ListNode *next = NULL;        ListNode *tail = NULL;        while (tail != plist->next)        {            cur = plist;            next = plist->next;            while (next != tail)            {                if (cur->data > next->data)                {                    DataType tmp = next->data;                    next->data = cur->data;                    cur->data = tmp;                }                cur = next;                next = next->next;            }            tail = cur;        }        cur = next = tail = NULL;    }    return plist;}

结果贴图:
这里写图片描述


合并两个有序链表:

ListNode *Merge(ListNode *plist1,ListNode *plist2)//合并两个有序链表, 合并后依然有序{    ListNode *tail = NULL;    ListNode *tmp = NULL;    ListNode *plist = NULL;    if (plist1 == NULL)    {        return plist2;    }    if (plist2 == NULL)    {        return plist1;    }    while (plist1 && plist2)    {        if (plist1->data < plist2->data)        {            tmp = plist1;            plist1 = plist1->next;        }        else        {            tmp = plist2;            plist2 = plist2->next;        }        if (plist == NULL)        {            plist = tmp;            tail =ist;        }        else        {            tail->next = tmp;            tail = tmp;        }    }    if (plist1 == NULL)    {        tail->next = plist2;    }    else    {        tail->next = plist1;    }    return plist;}

结果贴图:

这里写图片描述


查找单链表的中间节点:

ListNode *FindMidNode(ListNode *plist)//查找单链表的中间节点,要求只能遍历一次链表{    assert(plist);    ListNode *fast = plist;    ListNode *slow = plist;    while (fast && fast->next)    {        fast = fast->next->next;        slow = slow->next;    }    return slow;}

结果贴图:
这里写图片描述


查找单链表的倒数第k个节点:

ListNode *FindTailKNode(ListNode *plist,int k)//查找单链表的倒数第k个节点,要求只能遍历一次链表{    ListNode *fast = plist;    ListNode *slow = plist;    assert(plist);    while (--k)    {        fast = fast->next;        assert(fast);    }    while (fast->next != NULL)    {        fast = fast->next;        slow = slow->next;    }    return slow;}

结果贴图:
这里写图片描述