数据结构与算法之美【5】-链表

关于链表,首先我们要了解它和数组的一个最大区别:数组需要一块连续的内存空间来存储,而链表恰恰相反,它通过“指针”将一组零散的内存块串联起来使用。而且因为在对链表执行插入和删除操作时无需保持内存数据的连续性,而仅仅是修改一下指定节点的next指针指向,所以对应的时间复杂度是 O(1),但是因为它不支持随机访问,只能通过节点的next指针进行遍历,所以随机访问的时间复杂度是O(n)。

链表结构五花八门,今天重点介绍四种最常见的链表结构:单链表、双向链表、循环链表以及双向循环列表。

目录

一、单链表

二、循环链表

三、双向链表

(1)删除结点中“值等于某个给定值”的结点

(2)删除给定指针指向的结点

四、双向循环链表

五、代码示例

 1、单向不循环链表

2、双向循环链表


一、单链表

为了将所有的结点串起来,单链表的每个结点除了存储数据之外,还需要记录单链表上的下一个结点的位置。我们把这个记录下个结点位置的指针叫作后继指针 next。

从上面的单链表图中,可以明显看出,其中有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点,即头结点和尾结点,其中,头结点用来记录链表的首地址,有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方在于next指针指向的下一个结点位置为空地址 NULL,表示这是链表的结尾。

二、循环链表

循环链表很简单。它跟单链表唯一的区别就是循环链表的尾结点的指针不再指向空地址,而是指向链表的头结点。由此生成循环,如下图:

三、双向链表

单向链表和循环链表都只有一个方向,即只能通过结点的后继指针 next 获取后面的结点。而双向链表则不同,顾名思义,它支持两个方向,每个结点不仅仅有一个后继指针 next ,还有一个前驱指针 prev 指向其前面的结点。

相对于单向链表,虽然双向链表占用的内存更多(多存储了一个前驱指针),但它的插入和删除操作的效率却有明显提升。

可能这里会有些疑问,本篇开头的位置明明写着链表的插入和删除的时间复杂度都是O(1),双向链表的效率还咋提升呢?这里请注意,前面说的时间复杂度都是指的是赋值操作,忽略了其查询的过程,而双向链表的效率提升就体现在其查询过程,下面我们将查询过程也统计下来,重新计算一下复杂度,以删除操作为例:

(1)删除结点中“值等于某个给定值”的结点

对于这种情况,不管是单链表还是双向链表,时间复杂度是一样的,都是O(n)。为了查找到值等于给定值的结点,都需要从头结点开始一个一个依次遍历对比,直到找到值等于给定值的结点,然后再通过我前面讲的指针操作将其删除。尽管单纯的删除操作时间复杂度是 O(1),但遍历查找的时间是主要的耗时点,对应的时间复杂度为 O(n)。根据时间复杂度分析中的加法法则,删除值等于给定值的结点对应的链表操作的总时间复杂度为 O(n),即不管是单链表还是双向链表,在这种情况下,时间复杂度都是O(n)。

(2)删除给定指针指向的结点

对于这种情况,我们已经知道了要删除的结点,假设为q,删除q 需要知道其前驱结点p,将前驱结点的nex指针指向q的后驱结点,即可完成删除,但单链表并不支持直接获取前驱结点,所以为了找到前驱结点,我们还是要从头结点开始遍历链表,直到 p->next==q,此时单项链表的复杂度为O(n)。但是对于双向链表来说就很简单了,因为双向链表中的结点已经保存了指向前驱结点的指针pre,无需再去遍历。所以,此时双向链表的时间复杂度为 O(1)。

四、双向循环链表

双向循环链表仅仅是修改了双向链表最后一个结点的next指针使其指向链表头部,其他的和双向链表完全没有任何区别。

五、代码示例

 1、单向不循环链表

// 单链表.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>

template <class T>
class Node
{
public:
    T data;
    Node(T& item);
    Node<T>* next;
};

template<class T>
class LinkList
{
public:
    LinkList();
    ~LinkList();
    int getSize(void);
    bool IsEmpty(void);
    int gotoNext(void);
    int getPostion(void);
    int InsertNode(T& data);
    int DeleteNode(void);
    void getCurrNodeData(T& data);
    void setCurrNodeData(T& data);
    void clear();
    void print();

private:
    Node<T>* head;
    Node<T>* currNode;
    int size;
    int position;
    void freeNode(Node<T>* p);
};

/* 链表节点构造函数 */
template <class T>
Node<T>::Node(T& item)
{
    data = item;
    next = NULL;
}

