数据结构之链表-链表实现及常用操作(C++篇)

数据结构单链表基本操作(C/C++实现) 数据结构-单链表-基本操作(C/C++实现) 注意:本代码为了测试运行默认含有操作所需数据,如有需要可自己增删改相关数据 涉及基本运算 初始化单链表 依次采用尾插法插入元素 输出单链表 输出单链表的长度 判断单链表是否为空 输出单链表的第n个元素 输出元素x的位置 在第n个元素位置上插入x元素 输出单链表 删除单链表的第n个元素 输出单链表 释放单链表 GitHub地址(包含.cpp文件和可执... 阅读详情

之前刷leetcode和剑指offer,看到很多需要用到链表的题,但没看到什么全面总结链表的文章,刚看到一篇总结链表的,很详细,特别好。

出处:http://www.cnblogs.com/byonecry/p/4458821.html

自己在此之上再进行了补充

数据结构之链表-链表实现及常用操作(C++篇)

0.摘要

  • 链表数据结构
  • 插入节点(单向链表)
  • 删除节点(单向链表)
  • 反向遍历链表
  • 找出中间节点
  • 找出倒数第k个节点
  • 翻转链表
  • 判断两个链表是否相交,并返回相交点
  • 判断链表是否有环路,获取连接点,计算环的长度
  • 二叉树和双向链表转化

1.数据结构

1.1单向链表

单向链表的节点包括:

  1. 数据域:用于存储数据元素的值。
  2. 指针域(链域):用于存储下一个结点地址或者说指向其直接后继结点的指针。

    struct Node{
        int value;
        Node * next;
    };

1.2双向链表

双向链表的节点包括:

  1. 数据域:用于存储数据元素的值。
  2. 左指针域(左链域):用于存储上一个结点地址或者说指向其直接前继结点的指针。
  3. 右指针域(右链域):用于存储下一个结点地址或者说指向其直接后继结点的指针。

    struct DNode{
        int value;
        DNode * left;
        DNode * right;
    };

2.常用操作例题

2.1插入节点(单向链表)

//p节点后插入值为i的节点
void insertNode(Node *p, int i){
    Node* node = new Node;
    node->value = i;
    node->next = p->next;
    p->next = node;
}

2.2删除节点(单向链表)

当需要删除一个节点p时,只需要将p的前一个节点的next赋为p->next,但是由于是单向的,只知道p,无法知道p前一个节点,所以需要转换思路。将p下一个的节点覆盖到p节点即可,这样就等效于将p删除了。

void deleteNode(Node *p){
    p->value = p->next->value;
    p->next = p->next->next;
}

2.3反向遍历链表

法一:反向遍历链表就类似于事先遍历的节点后输出,即“先进后出”,那么可以将链表遍历存放于栈中,其后遍历栈依次弹出栈节点,达到反向遍历效果。

//1.借助stack
void printLinkedListReversinglyByStack(Node *head){
    stack<Node* > nodesStack;
    Node* pNode = head;
    //遍历链表
    while (pNode != NULL) {
        nodesStack.push(pNode);
        pNode = pNode->next;
    }
    while (!nodesStack.empty()) {
        pNode=nodesStack.top();
        printf("%d\t", pNode->value);
        nodesStack.pop();
    }
}//2.递归
void printLinkedListReversinglyRecursively(Node *head){
    if (head!=NULL) {
        if (head->next!=NULL) {
            printLinkedListReversinglyRecursively(head->next);
        }
        printf("%d\t", head->value);
    }
}//3.借助vector
vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> value;
        if(head!=NULL){
            value.insert(value.begin(),head->val);
            while(head->next!=NULL){
                value.insert(value.begin(),head->next->val);
                head=head->next;
            }
        }
        return value;
    }

2.4找出中间节点

用slow和fast指针标记,slow每次走一步,fast每次走两步,当fast到尾节点时,slow就相当于总长度的一半,即在中间节点。

