• 链表


    1.基本概念

      链表 (Linked List)是一种线性表,但是在内存中不是按照线性的顺序储存数据,是通过每个节点的指针指向下一个节点的指针来链接。相对于顺序储存(例如数组),链表的插入操作更快( O(1) ),但是失去了随机读取的优点。

      链表一般有单向链表,双向链表,循环链表这三种形式。

    2.单向链表

      该种形式是链表中最简单的,每个节点包含了数据域指针域,数据域用来保存该节点的值,指针域用来指向下一个节点(链表的尾巴节点应指向NULL),它保存的是下一个节点的地址。

      一般完整的链表会有头节点,头节点不储存数据。查找数据的话要从头节点开始,每次访问下一个节点直到查找到数据或到达尾部。

      单向链表典型的插入方法有头插法尾插法,头插法只需要一个头指针,而尾插法还需要一个指向尾部的尾指针。

    单向链表的C++实现:

    template<class T>
    struct Node // 节点的数据结构
    {
        T data;
        Node *next;
    
        Node() :next(nullptr){}  // 无参构造
        Node(T t) :data(t), next(nullptr){} // 带参数构造
    };
    
    template<class T>
    class LinkList  //链表类
    {
    private:
        Node<T> *head; // 头指针 (空节点)
        Node<T> *tail; // 尾指针 (空节点)
        int size;
    public:
        LinkList()
        {
            head = new Node<T>;
            tail = new Node<T>;
    
            size = 0;
        }
        ~LinkList()
        {
    
        }
    public:
       /**
        *    在链表的头部插入新节点
        *        @param val:赋给新节点的值
        */
        void insertOnHead(T val);
    
       /**
        *    在链表的尾部插入新节点
        *        @param val:赋给新节点的值
        */
        void insertOnTail(T val); 
    
       /**
        *    在链表的某一位置插入新节点,插入成功返回true,否则返回false
        *        @param i:指定的位置
        *        @param val:赋给新节点的值
        */
        bool insert(int i,T val); 
    
       /**
        *    获取某节点的值,获取成功返回true,否则返回false
        *        @param i:指定的位置
        *        @param val:赋给新节点的值    
        */
        bool getData(int i,T &val); 
    
       /**
        *    判断空,空的话返回true,否则返回false
        */
        bool isEmpty(); 
    
       /**
        *    清空链表
        */
        void clear(); 
    
       /**
        *    打印链表
        */
        void printList();
    };
    
    template<class T>
    void LinkList<T>::insertOnHead(T val)
    {
        Node<T> *newNode = new Node<T>;
        newNode->data = val;
        newNode->next = head->next;
        head->next = newNode;
    
        if (!size)
            tail->next = newNode; // 把尾指针指向第一个节点
        ++size;
    }
    template<class T>
    void LinkList<T>::insertOnTail(T val)
    {
        Node<T> *newNode = new Node<T>;
        newNode->data = val;
        newNode->next = nullptr;
        tail->next->next = newNode;
        tail->next = newNode;
        ++size;
    }
    template<class T>
    bool LinkList<T>::insert(int i,T val)
    {
        if (i <= 0|| i > size)
            return false;
    
        Node<T> *newNode = new Node<T>;
        Node<T> *temp = head;
    
        if (i == size)
            tail->next = newNode; // 如果在最后插入,更新尾指针
    
        for (int j = 0; j <= size; ++j)
        {
            if (i != 0)
            {
                temp = temp->next;
                --i;
            }
            else
            {
                newNode->data = val;
                newNode->next = temp->next;
                temp->next = newNode;
                break;
            }
                
        }
    
        ++size;
        return true;
        
        
    }
    
    template<class T>
    bool LinkList<T>::getData(int i, T &val)
    {
        if (i <= size && i > 0)
        {
            Node<T> *temp = head;
            for (int j = 0; j < i; ++j)
            {
                temp = temp->next;
            }
            val = temp->data;
            return true;
        }
        else
        {
            return false;
        }
    }
    template<class T>
    bool LinkList<T>::isEmpty()
    {
        return size ? false : true;
    }
    template<class T>
    void LinkList<T>::clear()
    {
        Node<T> *tempFront = head->next;  // 指向下一个待删除
        Node<T> *tempBack = head->next;   // 指向待删除的元素
        for (int i = 0; i < size; ++i)
        {
            tempBack = tempFront;
            tempFront = tempFront->next;
            delete tempBack;
        }
        size = 0;
    }
    template<class T>
    void LinkList<T>::printList()
    {
        if (!size)
        {
            cout << "empty link" << endl;
            return;
        }
    
        Node<T> * temp = head->next;
        for (int i = 0; i < size; ++i)
        {
            cout << temp->data << endl;
            temp = temp->next;
        }
    }
    View Code

    2.双向链表

      和单向链表类似,不过双向链表的每个节点包含一个数据域两个指针域,一个前向指针和一个后向指针,相对单链表的优点是可以访问前驱而不用从头节点开始,其结构如下图:

      其实链表的操作是相似的,都是对节点的连接和断开与销毁,不过双向链表需要注意的是增加了对前向指针的操作,新增节点的图示如下:

      双向链表插入的C++示例如下,完整代码见GitHub。

    bool insert(int i,T val)
    {
        if (i <= 0|| i > size)
            return false;
    
        Node *newNode = new Node;
        Node *temp = head;
    
        if (i == size)
            tail->next = newNode; // 如果在最后插入,更新尾指针
    
        for (int j = 0; j <= size; ++j)
        {
            if (i != 0)
            {
                temp = temp->next;
                --i;
            }
            else
            {
                newNode->data = val;
                newNode->pre = temp;
                newNode->next = temp->next;
                temp->next = newNode;
                break;
            }
                
        }
    
        ++size;
        return true;
        
        
    }

    GitHub:https://github.com/whlook/LinkList

    ------------ 转载请注明出处 ------------
  • 相关阅读:
    一个购物网站的思路设计分享
    B/S和C/S的区别(转)
    TreeSet
    计算出给你一个随机乱敲的一个字符串最多的一个
    JavaScript来实现打开链接页面(转载)
    js小数计算小数点后显示多位小数(转)
    java中使用 正则 抓取邮箱
    浅谈 正则表达式
    jQuery中each()、find()、filter()等节点操作方法
    Xcode插件VVDocumenter Alcatraz KSImageNamed等安装
  • 原文地址:https://www.cnblogs.com/whlook/p/6551474.html
Copyright © 2020-2023  润新知