/* 链表构造函数 */
template <class T>
LinkList<T>::LinkList()
{
    head = NULL;
    currNode = NULL;
    size = 0;
    position = -1;
}

/* 链表析构函数 */
template <class T>
LinkList<T>::~LinkList()
{
    clear();
}

/* 获取链表长度 */
template <class T>
int LinkList<T>::getSize(void)
{
    return size;
}

/* 判断链表是否为空 */
template <class T>
bool LinkList<T>::IsEmpty(void)
{
    return size==0?true:false;
}

/* 移动到下个节点,返回下个节点的位置值 */
template <class T>
int LinkList<T>::gotoNext(void)
{
    if(NULL == head)
    {
        return -1;
    }

    if(NULL == currNode)
    {
        return -1;
    }
    else
    {
        currNode = currNode->next;
        position++
    }

    return position;
}

/* 获取当前节点的位置 */
template <class T>
int LinkList<T>::getPostion(void)
{
    return position;
}

/* 在当前节点前插入新节点 */
template <class T>
int LinkList<T>::InsertNode(T& data)
{
    Node<T>* p = new Node<T>(data);

    if(0 == size)
    {
        head = p;
        head->next = NULL;
        currNode = p;
        
    }
    else
    {
        p->next = currNode->next;
        currNode->next = p;
        currNode = p;
    }

    size++;
    position++;
    return size;
}


/* 删除当前节点 */
template <class T>
int LinkList<T>::DeleteNode(void)
{
    if(0 == size)
    {
        return -1;
    }
    
    Node<T>* p = head;
    Node<T>* tmp;
    for(int i = 0;i < size;i++)
    {
        if(NULL == p)
        {
            return -1;
        }

        if(p->next == currNode)
        {
            p->next = currNode->next;
            break;
        }

        p = p->next;
    }

    tmp = currNode;

    if(currNode == head)
    {
        head = currNode->next;
    }

    if(NULL == currNode->next)
    {
        position--;
        currNode = p;
    }
    else
    {
        currNode = currNode->next;
    }
    

    freeNode(p);
    size--;
    if(0 == size)
    {
       position = -1;
    }

    return 0;
}

/* 释放指定节点的内存 */
template <class T>
void LinkList<T>::freeNode(Node<T>* p)
{
    if(!p)
    {
        delete p;
    }

    return;
}

/* 获取当前节点的数据 */
template <class T>
void LinkList<T>::getCurrNodeData(T& data)
{
    if(currNode)
    {
        data = currNode->data;
    }

    return ;
}

/* 修改当前节点的数据 */
template <class T>
void LinkList<T>::setCurrNodeData(T& data)
{
    if(currNode)
    {
        currNode->data = data;
    }

    return ;
}

/* 清空链表 */
template <class T>
void LinkList<T>::clear()
{
    if(0 == size)
    {
        return;
    }

    Node<T>* p = head;
    Node<T>* tmp = head->next;
    while(p)
    {
        freeNode(p);
        p = tmp;
        if(tmp)
        {
            tmp = tmp->next;
        }
    }

    head = NULL;
    currNode = NULL;
    size = 0;
    position = -1;

    return;
}

template <class T>
void LinkList<T>::print()
{
    if(0 == size)
    {
        return;
    }

    Node<T>* p = head;
    Node<T>* tmp = head->next;
    while(p)
    {
        std::cout<<p->data<<std::endl;
        p = tmp;
        if(tmp)
        {
            tmp = tmp->next;
        }
    }

    return;
}

int main(int argc, _TCHAR* argv[])
{
    int a = 10,b=20,c=30,d=40,e=50;
    LinkList<int> list;
    list.InsertNode(a);
    list.InsertNode(b);
    list.InsertNode(c);
    list.DeleteNode();
    list.InsertNode(d);
    list.InsertNode(e);
    list.print();
    std::cin.get();
	return 0;
}

2、双向循环链表

#include <iostream>
#include <stdlib.h>

#include "stdafx.h"



template <class T>
class Node
{
public:
    T data;
    Node(T& item);
    Node<T>* next;
    Node<T>* pre;
};

template<class T>
class LinkList
{
public:
    LinkList();
    ~LinkList();
    int getSize(void);
    bool IsEmpty(void);
    int gotoNext(void);
    int setPostion(int pos);
    int getPostion(void);
    int InsertNodeBefore(T& data);
    int InsertNodeAfter(T& data);
    void DeleteNode(void);
    void getCurrNodeData(T& data);
    void setCurrNodeData(T& data);
    void clear();
    void print();

private:
    Node<T>* head;
    Node<T>* currNode;
    int size;
    int position;
    void freeNode(Node<T>* p);
};