//找出中间节点
Node* findMidNode(Node* head){
    Node* slow = head;
    Node* fast = head;
    while (fast->next != 0&&fast->next->next!=0) {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

2.5找出倒数第k个节点

用slow和fast指针标记,fast指针事先走k步,然后slow和fast同时走,当fast到达末节点时,slow在fast的前k个节点,即为倒数第k个节点。

ListNode* FindKthFromTail(ListNode* pListHead,int k)  
{  
    if (pListHead == NULL || k == 0) //防止空的链表  和 k值 等于 0 的情况  
    {  
        return NULL;  
    }  
    ListNode* pAhead = pListHead;  
    ListNode* pBehind = NULL;  
    for (int i = 0; i < k-1; i++)  
    {  
        if (pAhead->m_pNext != NULL)  
        {  
            pAhead = pAhead->m_pNext;  
        }  
        else  
        {  
            return NULL;  
        }  
    }  
    pBehind = pListHead;  
    while(pAhead->m_pNext != NULL)  
    {  
          
        pAhead  = pAhead->m_pNext;  
        pBehind = pBehind->m_pNext;  
    }  
  
    return pBehind;    
} 

2.6翻转链表

题意即为将链表反过来,即,原本为p1-p2-p3翻转为p3-p2-p1。读者需自行画图体会指针操作。

网上有个很好的带图解释,链接如下:

       http://blog.youkuaiyun.com/feliciafay/article/details/6841115

2.7判断两个链表是否相交,并返回相交点

如果两个链表相交,其形状必为y形,而不可以能为x形,即两条链表必有相同的尾节点。首先,计算得到两个链表的长度:m,n,求得两个链表长度之差distance=|m-n|,让较长得那个链表事先走distance步,这样,若是链表相交得话,二者指针必相撞,相撞点即为相交点。

Node* findCrosspoint(Node* l1, Node* l2){
    int m = getLinkedListLength(l1);
    int n = getLinkedListLength(l2);
    int distance=0;
    Node *temp1= l1;
    Node *temp2= l2;
    if (m>n) {
        distance = m - n;
        while (distance>0) {
            distance--;
            temp1=temp1->next;
        }
    } else{
        distance = n - m;
        while (distance>0) {
            distance--;
            temp2 = temp2->next;
        }
    }
    while(temp1!=temp2&&temp1->next!=NULL&&temp2->next!=NULL){
        temp1=temp1->next;
        temp2=temp2->next;
    }
    if(temp1 == temp2){
        return temp1;
    }
    return 0;
}

2.8判断链表是否有环路,获取连接点,计算环的长度

此题很有意思,具体详细请参考:http://www.cnblogs.com/xudong-bupt/p/3667729.html
判断是否含有环:slow和fast,slow指针每次走一步,fast指针每次走两步,若是链表有环,fast必能追上slow(相撞),若fast走到NULL,则不含有环。

//判断是否含有环
bool containLoop(Node* head){
    if (head==NULL) {
        return false;
    }
    Node* slow = head;
    Node* fast = head;
    while (slow!=fast&&fast->next!=NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    if (fast==NULL) {
        return false;
    }
    return true;
}

判断环的长度:在相撞点处,slow和fast继续走,当再次相撞时,slow走了length步,fast走了2*length步,length即为环得长度。

//获得环的长度
int getLoopLength(Node* head){
    if (head==NULL) {
        return 0;
    }
    Node* slow = head;
    Node* fast = head;
    while (slow!=fast&&fast->next!=NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    if (fast==NULL) {
        return 0;
    }
    //slow和fast首次相遇后,slow和fast继续走
    //再次相遇时,即slow走了一圈,fast走了两圈
    int length = 0;
    while (slow!=fast) {
        length++;
        slow = slow->next;
        fast = fast->next->next;
    }
    return length;
}

环得连接点:slow和fast第一次碰撞点到环的连接点的距离=头指针到环的连接点的距离,此式可以证明,详见上面链接。

//获得环的连接点
Node* getJoinpoit(Node* head){
    if (head==NULL) {
        return NULL;
    }
    Node* slow = head;
    Node* fast = head;
    while (slow!=fast&&fast->next!=NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    if (fast==NULL) {
        return NULL;
    }
    Node* fromhead = head;
    Node* fromcrashpoint = slow;

    while (fromcrashpoint!=fromhead) {
        fromhead = fromhead->next;
        fromcrashpoint = fromcrashpoint->next;
    }
    return fromhead;
}

2.9二叉树和双向链表转化

二叉树和双向链表转化指的是,二叉树节点结构和双向链表的结构想类似,只不过二叉树节点的节点存储的两个指针为左右子数,而双向链表存储的是前后节点。题意为将二叉树的某种遍历转化为链表存储。此题很明显该用递归,读者可以画图体会一下指针变化。

二叉树节点或双向链表节点定义:

struct BinaryTreeNode{
    int value;
    BinaryTreeNode* left;
    BinaryTreeNode* right;
};

二叉树的中序遍历转换为双向链表

BinaryTreeNode* convertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInLast){
    if (pNode == NULL) {
        return NULL;
    }
    BinaryTreeNode *pCurrent = pNode;
    if (pCurrent->left != NULL) {
        convertNode(pCurrent->left, pLastNodeInLast);
    }

    pCurrent->left = *pLastNodeInLast;
    if (*pLastNodeInLast != NULL) {
        (*pLastNodeInLast)->right=pCurrent;
    }

    *pLastNodeInLast = pCurrent;
    if (pCurrent->right != NULL) {
        convertNode(pCurrent->right, pLastNodeInLast);
    }
    return NULL;
}

BinaryTreeNode* convertBTToDLL(BinaryTreeNode* root){
    BinaryTreeNode *pLastNodeInLast = NULL;
    convertNode(root, &pLastNodeInLast);
    BinaryTreeNode *pHeadOfList = pLastNodeInLast;
    while (pHeadOfList != NULL && pHeadOfList->left != NULL) {
        pHeadOfList = pHeadOfList->left;
    }
    return pHeadOfList;
}

第9章:群体类和群体数据的组织 实现 main 函数的功能:两个整型链表 A 和 B,分别插入 5 个元素,然后把 B 中的元素加入 A 的尾部。在 main 函数中会声明一个整型队列对象,插入 5 个整数,压入队列,再依次取出并显示出来。预期输出:1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 9 10。根据提示,在右侧编辑器补充 Node 类代码,实现 main 函数里调用的成员函数。根据提示,在右侧编辑器补充代码实现 LinkedList 类。本关任务:编写程序实现节点类 Node,实现链表的基本操作。 阅读详情

相关推荐

实验二 链表基本操作的实现

【实验课程名称】算法与数据结构 【实验项目名称】链表基本操作的实现

数据结构___链表的操作

概要 因为操作的需要现在重新学习了数据结构相关知识,故以此文来总结一下链表的基本操作和基本知识 声明:因个人能力有限,本文仅是个人的学习记录笔记,有错误之处还望指出 数据结构(抽象到具体) 提供类型属性和相关操作的抽象描述。这种抽象的描述被称为抽象数据结构即ADT(Abstract Data Type) 开发一个实现ADT的编程接口(指明数据如何存储和执行的函数) 编写代码的实现接口 具体流程 1.建立抽象 初始化一个空链表 在链表末尾添加一个新项 确定链表是否为空 确定链表是否为满 确定链表中的项数

而塞过的学习之旅 588

QT编程系列1--QT学习前需知

本周学习了一些QT编程方面的知识,学写了几个程序,这里把记录下来。一方面对前面学习的知识做一个总结,便于以后复习、再学习和应用;另一方面也和大家做个交流,有不足的地方请各位看官多指点。   首先是做一些学习之前的交代:一,学习QT需要哪些基础;二,学习QT有哪些用途,QT较其他的开发工具有哪些特点;三,学习QT的思路。

霜月孤鸟&RTEMS&TinyOS 1115

C++中的节点类与链表类及链表操作

链表能使人随时方便的插入删除元素,不太影响其他元素。 .顺序访问的线性群体——链表类 1、链表是一种动态数据结构,可以 用来表示顺序访问的线性群体(即你无法像数组那样通过下标灯光方式直接访问元素,需得知开始坐标往下找)。 2、链表是由系列节点组成的,节点可以在运行时动态生成。 3、每一个节点包括数据域和指向链表中下一个节点的指针(即下一个节点的地址,需要依赖它维持节点与节点间的次序关系)。如果链表每个节点中只有一个指向后继节点的指针,则该链表称为单链表。 注: 在使用数组的时候,我们是靠数组的物理地址的次序

Vergesson 6058

手把手教你用ECharts打造炫酷3D饼图(附完整源码)

本文详细介绍了如何使用ECharts创建炫酷的3D饼图,从环境配置到完整源码实现,涵盖参数解析、视觉优化和交互增强技巧。通过实战案例,帮助开发者快速掌握3D数据可视化技术,提升数据展示效果。

weixin_29031057的博客 280

强化学习赋能宇树机器狗:Isaac Gym与MuJoCo仿真训练全解析

本文详细解析了如何利用Isaac Gym和MuJoCo两大仿真平台为宇树机器狗构建强化学习训练管道。内容涵盖环境配置、模型导入、PPO算法框架设计、奖励函数构建以及从仿真到实机的策略迁移技巧,为开发者提供了一套完整的宇树狗仿真训练实践方案。

zeta9的博客 619

链表基本操作的程序实现(转)

#include #include typedef struct List_Node{    int info;    struct List_Node *next;  }node;//结点结构体 //尾插法建立带头结点的单链表  node* Creat_Node() {    node *head,*pre,*p;    int x;    head=(node*)malloc(size

鬼魅叔叔的小窝 648

华为HCIP大数据备考实战:从题库精析到834分通关策略

本文详细介绍了华为HCIP大数据认证考试的备考策略,包括题库精析、重点章节突破和考场实战经验。作者以834分的高分通过考试,分享了官方教材的三遍阅读法、自建题库技巧以及各章节核心知识点,帮助考生高效备考并顺利通关。

weixin_29169899的博客 149

C++数据结构X篇_04_单向链表框架搭建、实现和测试(链表的定义,常用操作的实现等)

本文以C中struct的形式介绍单向链表,亦提及C++中的实现方法

分享学习成长历程,Winter is coming! 905

Spring Boot 与 Redisson 版本适配深度解析

Spring Boot 与 Redisson 的版本适配是一个动态演进的过程。开发者需要密切关注两者官方的版本更新日志,建立完善的版本验证机制。本文提供的适配方案基于当前最新技术状态(2024年1月),建议在实际生产环境中通过灰度发布策略验证版本兼容性。只有深入理解底层技术原理,才能在分布式系统的星辰大海中稳健航行。延伸阅读Spring Boot 官方迁移指南。

fudaihb的博客 1081

树莓派 5 AI 套件(Hailo)示例

该用户自定义类作为输入传递给回调函数,该函数在管道输出上运行。它用于主程序与回调函数之间的通信。它对 hailo_rpi_common::app_callback_class 进行了扩展,并可根据应用程序的具体情况定制附加变量和方法。

机器人控制与仿真应用 4115

python自动数据结构_Python实现常用的逻辑数据结构

Python实现常用的逻辑数据结构

weixin_39634900的博客 269

【硬件】LT3763中文手册

1)精确控制输入和输出电流;2) 3000:1真彩色PWM™调光;3) ±1.5%电压调节精度;4) ±6%电流调节精度;5)6V至60 V输入电压范围;6)宽输出范围最高可达55V;7)<2µA关断电流;8)用于负载电流热控制的控制引脚;9)输入和输出电流监控和限制;10)开路、短路和C/10故障检测;11)适用于LED应用的PWM驱动器输出;12)热增强型28引脚FE封装。

