数据结构与算法——链表

链表

链表的概念

  • 链表是一种通过指针串联在一起的线性数据结构
  • 节点由两部分组成,一个数据域,一个是指针域(存放指向下一个节点的指针)
  • 最后一个节点的指针域为 nullptr

链表的类型

  • 单链表
  • 双链表:双链表的每个节点有两个指针域,一个指向前一个节点,一个指向后一个节点
  • 环形链表

链表的存储方式

  • 链表在内存中不是连续分布的,
  • 链表是通过指针域的指针来链接内存中各个节点的

链表的优缺点

优点:
链表可以轻松的添加元素,适用于经常需要添加删除的场景
缺点:
查询速度慢,指针域需要消耗额外的内存控件

链表节点的定义(C++)

struct Node
{
    int m_nValue;
    Node* m_pNext;
    
    Node(int nValue, Node* pNext = nullptr) : m_nValue(nValue), m_pNext(pNext)
    {
        
    }
};

链表类的代码实现

头文件 SingleLinkedList.h

#ifndef SINGLELINKEDLIST_H
#define SINGLELINKEDLIST_H


class SingleLinkedList
{
public:
    const static int ELEMENT_NOT_FIND = -1;

public:
    struct Node
    {
        int m_nValue;
        Node* m_pNext;

        Node(int nValue = 0, Node* pNext = nullptr) : m_nValue(nValue), m_pNext(pNext)
        {
            // ...
        }
    };

public:
    SingleLinkedList();
    ~SingleLinkedList();

public:
    void AddAtHead(int nValue);                                 // 在头部添加节点
    void AddAtTail(int nValue);                                 // 在尾部添加节点
    void Insert(int nValue, int nIndex);                        // 插入节点

    void RemoveAtHead();                                        // 删除头部节点
    void RemoveAtTail();                                        // 删除尾部节点
    void RemoveByValue(int nValue);                             // 删除指定节点(值)
    void RemoveByIndex(int nIndex);                             // 删除指定节点(索引)

    int Get(int nIndex);                                        // 获取 index 的 value
    int IndexOf(int nValue) const;                              // 获取 value 的 index
    bool Empty() const;                                         // 链表是否为空
    bool Contain(int nValue) const;                             // 链表是否包含某个元素
    int Size() const;                                           // 链表长度
    void Print() const;

    void Set(int nValue, int nIndex);                           // 修改 index 节点的 value 值
    void Clear();                                               // 清除链表数据

private:
    Node* FindPreByValue(int nValue);                           // 寻找前驱节点(值)
    Node* FindPreByIndex(int nIndex);                           // 寻找前驱节点(索引)
    void CheckInsertIndex(int nIndex);                          // 检查插入元素的 index
    void CheckIndex(int nIndex);                                // 检查一般操作的 index
    void RemoveNextNode(Node* pPrev);                           // 删除下一个节点(备注:参数为被删除节点的前一个节点)
    void Release();                                             // 释放内存

private:
    Node* m_pDummyHead;                                         // 虚拟头节点
    Node* m_pHead;                                              // 真实头节点
    int m_nSize;                                                // 链表长度
};


#endif // SINGLELINKEDLIST_H

源文件 SingleLinkedList.cpp

#include "SingleLinkedList.h"
#include <stdexcept>
#include <iostream>

using namespace std;

SingleLinkedList::SingleLinkedList() : m_pHead(nullptr), m_nSize(0)
{
    m_pDummyHead = new Node();
}

SingleLinkedList::~SingleLinkedList()
{
    Release();
}

void SingleLinkedList::AddAtHead(int nValue)
{
    // 虚拟头节点的后继便是头结点
    // 我们在插入链表的时候,只要找到这个插入结点位置的前一个节点
    // 一行代码便可搞定插入 pPrev->m_pNext = new Node(nValue, pPrev->m_pNext)
    // 这行代码的意义在于把新的节点放在 pPrev 后面, 将原有的 pPrev 后面节点给挪后一个位置
    // 即使 pPrev->m_pNext = nullptr 也适用

    m_pDummyHead->m_pNext = new Node(nValue, m_pDummyHead->m_pNext);

    // 更新头节点
    m_pHead = m_pDummyHead->m_pNext;
    ++m_nSize;
}