/* 链表节点构造函数 */
template <class T>
Node<T>::Node(T& item)
{
    data = item;
    next = NULL;
    pre = NULL;
}

/* 链表构造函数 */
template <class T>
LinkList<T>::LinkList()
{
    head = NULL;
    currNode = NULL;
    size = 0;
    position = -1;
}

/* 链表析构函数 */
template <class T>
LinkList<T>::~LinkList()
{
    clear();
}

/* 获取链表长度 */
template <class T>
int LinkList<T>::getSize(void)
{
    return size;
}

/* 判断链表是否为空 */
template <class T>
bool LinkList<T>::IsEmpty(void)
{
    return size==0?true:false;
}

/* 移动到下个节点,返回下个节点的位置值 */
template <class T>
int LinkList<T>::gotoNext(void)
{
    if(NULL == head)
    {
        return -1;
    }

    if(NULL == currNode)
    {
        return -1;
    }
    else
    {
        currNode = currNode->next;
        position = (position+1+1)%(size);
    }

    return position;
}

/* 将当前节点移动到指定节点值 */
template <class T>
int LinkList<T>::setPostion(int pos)
{
    if(pos >= size||pos < 0)
    {
        return -1;
    }

    position = 0;
    currNode = head;

    for(int i = 0; i < pos;i++)
    {
        position++;
        currNode = currNode->next;
    }

    return position;
}

/* 获取当前节点的位置 */
template <class T>
int LinkList<T>::getPostion(void)
{
    return position;
}

/* 在当前节点前插入新节点 */
template <class T>
int LinkList<T>::InsertNodeBefore(T& data)
{
    Node<T>* p = new Node<T>(data);

    if(0 == size)
    {
        head = p;
        head->next = head;
        head->pre = head;
        currNode = head;
        position++;
    }
    else
    {
        currNode->pre->next = p;
        currNode->pre = p;
        p->next = currNode;
        p->pre = currNode->pre->next; 

        if(head == currNode)
        {
            head = p;
        }
        currNode = p;
    }

    size++;

    return size;
}

/* 在当前节点后插入新节点 */
template <class T>
int LinkList<T>::InsertNodeAfter(T& data)
{
    Node<T>* p = new Node<T>(data);

    if(0 == size)
    {
        head = p;
        head->next = head;
        head->pre = head;   
        currNode = p;
    }
    else
    {
        currNode->next->pre = p; 
        p->next = currNode->next ;
        currNode->next = p;
        p->pre = currNode; 
        currNode = p;
    }

    size++;
    position++;
    return size;
}

/* 删除当前节点 */
template <class T>
void LinkList<T>::DeleteNode(void)
{
    if(0 == size)
    {
        return;
    }

    Node<T>* p = currNode;

    currNode->pre->next = currNode->next;
    currNode->next->pre = currNode->pre;
    if(head == currNode)
    {
        head = currNode->next;
    }

    currNode = currNode->next;
    freeNode(p);
    size--;

    return ;
}

/* 释放指定节点的内存 */
template <class T>
void LinkList<T>::freeNode(Node<T>* p)
{
    if(!p)
    {
        delete p;
    }

    return;
}

/* 获取当前节点的数据 */
template <class T>
void LinkList<T>::getCurrNodeData(T& data)
{
    if(currNode)
    {
        data = currNode->data;
    }

    return ;
}

/* 修改当前节点的数据 */
template <class T>
void LinkList<T>::setCurrNodeData(T& data)
{
    if(currNode)
    {
        currNode->data = data;
    }

    return ;
}

/* 清空链表 */
template <class T>
void LinkList<T>::clear()
{
    Node<T>* p = currNode;

    for(int i = 0;i < size;i++)
    {
        p = p->next;
        freeNode(currNode);
        currNode = p;
    }

    head = NULL;
    currNode = NULL;
    size = 0;
    position = -1;

    return;
}

template <class T>
void LinkList<T>::print()
{
    Node<T>* p = head;

    for(int i = 0;i < size;i++)
    {
        std::cout<<"data = "<<p->data<<std::endl;
        p = p->next;
    }

    std::cout<<"====================================== "<<std::endl;
    return;
}

int main(int argc, _TCHAR* argv[])
{
    int a = 10,b=20,c=30,d=40,e=50;

    LinkList<int> linklist;
    
    linklist.InsertNodeAfter(a);
    linklist.InsertNodeAfter(b);
    linklist.InsertNodeAfter(c);
    linklist.InsertNodeAfter(d);
    linklist.InsertNodeAfter(e);
    linklist.print();

    std::cin.get();
	return 0;
}

运行结果为:

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiang木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值