qq_39200110的博客 489

数据结构:链表及其C++实现

前言 链表是一种非常非常基础的数据结构,本文首先讲解链表的基础知识,然后使用C++的模板实现了一个链表类,并简单实现了常见的插入、删除、查找等算法。 阅读本文需要对C/C++的指针具有一定的了解。 基础知识 链表是一种逻辑上连续,内存上分散的线性表数据结构,其基本单位为结点,每个结点分数据区和指针区,数据区用于存放数据,指针区则用于指向其他结点,通过指针每个结点就被串接成了一条“链子”。

forGemini的博客 1万+

使用R语言绘制Logistic回归模型的亚组分析森林图

亚组分析是一种常用的统计方法,用于探索不同亚组之间的差异,并确定是否存在某些变量对结果的调节作用。在本文中,我们将使用R语言来绘制Logistic回归模型的亚组分析森林图。在该图中,每个亚组都用一个点表示,点的位置表示亚组的均值,而线段表示亚组的置信区间。垂直参考线用于表示效应值为0的点,帮助我们判断亚组之间的差异。希望本文对你有所帮助,让你能够使用R语言绘制Logistic回归模型的亚组分析森林图。是一个二分类的变量,我们希望在亚组分析中探索它与响应变量之间的关系。函数来计算每个亚组的均值,并使用。

