C++学习笔记_12 单向链表和单向链表模板 2021-04-29

简介: C++学习笔记_12 单向链表和单向链表模板 2021-04-29
// C++学习笔记_12 单向链表和单向链表模板
#include<cstdio> 
#include<iostream>
#include<string>
using namespace std;
//C 语言链表两个问题:
//1: 定义链表的时候,我们需要指定链表中存放的数据类型
//   不同数据类型的链表,需要分别实现
//2: LIST *pNode;  语义容易引起误解
//   它既可以代表一个节点,也可以代表一个链表
//--》1:通过模板类来实现。2:通过封装Node来规避歧义
//这里:头结点不放数据,仅仅表示这是链表的引子
template<typename T>
class CList
{
private:
    class Node
    {
    public:
        T data;
        Node *pNext;
        Node(T t_data) :data(t_data), pNext(NULL){}
    };
    Node *pHead;  //这是链表的头结点
    size_t iSize; //链表的长度(节点个数)
public:
    //T() : 生成一个T的默认值(比如 int 生成一个0,string 生成一个空串,等等)
    CList() :pHead(new Node(T())), iSize(0){}
    ~CList() { clear(); delete pHead;}//释放整个链表 申请的内存
    size_t size() { return iSize; } //获取链表长度
    //增删改查
    int insert(size_t pos, T t_data);
    int erase(size_t pos);
    int modify(size_t pos, T t_data);
    T& getValue(size_t pos);
    void clear(); //清空节点,保留头结点
    void Print() { //遍历链表数据
        Node *pNode = pHead->pNext;
        while (pNode){
            cout << pNode->data << " ";
            pNode = pNode->pNext;
        }
        cout << endl;
    }
};
//模板类成员函数在外部实现,需要加上模板声明
template<typename T>
int CList<T>::insert(size_t pos, T data)//在pos位置前插入节点,节点数据是 data
{
    //  pos 从 0 开始编号
    if (pos > iSize){return -1;}//越界
    Node *pNewNode = new Node(data);
    //找插入位置前面那个节点
    size_t i = 0;
    Node *pNode = pHead; //起始节点
    while (i < pos){
        pNode = pNode->pNext;
        i++;
    }
    //把 pNewNode 插入在 pNode 的后面
    //1:pNewNode 的 pNext 赋值, 指向 pNode 的下一个元素
    pNewNode->pNext = pNode->pNext;
    //2:把 pNode 的 pNext 指向 pNewNode
    pNode->pNext = pNewNode;
    iSize++;
    return 0;
}
template<typename T>
int CList<T>::erase(size_t pos)//删除在pos位置的节点
{
    if (pos > iSize - 1){ return -1; }//越界,节点不存在
    //查找待删除节点的上一个元素、
    size_t i = 0;
    Node *pNode = pHead; //起始节点
    while (i < pos){
        pNode = pNode->pNext;i++;
    }
    Node *pDelNode = pNode->pNext; //待删除节点
    //删除元素,只需要把 pNode 指向 pDelNode 的下一个节点就可以了
    pNode->pNext = pDelNode->pNext;
    delete pDelNode;//释放节点空间
    iSize--;
    return 0;
}
template<typename T>
int CList<T>::modify(size_t pos, T data)//在pos位置修改节点数据为 data
{
    if (pos > iSize - 1) { return -1;}//越界,节点不存在
    //这个时候跟上面有点不一样:不再找上一个节点,直接找需要修改的节点
    int i = 0;
    Node *pNode = pHead->pNext; //从第 0 个节点开始找
    while (i < pos){
        pNode = pNode->pNext; i++;
    }
    pNode->data = data;
    return 0;
}
template <typename T>
T& CList<T>::getValue(size_t pos)//获取pos位置节点的data
{
    //越界判断
    if (pos > iSize - 1){
        //return -1;//这个显然不行,类型和返回值类型不匹配
        //事实上这里返回什么都不合适
        //1:写 assert  --》 比较耗资源,一般在调试的时候使用
        //2:抛出异常   --》 C++比较正常的解决方法 throw out_of_range;
        //3:不做任何处理 --》C 语言的一般处理方式
        //              我们已经对外提供了 size() 这个接口
        //              使用者完全可以判断是否越界 
    }
    //这个时候跟上面有点不一样:不再找上一个节点,直接找需要修改的节点
    int i = 0;
    Node *pNode = pHead->pNext; //从第 0 个节点开始找
    while (i < pos){
        pNode = pNode->pNext;
        i++;
    }
    return pNode->data;
}
template <typename T>
void CList<T>::clear()//释放所有节点 (头结点除外)
{ 
    if (iSize == 0){return;}
    Node *pNode = pHead->pNext;
    while (pNode){ //把pNode脱链, pHead 的pNext 指向 pNode 的下一个节点
        pHead->pNext = pNode->pNext; //1:把下一个待删除节点保存到 pHead->pNext中
        delete pNode; //2:删除节点
        //进入下一个循环,待删除节点后移
        pNode = pHead->pNext;
    }
    iSize = 0;
    return;
}
//C 语言方式实现链表:单链表 -- 保存整数
//我们写链表有两种方式
// 第一种:头结点不放数据,仅仅指明 这是链表的引子
// 第二种:头结点放数据,和正常节点一样
// 这里我们写第二种情况:头结点放数据。
//节点的结构体
typedef struct tagList
{
    int data;                   //数据部分
    struct tagList *pNext;      //指向下一个节点的指针
}LIST;
//增删改查
//初始化: 传入的是 已经存在 的 LIST 对象的地址
int Init_List(LIST **ppList)
{
    *ppList = (LIST*)malloc(sizeof(LIST));
    (*ppList)->data = 0;
    (*ppList)->pNext = NULL;
    return 0;
}
//在 pHead 这个链表中,第 pos 个节点位置,插入节点,节点数据是 data
int Insert_List(LIST **ppHead, int pos, int data)
{
    LIST *pPos = *ppHead;
    int i = 0;
    //如果 pos == 0, 新插入节点做为头结点
    if (pos == 0){
        LIST *pNode = (LIST*)malloc(sizeof(LIST));
        if (pNode == NULL){   return -1;}
        pNode->data = data;
        pNode->pNext = *ppHead;
        *ppHead = pNode;  //新的头结点
        return 0;
    }
    //查找节点的时候,需要考虑是否越界
    //      越界解决:1- 插入失败,2- 插入末尾
    //还需要考虑插入位置,插入 pos 节点前 --》找 pos 前面那个节点
    while (i < pos - 1){
        if (pPos == NULL) { return -1;} //越界
        pPos = pPos->pNext;
        i++;
    }
    //
    LIST *pNode = (LIST*)malloc(sizeof(LIST));
    if (pNode == NULL){return -1;}
    pNode->data = data;
    //新增节点插入 pPos 后面
    pNode->pNext = pPos->pNext; //把 pPos 的下一个节点 作为 pNode 的下一个节点
    pPos->pNext = pNode;   // pPos 的下一个节点指向 pNode
    //这两行不能调换
    return 0;
}
//销毁链表:释放内存
void Destroy_List(LIST **ppHead) { }
void TestList()
{
    CList<int>    iList;
    CList<string> sList;
    for (int i = 0; i < 5; i++){
        iList.insert(i, i*i);
        sList.insert(i, string(3, 'A' + i));
    }
    iList.insert(2, 99);
    iList.erase(4);
    iList.modify(0, 88);
    cout << "iList:";
    iList.Print();
    cout << "sList:";
    sList.Print();
    cout << endl;
    //最后一个数据
    cout << iList.getValue(iList.size() - 1) << endl;
    //getValue 返回一个引用 --> 可以当左值使用,修改成员
    //--》我们可以不要 modify成员
    iList.getValue(0) = 77;
    iList.Print();
    return;
}
int main()
{
    TestList();
    system("pause");
  return 0;
}
//作业:实现双向链表模板
相关文章
|
6月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
10月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
296 0
|
10月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
250 0
|
存储 Python
Python 实现单向链表,和单向链表的反转
链表是一种数据结构,每个节点存储相邻节点的位置信息。单链表中的节点仅存储下一节点的位置。通过Python实现单链表,定义`ListNode`类并关联节点可创建链表。例如,创建A-&gt;B-&gt;C的链表后,可通过反转函数`reverse`将链表反转为CBA。代码展示了如何实现和操作单链表。
315 6
Python 实现单向链表,和单向链表的反转
|
编译器 C++
模板(C++)
本内容主要讲解了C++中的函数模板与类模板。函数模板是一个与类型无关的函数家族,使用时根据实参类型生成特定版本,其定义可用`typename`或`class`作为关键字。函数模板实例化分为隐式和显式,前者由编译器推导类型,后者手动指定类型。同时,非模板函数优先于同名模板函数调用,且模板函数不支持自动类型转换。类模板则通过在类名后加`&lt;&gt;`指定类型实例化,生成具体类。最后,语录鼓励大家继续努力,技术不断进步!
|
编译器 C++
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
260 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
618 5
|
安全 C++
【c++】模板详解(2)
本文深入探讨了C++模板的高级特性,包括非类型模板参数、模板特化和模板分离编译。通过具体代码示例,详细讲解了非类型参数的应用场景及其限制,函数模板和类模板的特化方式,以及分离编译时可能出现的链接错误及解决方案。最后总结了模板的优点如提高代码复用性和类型安全,以及缺点如增加编译时间和代码复杂度。通过本文的学习,读者可以进一步加深对C++模板的理解并灵活应用于实际编程中。
214 0
|
存储 安全 算法
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。