单链表

单链表的设计与操作

#include<iostream>
using namespace std;


//单链表中每个节点的定义
template <typename T>
struct Node
{
    T   data;                               //数据域,存放数据原元素
    Node <T>* next;                         //指针域,指向下一个同类型(和本类型相同)节点。
};

//单链表的定义
template <typename T>
class LinkList
{
public:
    LinkList();                                         //构造函数
    ~LinkList();                                        //析构函数

public:
    bool ListInsert(int i, const T& e);                 //在第i个位置插入指定元素e
    bool ListDelete(int i);                             //删除第i个位置的元素
    bool GetElem(int i, T& e);                          //获得第i个位置的元素值
    int LocateElem(const T& e);                         //按元素值查找其在单链表中第一次出现的位置

    void DispList();                                    //输出单链表中所有元素
    int ListLength();                                   //获取单链表的长度
    bool Empty();                                       //判断单链表是否为空
    void ReverseList();                                 //翻转单链表
    bool InsertPriorNode(Node<T> *pcurr, const T& e);   //在pcurr的前面插入一个节点,节点值为e
    bool DeleteNode(Node<T>* pdel);
private:
    Node<T>* m_head;                                    //头指针(指向链表第一个节点的指针,如果链表有头节点,则指向头结点)
    int m_length;                                       //单链表当前长读(当前有几个元素),为编写程序更方便和提高程序运行效率而引入
};


//通过构造函数对单链表进行初始化
template<typename T>
LinkList<T>::LinkList()
{
    m_head = new Node<T>;                   //先创建一个头结点
    m_head->next = nullptr;
    m_length = 0;                           //头结点不计入单链表差长度


    //如果不带头结点的单链表的初始化
    // m_head = nullptr;
    // m_length = 0; 
}

//在第i个位置(位置编号从1开始)插入指定元素
template<typename T>
bool LinkList<T>::ListInsert(int i, const T& e)
{
    //判断插入位置i是否合法,i的合法值应该是从1到m_length + 1之间
    if(i < 1 || i > (m_length + 1))
    {
        cout << "元素 " << e << " 插入的位置" << i << " 不合适,合法的位置是1到" << m_length + 1 << " 之间" <<endl; 
        return false;
    }
    Node<T>* p_curr = m_head;
    
    //for循环用于找到第i - 1 个节点
    for(int j = 0; j < (i - 1); ++j)        //j从0开始,表示p_curr刚开始指向的是第0个节点(头结点)
    {
        p_curr = p_curr->next;              //pcurr会找到当前要插入的位置,比如要在第二个位置插入,pcurr会指向第一个位置
    }
    Node<T> *node = new Node<T>;            //(1)创建一个新节点
    node->data = e;                        
    node->next = p_curr->next;              //(2)让新节点链上后续链表,因为pcurr->next指向后续的链表节点
    p_curr->next = node;                    //(3)让当前位置链上新节点,因为node指向新节点
    cout << "成功在位置为 " << i << " 处插入元素 " << e << endl;
    m_length++;                             //实际表长加一
    return true;                            
}

//在第i个位置(位置编号从1开始)插入指定元素[不带头结点版本]
// template<typename T>
// bool LinkList<T>::ListInsert(int i, const T& e)
// {
//     if(i < 1 || i > (m_length + 1))
//     {
//         cout << "元素 " << e << " 插入的位置" << i << " 不合适,合法的位置是1到" << m_length + 1 << " 之间" <<endl; 
//         return false;
//     }

//     if(i == 1)                              //插入到第一个位置与插入到其他位置不同,要单独处理
//     {
//         Node<T> *node = new Node<T>;
//         node->data = e;
//         node->next = m_head;
//         m_head = node;                      //头指针质量新插入的第一个节点
//         cout << "成功在位置为 " << i << " 处插入元素 " << e << endl;
//         m_lenth++;                          //实际表长+1
//         return true;
//     }

//     //插入的不是第一个位置
//     for(int j = 1; j < (i - 1); ++j)        //j从0开始,表示p_curr刚开始指向的是第1个节点(头结点)
//     {
//         p_curr = p_curr->next;              //pcurr会找到当前要插入的位置,比如要在第二个位置插入,pcurr会指向第一个位置
//     }
//     Node<T> *node = new Node<T>;            //(1)创建一个新节点
//     node->data = e;
//     node->next = p_curr->next;
//     p_curr->next = node;     
//     cout << "成功在位置为 " << i << " 处插入元素 " << e << endl;
//     m_length++;                             //实际表长加一
//     return true;                     
// }

//在节点pcurr之前插入新节点,新节点数据域元素为e ,
template<typename T>
bool LinkList<T>::InsertPriorNode(Node<T> *pcurr, const T& e)
{   
    if(pcurr == m_head)    //只有一个头结点
    {
        return false;
    }
    Node<T> *node = new Node<T>;
    node->next = pcurr->next;
    pcurr->next = node;
    
    node->data = pcurr->data;
    pcurr->data = e;
    m_length++;
    return true;
}

//删除第i个位置的元素
template<typename T>
bool LinkList<T>::ListDelete(int i)
{
    if(m_length < 1)
    {
        cout << "当前单链表为空,不能删除任何数据" <<endl;
        return false;
    }

    if(i < 1 || i > m_length)
    {
        cout << "删除位置 " << i << " 不合法,合法的位置是1到 " << m_length <<"之间" <<endl; 
        return false;
    }

    Node<T> *p_curr = m_head;
    //for循环用于找到i - 1个节点
    for(int j = 0; j < (i - 1); ++j)    //j从0开始,表示p_curr刚开始指向的是0个节点(头结点)
    {
        p_curr = p_curr->next;          //p_curr会找到当前要删除的位置所代表的节点的前一个节点的位置
    }
    Node<T> *p_willdel = p_curr->next;  //p_willdel指向带删除的节点
    p_curr->next = p_willdel->next;     //第 i - 1个节点的next指针指向了第 i + 1个节点
    cout << "成功删除位置为 " << i << " 的元素,该元素的值为 " << p_willdel->data <<endl;
    m_length--;                         //实际表长-1
    delete p_willdel;
    p_willdel->next = nullptr;
    return true; 

}