PixelNinja的博客 2002

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

C++链表类及链表常用功能实现

使用C++编写链表类,实现以下功能: 建立链表 删除链表 插入元素(链表尾部或者指定位置) 删除元素(指定位置) 查找第K个元素 查找某个元素值是否存在 逆序链表 为了使链表可以用于不同数据类型,因此使用了模板类。 1,节点类 链表的每个节点都是一个独立的单元,每个单元由数据和指向下一节点的指针构成 //定义链表节点 template &amp;amp;lt;class T&amp;amp;gt; class Node {...

chenyr0021 8629

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【数据结构篇C++实现】- 线性表 - 顺序表和链表

线性表知识总结,一文搞懂什么是顺序表与链表,以及常见的操作

陈七的博客,学习知识分享,期待与大家的交流 4258

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

链表【C++数据结构】

本文系统讲解链表指针原理与实现,对比std::list和vector,并深入分析了其在频繁插入/删除场景(如LRU缓存)下的独特性能优势与应用价值。

不会C++、害怕嵌入式且不爱看动漫的小趴菜 1万+

C++实现链表

C++实现链表。本文介绍两种实现方法:一、使用指针自定义实现;二、使用C++ STL(标准模板库)中的list容器实现。

cnds123的专栏 6181

数据结构—单链表的操作与实现—c++代码(含全部代码)

