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;
}
//作业:实现双向链表模板
相关文章
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
108 10
|
17天前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
31 4
|
17天前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
29 3
|
20天前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
28 0
|
2月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
18 1
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
47 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
84 2
|
2月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
42 2
|
2月前
|
存储 算法 编译器
【C++】初识C++模板与STL
【C++】初识C++模板与STL
|
2月前
|
算法 Java
数据结构与算法学习六:单向环形链表应用实例的约瑟夫环问题
这篇文章通过单向环形链表的应用实例,详细讲解了约瑟夫环问题的解决方案,并提供了Java代码实现。
24 0