C++学习笔记_13 双向链表和链表模板 2021-05-06

简介: C++学习笔记_13 双向链表和链表模板 2021-05-06
// C++学习笔记_13 双向链表和链表模板
#include<cstdio> 
#include<iostream>
using namespace std;
class AAA
{
private:
    int x;
    int y;
public:
    AAA() :x(0), y(0) {}
    AAA(int a, int b) :x(a), y(b){}
    friend ostream&  operator << (ostream &os, const AAA &A)
    {
        os << "(" << A.x << ", " << A.y << ")";
        return os;
    }
};
template <typename T>
class CDbList
{
private:
    class Node
    {
    public:
        T data;
        Node *pNext;
        Node *pPrev;
        Node(T t_data) :data(t_data), pNext(NULL), pPrev(NULL){}
    };
    //头结点和尾节点都不放数据
    Node *pHead; //链表的头结点
    Node *pTail; //链表的尾节点
    size_t  iSize; //链表中节点个数 
public:
    CDbList() :iSize(0), pHead(new Node(T())), pTail(new Node(T()))
    {
        //连接头尾节点,构成一个中间无数据节点的双向链表
        pHead->pNext = pTail;
        pTail->pPrev = pHead;
    }
    ~CDbList()
    {
        clear();
        delete pHead;
        delete pTail;
    }
    size_t size(){return iSize;}
    //增删查
    int insert(size_t pos, T data);
    int erase(size_t pos);
    T& getValue(size_t pos);
    //清除所有节点数据,释放资源 (保留头尾节点)
    void clear();
    void reverse();
    void Print()
    {
        Node *pNode = pHead->pNext;
        while (pNode != pTail)
        {
            cout << pNode->data << " ";
            pNode = pNode->pNext;
        }
        cout << endl;
    }
    void rPrint()
    {
        Node *pNode = pTail->pPrev;
        while (pNode != pHead)
        {
            cout << pNode->data << " ";
            pNode = pNode->pPrev;
        }
        cout << endl;
        return;
    }
};
template <typename T>
int CDbList<T>::insert(size_t pos, T data)
{
    //pos 从 0 还是编号
    if (pos > iSize){return -1; }//越界
    Node *pNewNode = new Node(data);
    //寻找待插入位置的前一个节点
    int i = 0;
    Node *pNode = pHead;
    while (i < pos){
        pNode = pNode->pNext;i++;
    }
    //考虑一个问题,如果 iSize = 100000,十万个节点了
    //我们想在 99000 个节点位置增加一个节点, 这个查找方式能不能更快一点?
    //---> 从 pTail 位置往前找 
    //  -->if(pos < iSize/2) {从前往后找} else {从后往前找}
    //pNewNode 插入到 pNode 之后
    //顺序从 pNode --- pTmpNode 变为  pNode --- pNewNode --- pTmpNode
    //1:把 pNode 后面那个节点 pTmpNode ,(pPrev)连接到 pNewNode (防止断链)
    (pNode->pNext)->pPrev = pNewNode; //给下一个节点的pPrev赋值
    //2:把 pNewNode 的 pNext 赋值为 pTmpNode
    pNewNode->pNext = pNode->pNext;
    //3:把 pNewNode 的 pPrev 赋值为 pNode
    pNewNode->pPrev = pNode;
    //4:把 pNode 的 pNext 指向 pNewNode
    pNode->pNext = pNewNode;
    iSize++;
    return 0;
}
template <typename T>
int CDbList<T>::erase(size_t pos)
{
    //pos 从 0 还是编号
    if (pos > iSize-1){return -1; }//越界
    //寻找待删除节点的前一个节点
    int i = 0;
    Node *pNode = pHead;
    while (i < pos){
        pNode = pNode->pNext;i++;
    }
    //pNode 是待删除节点的上一个节点
    //第一步,保存待删除节点   ---》 释放内存
    Node *pDelNode = pNode->pNext;
    //第二步,连接待删除节点的前后节点(连接 pDelNode->pNext 这个节点和 pNode 节点)
    (pDelNode->pNext)->pPrev = pNode;
    pNode->pNext = (pDelNode->pNext);
    //第三步,释放内存
    delete pDelNode;
    iSize--;
    return 0;
}
template <typename T>
T& CDbList<T>::getValue(size_t pos)
{
    if (pos > iSize - 1){
        //越界,不处理:已经提供了 size 函数给使用者判断是否越界
        //      这里由用户来保存程序不越界
    }
    //寻找节点位置
    int i = 0;
    Node *pNode = pHead->pNext; // 从第 0 个节点开始往后找
    while (i < pos){
        pNode = pNode->pNext;
        i++;
    }
    return pNode->data;
}
template <typename T>
void CDbList<T>::clear()
{
    if (iSize == 0){return; }
    Node *pDelNode = pHead->pNext; //删除所有节点,保留 pHead 和 pTail
    while (pDelNode != pTail){
        //删除节点前,需要保存这个节点的下一个节点
        pHead->pNext = pDelNode->pNext;
        delete pDelNode;
        pDelNode = pHead->pNext;//取出下一个要删除的节点
    }
    pTail->pPrev = pHead; //最后,链表的 pTail->pPrev 要指向 pHead
    iSize = 0;
    return;
}
template <typename T>
void CDbList<T>::reverse()
{
    if (iSize < 2){return;}
    Node *pNode = pTail; 
    Node *pNextNode = pHead->pNext; //第0个节点
    while (pNextNode != pTail) {
       pHead->pNext = pNextNode->pNext;//保存下一个节点
        //依次连接 尾节点和第0个节点,第0个和第一个节点,第一个和第二个......
        pNextNode->pNext=pNode;//最后连接 最后一个节点和pHead 
        pNode->pPrev = pNextNode;
        //替换,进入下一轮节点的连接 
        pNode=pNextNode;
        pNextNode=pHead->pNext;//这个在上面暂存了一下待连接的节点 
    }
    //pNextNode到了pTail位置后,把pNode节点和pHead连接起来 
    pNode->pPrev=pHead;
    pHead->pNext=pNode;
    return;
}
void Test_Dblist()
{
    CDbList<int>  iList;
    CDbList<AAA>  aList;
    int iArr[] = { 1, 3, 5, 7, 9, 11 };
    AAA aArr[] = { AAA(), AAA(1, 0), AAA(1, 1), AAA(2, 1), AAA(2, 2) };
    for (int i = 0; i < sizeof(iArr) / sizeof(iArr[0]); i++)
        iList.insert(i, iArr[i]);
    for (int i = 0; i < sizeof(aArr) / sizeof(aArr[0]); i++)
        aList.insert(i, aArr[i]);
    iList.insert(2, 99);
    aList.insert(2, AAA(3, 3));
    iList.erase(3);
    aList.erase(3);
    cout << "iList:";
    iList.Print();
    cout << "aList:";
    aList.Print();
    cout << endl;
    cout << "Reverse Print:" << endl;
    cout << "iList:";
    iList.rPrint();
    cout << "aList:";
    aList.rPrint();
    cout << endl;
    cout << iList.getValue(0) << endl; //获取某个元素的值
    iList.getValue(0) = 88; //修改某个元素的值
    iList.Print();
    iList.reverse();
    iList.Print();
    cout << "逆向输出:";
    iList.rPrint();
}
int main()
{
    Test_Dblist();
  return 0;
}
目录
打赏
0
0
0
0
3
分享
相关文章
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
5月前
|
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
148 10
|
5天前
|
【c++】模板详解(2)
本文深入探讨了C++模板的高级特性,包括非类型模板参数、模板特化和模板分离编译。通过具体代码示例,详细讲解了非类型参数的应用场景及其限制,函数模板和类模板的特化方式,以及分离编译时可能出现的链接错误及解决方案。最后总结了模板的优点如提高代码复用性和类型安全,以及缺点如增加编译时间和代码复杂度。通过本文的学习,读者可以进一步加深对C++模板的理解并灵活应用于实际编程中。
19 0
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
140 4
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
55 3
|
4月前
|
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
52 0
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
38 1
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
120 2
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
99 0
C++入门6——模板(泛型编程、函数模板、类模板)
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等