单链表的操作与实现

Initial_Mind的博客 7263

c++数据结构篇——链表

通过文字很难明白链表到底是一种什么样的数据结构,所以我们一起来试着将理解到的东西画出来:试着想想,生活中的铁链是怎么构成的,是不是想到了最基础的部分——链结?所以我们先画个链结吧。然后将很多个 这样的链结首尾相连起来我们就得到了这样一个链子。这时候我们发现,只要用一个指针储存一个链结的头节点(也就是其首地址)在将这个指针放在前一个链结的内部形成尾节点。就能将两个链结连接起来形成一条铁链。再然后,由于指针的存在,所以每个链结就不用紧紧排列在一起。

2301_79702677的博客 1373

数据结构:链表详解 (c++实现)

本文用通俗的语言加上图片展现的形式详细讲解链表,并且附上 C++ 实现代码。

weixin_73162565的博客 3045

利用C++实现双向链表的基本操作

本文采用利用C++实现了对双向链表的基本操作。操作包括:双向链表的构建、链表指定位置的插入、链表指定位置的删除、链表长度的获取、链表指定位置元素的获得及指定元素位置的获得、整体链表的删除。

weixin_43165699的博客 4336
上一篇: C++中基类和派生类的一个应用
下一篇: 机器学习中距离度量的方法
code_caq
博客等级 码龄11年 112粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值