template<typename T>
bool LinkList<T>::DeleteNode(Node<T>* pdel)
{
    //删除pdel所指向的节点,请自行添加相关代码
    //1)把pdel后面一个节点的值赋值给pdel
    //2)删除pdel后面那个节点,间接的删除pdel
    //3)注意pdel本身就是最后一个节点的情况
    if(pdel->next == nullptr)
    {
        delete pdel;
    }

    Node<T> *node = pdel->next;
    pdel->data = node->data;
    pdel->next = node->next;
    delete node;
    node = nullptr;
    m_length--;

    return false;
}

//获取第i个位置的元素值
template<typename T>
bool LinkList<T>::GetElem(int i, T& e)
{
    if(m_length < 1)
    {
        cout << "当前单链表为空,不能获取任何数据" <<endl;
        return false;
    }

    if(i < 1 || i > m_length)
    {
        cout << "获取位置 " << i << " 不合法,合法的位置是1到 " << m_length <<"之间" <<endl; 
        return false;
    }
    Node<T>* p_curr = m_head;
    for(int j = 0; j < i; ++j)
    {
        p_curr = p_curr->next;
    }
    e = p_curr->data;
    cout << "成功获取 " << i << " 的元素,该元素的值为 " << e << endl;
    return true;
}

//按元素值查找其在单链表中第一次出现的位置
template<typename T>
int LinkList<T>::LocateElem(const T& e)
{
    Node<T>* p_curr = m_head;
    for(int i = 1; i <= m_length; ++i) 
    {
        if((p_curr->next->data) == e)
        {
            cout << "值为 " << e << " 的元素在单链表中第一次出现的位置为 " << i <<endl;
            return i;
        }
        p_curr = p_curr->next;
    }
    cout << "值为 " << e << " 的元素在单链表中没有找到  " << endl;
    return -1;              //返回-1表示查找失败
}

//输出单链表中所有元素
template<typename T>
void LinkList<T>::DispList()
{
    Node<T> *p = m_head->next;
    while(p != nullptr)
    {
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}

//获取单链表长度
template<typename T>
int LinkList<T>::ListLength()
{
    return m_length;
}

//判断单链表是否为空
template<typename T>
bool LinkList<T>::Empty()
{
    if(m_head->next == nullptr)
    {
        return true;
    }
    return false;
}

//翻转单链表
template<typename T>
void LinkList<T>::ReverseList()
{
    if(m_length <= 1)
    {
        //如果顺序表中没有元素或者自由一个元素,那么不要做任何事情
        cout << "不需要进行翻转" <<endl;
        return;
    }
    

    //至少由两个节点才会走到这里
    Node<T> *pothersjd = m_head->next->next;  //指向从第二个节点开始的后续节点
    m_head->next->next = nullptr;             //把第一个节点的指针域先置空

    Node<T>* ptmp;
    while(pothersjd != nullptr)
    {
        //比如a1,a2,a3,a4共四个节点,第一次执行该循环时指向:
        ptmp = pothersjd;               //ptmp -> a2;
        pothersjd = pothersjd->next;    //pothersjd ->a3;

        ptmp->next = m_head->next;      // a2 -> a1;
        m_head->next = ptmp;            //head -> a2 -> a1 ->nullptr;             
    }

}

//析构函数
template<typename T>
LinkList<T>::~LinkList()
{
    Node<T> *pnode = m_head->next;
    Node<T> *ptmp;
    while(pnode != nullptr)
    {
        ptmp = pnode;
        pnode = pnode->next;

        delete ptmp;
    }
    delete m_head;    //释放头结点
    m_head = nullptr;
    m_length = 0;
}
int main()
{
    LinkList<int> slinkobj;
    slinkobj.ListInsert(1,12);
    slinkobj.ListInsert(1,24);
    slinkobj.ListInsert(3,48);
    slinkobj.ListInsert(2,100);

    //slinkobj.ListDelete(4);
    int temp = 0;
    slinkobj.GetElem(3,temp);
    cout << "get到的值为 " << temp << endl;
    slinkobj.LocateElem(100);
    slinkobj.DispList();

    slinkobj.DispList();
    slinkobj.ReverseList();
    slinkobj.DispList();
    return 0;
}

//额外学到的内容
//已知a2,要求往a2之前插入a5,简单方法是
    //a)往a2后面插入a5
    //b)把a2和a5数据域交换

//如果频繁的向单链表末尾插入新节点,可以考虑引入一个表尾指针。该指针在单链表为空时指向头结点。
//在单链表非空时要注意保持其指向最后一个节点。

//如何不使用前驱节点删除本节点  a1->a2->a3    只知道 a2 如何删除 a2
    //a)把a3的值赋给 a2   (注意是值,而不是地址)          
    //b)删除a3                  
    //c)注意a2是最后一个元素的情况
    //d) a) b)之后 a1-> a2[实际上,a2的值已经是a3了,只是地址没变,间接的删除了a2]  ==> a1 -> a3 (最后的结果看成是这个)

 

posted @ 2022-07-23 22:42  huahuati  阅读(26)  评论(0)    收藏  举报