【线性表】—带头哨兵卫单链表的应用

简介: 【线性表】—带头哨兵卫单链表的应用

目录


前言

实战练习

链表分割

合并两个有序链表

总结

前言


我们之前学过了无头单向非循环链表的实现,但是我们发现,该链表在尾插的时候有一点不好,就是第一次尾插时,会改变头节点,所以我们在上篇文章实现时传的是二级指针。


而本次所讲哨兵卫单链表在尾插时则不用改变头节点。所谓哨兵卫,其实就是带了一个头节点,该节点不作为用来存储数据。如下:


1.png


接下来我们通过具体题目来感受该结构带来的好处。


实战练习


链表分割

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。


思路:假如这道题不是一个链表题,而是一个数组题,我相信大家很多人都会想到这么一个办法,开辟两个数组,然后对原数组进行遍历,<x的放在arr1中,剩下的放在arr2中,然后再将arr2合并到arr1中。

对于链表题我们也可以这么来搞,用两个链表分别链接<x以及其余的,最后再合并两个链表。不过这里我们用哨兵卫单链表实现的话,就不需要考虑到链表是否为空的情况。如下:


2.gif


代码实现:


class Partition {
  public:
    ListNode* partition(ListNode* pHead, int x) {
        //哨兵卫单链表,两个单链表分别存放<x和>x,最后合并
        ListNode* newnode1 = (ListNode*)malloc(sizeof(ListNode));
        ListNode* newnode2 = (ListNode*)malloc(sizeof(ListNode));
        ListNode* tail1 = newnode1;
        ListNode* tail2 = newnode2;
        while (pHead) {
            //<x的放在newnode1链表
            if (pHead->val < x) {
                tail1->next = pHead;
                tail1 = tail1->next;
            }
            //>=x的放在newnode2链表 
            else {
                tail2->next = pHead;
                tail2 = tail2->next;
            }
            pHead = pHead->next;
        }
        //链接两个链表
        tail1->next=newnode2->next;
        tail2->next=NULL;//一定记得置空!否则又可能出现死循环
        struct ListNode*phead=newnode1->next;//保存下一个节点
        //释放
        free(newnode1);
        free(newnode2);
        return phead;
    }
};



这里我们就不需要考虑链表是否为空的情况,会省很多事,因此对于一些单链表的题目,只要涉及到尾插问题,建议使用带头哨兵卫单链表。并且再次强调,一定要多画图!


3.png


合并两个有序链表



这道题,把它想象成一个普通的题,我们应该都会做,即双指针遍历,然后取小尾插。这里需要注意的是,当一方遍历完,另一方没完时,直接将没完的那一方插入即可。这里涉及到尾插,为了不用考虑空表情况下的尾插,我们依然采用哨兵卫单链表。如下:


4.gif


代码实现:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    //开辟一个新节点
    struct ListNode*newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
    newnode->next=NULL;
    struct ListNode*tail=newnode;
    while(list1 && list2)
    {
        //取小尾插
        if(list1->val < list2->val)
        {
            tail->next=list1;
            tail=tail->next;
            list1=list1->next;
        }
        else
        {
            tail->next=list2;
            tail=tail->next;
            list2=list2->next;
        }
    }
    //list2为空
    if(list1)
    {
        tail->next=list1;
    }
    //list1为空
    if(list2)
    {  
        tail->next=list2;
    }
    //保存头节点的下一个节点
    struct ListNode*newhead=newnode->next;
    //释放
    free(newnode);
    return newhead;
}


总结


** 对于需要进行尾插的链表,我们采用带头哨兵卫单链表用来实现尾插,会省事很多,不用考虑空表的存在,不过要注意的是,因为这个哨兵卫节点是我们malloc出来的,所以最后一定记得释放,并且哨兵卫节点的下一个节点才是用来存储有效数据的,另!一定要多画图! **



相关文章
|
2月前
|
存储 Java 索引
【数据结构】链表从实现到应用,保姆级攻略
本文详细介绍了链表这一重要数据结构。链表与数组不同,其元素在内存中非连续分布,通过指针连接。Java中链表常用于需动态添加或删除元素的场景。文章首先解释了单向链表的基本概念,包括节点定义及各种操作如插入、删除等的实现方法。随后介绍了双向链表,说明了其拥有前后两个指针的特点,并展示了相关操作的代码实现。最后,对比了ArrayList与LinkedList的不同之处,包括它们底层实现、时间复杂度以及适用场景等方面。
70 10
【数据结构】链表从实现到应用,保姆级攻略
|
5月前
|
算法
数据结构和算法学习记录——线性表之双向链表(上)-结点类型定义、初始化函数、创建新结点函数、尾插函数、打印函数、尾删函数
数据结构和算法学习记录——线性表之双向链表(上)-结点类型定义、初始化函数、创建新结点函数、尾插函数、打印函数、尾删函数
48 0
|
1月前
|
存储
探索数据结构:单链表的实践和应用
探索数据结构:单链表的实践和应用
|
2月前
|
前端开发 JavaScript C++
详解链表在前端的应用,顺便再弄懂原型和原型链!
该文章深入解析了链表在前端开发中的应用,并详细阐述了JavaScript中的原型和原型链的概念及其工作原理。
|
6月前
|
存储
数据结构第二课 -----线性表之单向链表
数据结构第二课 -----线性表之单向链表
|
3月前
【数据结构】双向带头(哨兵位)循环链表 —详细讲解(赋源码)
【数据结构】双向带头(哨兵位)循环链表 —详细讲解(赋源码)
92 4
|
5月前
|
存储 算法
单链表的应用
单链表的应用
41 6
|
5月前
|
存储 算法 C语言
【链表专题】深入探索链表:文章索引与知识架构(链表的概念、实现、应用、经典例题大合集)
【链表专题】深入探索链表:文章索引与知识架构(链表的概念、实现、应用、经典例题大合集)
|
5月前
链表\链表基础应用
链表\链表基础应用
24 3
|
6月前
|
算法
【数据结构与算法 | 基础篇】模拟LinkedList实现的链表(无哨兵)
【数据结构与算法 | 基础篇】模拟LinkedList实现的链表(无哨兵)