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;
}
//作业:实现双向链表模板
相关文章
|
3天前
|
存储 编译器 C++
6.C++模板(超全)
6.C++模板(超全)
|
9天前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(下)
从C语言到C++_21(模板进阶+array)+相关笔试题
23 2
|
9天前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(上)
从C语言到C++_21(模板进阶+array)+相关笔试题
22 0
|
13天前
|
存储 算法 C++
高效利用C++ STL库:标准模板库的使用技巧
本文介绍了C++ STL(标准模板库)的高效使用技巧,包括选择合适的容器类型、使用`emplace_back`而非`push_back`、预分配容器空间和范围for循环遍历容器。此外,还讨论了STL算法的运用,如用算法替代手动循环、使用lambda表达式和进行容器操作。通过这些技巧,开发者可以提升C++代码的性能和可读性。
|
13天前
|
程序员 编译器 C++
C++中的模板与泛型编程技术深度解析
C++中的模板与泛型编程技术深度解析
|
13天前
|
测试技术 C语言
如何用C语言实现无头单向非循环链表Single List ?
这篇文档介绍了一个关于单链表数据结构的实现和相关操作。单链表是一种线性数据结构,每个元素(节点)包含数据和指向下一个节点的指针。文档中列出了单链表的图示,并提供了C语言实现单链表的代码,包括动态申请节点、打印链表、头插、尾插、头删、尾删、查找和在特定位置插入或删除节点等函数。 此外,文档还包含了三个测试用例(TestSList1至TestSList4),展示了如何使用这些函数创建、修改和操作单链表。这些测试用例涵盖了插入、删除、查找等基本操作,以及在链表中特定位置插入和删除节点的场景。
21 0
|
14天前
|
算法 C++
c++算法学习笔记 (21) STL
c++算法学习笔记 (21) STL
|
14天前
|
算法 C++
c++算法学习笔记 (20) 哈希表
c++算法学习笔记 (20) 哈希表
|
14天前
|
算法 C++
c++算法学习笔记 (19) 堆
c++算法学习笔记 (19) 堆
|
14天前
|
人工智能 算法 C++
c++算法学习笔记 (18) 约数
c++算法学习笔记 (18) 约数