• 【C++链表】


    前言

    人生如逆旅,我亦是行人。


    一、链表的定义

    • 链表是有一系列结点组成的,每个结点包括两个部分:

      1、存储数据元素的数据域;

      2、存储下一个节点地址的指针域。

    struct ListNode
    {
        int val;
        struct ListNode* next;
        ListNode(int x):
        	val(x),next(NUL){
            }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二、基本操作

    1、创建单链表

    步骤:

    1. 创建头节点 head ,并且将当前节点p指向头结点 ( p = head )
    2. 创建下一个节点 q ,当前节点 p 的下一个结点为 q ( p->next = q )
    3. 结点 p 后移一位 ( p = p -> next)
    #include 
    #include
    
    using namespace std;
    
    struct ListNode{
    	int val;
    	struct ListNode* next;
    	ListNode(int x) :
    		val(x), next(NULL){
    	}
    };
    
    int main(){
    	int num;
    	cin >> num;
    	ListNode* head = new ListNode(num);
    	ListNode* p = head;
    	
    	//利用尾插法创建一个链表
    	while (cin >> num){
    		ListNode* q = new ListNode(num);
    		p->next = q; 
    		p = p->next;
    	}
    
    	//遍历这个链表,并输出每个结点的元素
    	ListNode* m = head;
    	while (m != nullptr){
    		cout << m->val << endl;
    		m = m->next;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2、插入节点

    • 判断原链表是否为空,if 为空,将head(头结点指针) 指向新增结点
    • if 不为空,向链表尾部插入新节点
    ListNode* insertNode(ListNode* head, int data)
    {
       //将新插入的数据定义到一个新节点中
       ListNode* newNode = new ListNode(data);
       //定义一个新节点存储头节点
       ListNode* p = head;
       if(p == nullptr)
           head = newNode;
       else
       {
           //一直遍历链表结点,直至遍历到最后一个
        	while(p->next != nullptr)
               p = p->next;
          p->next = newNode; 
       }
       //最后返回链表
       return head;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3、删除节点

    • 空链表
    • 非空链表
      1. 删除节点存在
        • 头节点
        • 非头节点
      2. 删除节点不存在
    ListNode* deleteNode(ListNode* head, int data)
    {
        ListNode* p = head;
        //首先判断是不是空链表
        if(p == nullptr)
            return head;
        else
        {
            //判断是不是删除头结点
            //如果头结点的值等于删除的值
            if(p->val == data)
            {
                head = p->next;
                delete p;
                return head;
            }    
            else
            {
                //如果有该结点且不是头结点,遍历到待删除节点的前一节点
                while(p->next != nullptr && p->next->val != data)
                    p = p->next;
                //遍历完整链表都没有待删除的结点
                if(p->next == nullptr)
                    return head;
                else
                {
                    ListNode* deleteNode = p->next;
                    p->next = deleteNode->next;
                    delete deleteNode;
                    return head;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4、反转链表

    • 假设在 c 开始反转,需要知道 c 前面的 b,还要保留 c 后面的 d。

    • if pNode 为当前的节点,pPrev 是 pNode 前面的节点,pNext 是 pNode 后面的节点,那么:

      while(pNode != nullptr && pNext != nullptr)
      
      • 1
      1. 将 pNode 指向 pPrev (pNode->next = pPrev)
      2. 将 pNode 给 pPrev (pPrev = pNode)
      3. 将 pNext 给 pNode(pNode = pNext)
      while(pNode != nullptr && pNext == nullptr)
      
      • 1
      • 把反转后的头部指向 pNode

        边界条件

    #include
    #include
    using namespace std;
    struct ListNode
    {
    	int val;
        struct ListNode* next;
        ListNode(int x):
        	val(x), next(NULL){
            }
    };
    //反转链表
    ListNode* reverse(ListNode* head)
    {
        ListNode* pPrev = nullptr;
        ListNode* pNode = head;
        ListNode* pReverseHead = nullptr;
        while(pNode != nullptr)
        {
            ListNode* pNext = pNode->next;
            if(pNext == nullptr)
                pReverseHead = pNode;
            pNode->next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        return pReverseHead;
    }
    
    int main()
    {
        int num;
        cin >> num;
        ListNode* head = new ListNode(num);
        ListNode* p = head;
        while(cin >> num)
        {
            ListNode* q = new ListNode(num);
            p->next = q;
            p = p->next;
        }
        //停止输入后
        p->next = nullptr;
        
        ListNode* result = reverse(head);
        ListNode* node = result;
        
        //将结果打印输出
        while(node != nullptr)
        {
            cout << node->val << endl;
            node = node->next;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    5、倒数第 X 个节点

    • 快慢指针:快指针比慢指针会多走 (X-1) 步,当快指针到达终点时,慢指针到达倒数第 X 个节点。(X与链表节点个数的关系
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int x)
    {
        if(pListHead == nullptr || x == 0)
            return nullptr;
        ListNode* pHead = pListHead;
        //判断x是不是超出了链表的长度范围
        for(int i=0; i<x-1; i++)
        {
            if(pHead->next != nullptr)
                pHead = pHead->next;
            else
                return nullptr;
        }
        
        ListNode* pNext = pListHead;
        while(pHead->next != nullptr)
        {
            pHead = pHead->next;
            pNext = pNext->next;
        }
        return pNext;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    6、是否有环 (快慢指针法)

    • 如果有环,找出环的入口节点
    • 判断是否有环:
      • 思想:设置一个快指针和一个慢指针,快指针一次走一步,慢指针一次走两步,如果快指针追上了慢指针,说明链表有环,如果走到链表尾部都没有追上,说明链表无环。(注:快慢指针是否为nullptr的判断
    • 如果有环,找出入口节点:
      • 思想:返回的节点一定在环内,如果找出环中节点的个数count,快指针比慢指针多走count步,那么两个指针相遇时,就是入口节点。
    //判断快慢指针是否相遇
    ListNode* MeetNode(ListNode* pHead)
    {
        ListNode* pNode = pHead;
        //判断链表是否为空
        if(pNode == nullptr)
            return nullptr;
        
        //设置慢指针,不能为nullptr
        ListNode* slowNode = pNode->next;
        if(slowNode == nullptr)
            return nullptr;
        //设置快指针
        ListNode* fastNode = slowNode->next;
        while(fastNode != nullptr && slowNode != nullptr)
        {
            //相遇返回快慢指针,找出入口位置
            if(fastNode == slowNode)
                return fastNode;
            slowNode = slow->next;//走一步
            fastNode = fastNode->next;//走两步
            if(fastNode->next != nullptr)
            {
                fastNode = fastNode->next;
            }
        }
        return nullptr;
    }
    
    //计算环中结点个数
    int Count(ListNode* pMeet)
    {
        int count = 0;
        ListNode* pNode = pMeet;
        while(pNode->next != pMeet)
        {
            count++;
            pNode = pNode->next;
        }
        count++;
        return count;
    }
    
    //计算环的入口节点
    LisNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode* meetNode = MeetNode(pHead);
        if(meetNode == nullptr)
            return nullptr;
        int count = Count(meetNode);
        ListNode* pPrev = pHead;
        ListNode* pNext = pHead;
        
        for(int i=0; i<count; i++)
            pPrev = pPrev->next;
        while(pPrev != pNext)
        {
            pPrev = pPrev->next;
            pNext = pNext->next;
        }
        ListNode* result = pPrev;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    mybatis-plus实现自定义SQL、多表查询、多表分页查询
    手机投屏到笔记本电脑小方法
    【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - 划分子网、构造超网
    事实分布式与价值集中式
    使用FFmpeg合并多个ts视频文件转为mp4格式
    使用 snappyjs 压缩数据并解压
    A-Level经济例题解析及练习Budget Constraint
    JS工具类
    电容式触摸按键功能的实现
    学生Dreamweaver静态网页设计 基于HTML+CSS+JavaScript制作简食餐厅美食网站制作
  • 原文地址:https://blog.csdn.net/WandZ123/article/details/126675700