void SingleLinkedList::AddAtTail(int nValue)
{
    Node* pCur = m_pDummyHead;

    // 找到链表的最后一个节点
    while (nullptr != pCur->m_pNext)
        pCur = pCur->m_pNext;

    // 添加链表
    pCur->m_pNext = new Node(nValue);

    // 更新头节点,为什么要更新,因为调用这个函数的时候,链表可能还一个节点没有
    // 只有个虚拟的头节点
    m_pHead = m_pDummyHead->m_pNext;

    ++m_nSize;
}

void SingleLinkedList::Insert(int nValue, int nIndex)
{
    // 检查插入的索引是否合理
    CheckInsertIndex(nIndex);

    if (0 == nIndex)
        return AddAtHead(nValue);

    if (m_nSize == nIndex)
        return AddAtTail(nValue);

    // 找到被插入位置的前驱节点
    Node* pPrev = FindPreByIndex(nIndex);

    // 问题 pPrev 一定不为空么? 答案是一定,为空的可能是 nIndex == m_nSize
    // 在上面代码中已经处理了

    // 这行代码上面有解释
    pPrev->m_pNext  = new Node(nValue, pPrev->m_pNext);
    ++m_nSize;
}

void SingleLinkedList::RemoveAtHead()
{
    // 此时为空链表
    if (nullptr == m_pHead)
        return;

    RemoveNextNode(m_pDummyHead);
}

void SingleLinkedList::RemoveAtTail()
{
    // 此时为空链表
    if (nullptr == m_pHead)
        return;

    Node* pPrev = m_pDummyHead;
    Node* pCur = m_pHead;

    // 让 pCur 指向链表最后一个节点
    while (nullptr != pCur->m_pNext)
    {
        pPrev = pCur;
        pCur = pCur->m_pNext;
    }

    RemoveNextNode(pPrev);
}

void SingleLinkedList::RemoveByValue(int nValue)
{
    // 找到要删除节点的前一个节点
    Node* pPrev = FindPreByValue(nValue);

    RemoveNextNode(pPrev);
}

void SingleLinkedList::RemoveByIndex(int nIndex)
{
    Node* pPrev = FindPreByIndex(nIndex);

    RemoveNextNode(pPrev);
}

int SingleLinkedList::Get(int nIndex)
{
    CheckIndex(nIndex);

    Node* pCur = m_pHead;

    while ((nIndex--) > 0 && nullptr != pCur)
        pCur = pCur->m_pNext;

    return pCur->m_nValue;
}

int SingleLinkedList::IndexOf(int nValue) const
{
    Node* pCur = m_pHead;
    int nIndex = 0;

    while (nullptr != pCur)
    {
        if (pCur->m_nValue == nValue)
            return nIndex;

        pCur = pCur->m_pNext;
        ++nIndex;
    }

    return ELEMENT_NOT_FIND;
}

bool SingleLinkedList::Empty() const
{
    return 0 == m_nSize;
}

bool SingleLinkedList::Contain(int nValue) const
{
    return ELEMENT_NOT_FIND != IndexOf(nValue);
}

int SingleLinkedList::Size() const
{
    return m_nSize;
}

void SingleLinkedList::Print() const
{
    cout << "List Data : [ ";

    Node* pCur = m_pHead;
    while(nullptr != pCur)
    {
        cout << pCur->m_nValue;

        if (nullptr == pCur->m_pNext)
        {
            cout << " ]\n";
            return;
        }
        else
        {
            cout << ", ";
        }

        pCur = pCur->m_pNext;
    }

    cout << " ]\n";
}

void SingleLinkedList::Set(int nValue, int nIndex)
{
    CheckIndex(nIndex);

    // 让 pCur 指向要被修改数据的节点
    Node* pCur = m_pHead;
    while ((nIndex--) > 0 || nullptr != pCur)
        pCur = pCur->m_pNext;

    pCur->m_nValue = nValue;
}

void SingleLinkedList::Clear()
{
    // 备注:虚拟头结点不要删除
    Node* pCur = m_pHead;

    // 一个一个删除节点
    while (nullptr != pCur)
    {
        Node* pDelete = pCur;
        pCur = pCur->m_pNext;
        delete pDelete;
    }

    m_pDummyHead->m_pNext = m_pHead = nullptr;
    m_nSize = 0;
}

SingleLinkedList::Node* SingleLinkedList::FindPreByValue(int nValue)
{
    Node* pPrev = m_pDummyHead;
    Node* pCur = m_pHead;

    while (nullptr != pCur)
    {
        if (nValue == pCur->m_nValue)
            return pPrev;

        pPrev = pCur;
        pCur = pCur->m_pNext;
    }

    // 没找到返回值为 nullptr
    return nullptr;
}

