链表

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

posted @ 2017-03-14 23:45  whlook  阅读(2560)  评论(0编辑  收藏  举报