SingleLinkedList::Node* SingleLinkedList::FindPreByIndex(int nIndex)
{
    // nIndex 的值不合理返回值为 nullptr
    if (nIndex < 0 || nIndex >= m_nSize)
        return nullptr;

    Node* pPrev = m_pDummyHead;
    Node* pCur = m_pHead;

    // 让 pCur 节点指向 index 的节点
    while ((nIndex--) > 0 && nullptr != pCur)
    {
        pPrev = pCur;
        pCur = pCur->m_pNext;
    }

    return pPrev;
}

void SingleLinkedList::CheckInsertIndex(int nIndex)
{
    // 插入的位置最大可以为 m_nSize ,也就是链表尾部
    if (nIndex < 0 || nIndex > m_nSize)
        throw out_of_range("CheckInsertIndex(int nIndex) index out of range");
}

void SingleLinkedList::CheckIndex(int nIndex)
{
    if (0 < nIndex || nIndex >= m_nSize)
        throw out_of_range("CheckIndex(int nIndex) index out of range");
}

void SingleLinkedList::RemoveNextNode(SingleLinkedList::Node* pPrev)
{
    if (nullptr == pPrev || nullptr == pPrev->m_pNext)
        return;

    // 找到要被删除的节点
    Node* pDelete = pPrev->m_pNext;

    // 空出要被删除的链表
    pPrev->m_pNext = pPrev->m_pNext->m_pNext;

    // 更新 m_pHead 节点
    m_pHead = m_pDummyHead->m_pNext;

    delete pDelete;
    --m_nSize;
}

void SingleLinkedList::Release()
{
    // 在清除链表的基础上并清除虚拟头节点

    Clear();
    delete m_pDummyHead;
    m_pDummyHead = nullptr;
}

链表的练习

移除链表元素

【题目需求】在链表中删除指定的元素
【示例】
输入:[1,4,2,4], value = 4
输出:[1,2]
解题代码:

//
//  main.cpp
//  ConsoleApp
//
//  Created by Xming on 2022/2/24.
//

#include <iostream>
#include <vector>

using namespace std;

struct Node
{
    int m_nValue;
    Node* m_pNext;
    
    Node(int nValue, Node* pNext = nullptr) : m_nValue(nValue), m_pNext(pNext)
    {
        
    }
};



class Solution
{
public:
    // 删除链表中指定元素的节点
    static Node* RemoveElement(Node* pHead, int nEle)
    {
        Node* pDummyHead  = new Node(0, pHead);
        Node* pPre = pDummyHead;
        Node* pCur = pHead;
        
        while (nullptr != pCur)
        {
            if (pCur->m_nValue == nEle)
            {
                Node* pDelete = pCur;
                pPre->m_pNext = pPre->m_pNext->m_pNext;
                pCur = pCur->m_pNext;
                delete pDelete;
            }
            else
            {
                pPre = pCur;
                pCur = pCur->m_pNext;
            }
        }
        
        pHead = pDummyHead->m_pNext;
        delete pDummyHead;
        return pHead;
    }
    
    // 创建链表
    static Node* CreateList(const vector<int>& vecData)
    {
        if (vecData.empty())
            return nullptr;
        
        Node* pHead = new Node(vecData[0]);
        Node* pCur = pHead;
        for(size_t i = 1; i < vecData.size(); ++i)
        {
            pCur->m_pNext = new Node(vecData[i]);
            pCur = pCur->m_pNext;
        }
        
        return pHead;
    }
    
    // 打印输出链表
    static void PrintList(Node* pHead)
    {
        Node* pCur = pHead;
        cout << "List Data:[ ";
        
        while (nullptr != pCur)
        {
            if (nullptr != pCur->m_pNext)
            {
                cout << pCur->m_nValue << ", ";
            }
            else
            {
                cout << pCur->m_nValue << " ]\n";
            }
            
            pCur = pCur->m_pNext;
        }
    }
    
    // 测试函数
    static void Test()
    {
        vector<int> vecData = { 1, 2, 2, 3, 5 };
        
        Node* pHead = CreateList(vecData);
        PrintList(pHead);
        
        pHead = RemoveElement(pHead, 2);
        PrintList(pHead);
    }
};

int main(int argc, const char * argv[]) {
    
    Solution::Test();
    
    return 0;
}

备注:后续会持续更新链表的相关练习题笔记

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值