2.算法进阶——链表 详解

本文深入探讨了单链表和双向循环链表的数据结构。单链表因其简单和方便的增删改操作在特定场景下适用,但随机访问效率较低。双向循环链表则提供了前向和后向遍历的便利,其迭代器实现通过处理节点的next和prev指针。文章详细阐述了链表的初始化、插入、删除操作,并强调了在插入和删除时处理节点指针的重要性。此外,还介绍了如何利用这些基本操作实现list的pop_front、pop_back和push_back功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单链表

我们日常接触最多的就是单链表。
其优点很明显,增删改很方便。
但非连续存储的方式,导致了我们随机访问第i个元素的时候必须从头遍历,时间复杂度为O(N)
所以单链表比较合适于以下频繁的场景:

  • 插入/删除
  • 遍历

但不适用于随机访问频繁的场景。

list的实现——基于双向循环链表

这个我们之前也提及过:
添加链接描述
在这里插入图片描述
我们之前说list列表基于双向链表实现,但没说细节,我们首先来看一下node的情况:既然是双向链表,很明显会有前后两个指针和一个val值。

template <class T>
struct __list_node {
  __list_node<T>* next;  // 前驱节点指针
  __list_node<T>* prev; // 后继节点指针
  T data; //存储数据
};

我们之前还说指针迭代器的问题,我们来看一下迭代器具体怎么实现的:


template<typename T>
struct __list_iterator{
    typedef __list_iterator<T>   self;
    typedef __list_node<T>*      link_type;
    link_type ptr; //成员
    __list_iterator(link_type p = nullptr):ptr(p){}
}

T& operator *(){return ptr->data;}
T* operator ->(){return &(operator*());}
// 类似 ++x 返回next节点
self& operator++(){
    ptr = ptr->next;
    return *this;
}
// 类似 x++ 返回当前节点
self operator++(int){
    self tmp = *this;
    ++*this;
    return tmp;
}
// 类似 --x 返回prev节点
self& operator--(){
    ptr = ptr->prev;
    return *this;
}
// 类似 x-- 返回当前节点
self operator--(int){
    self tmp = *this;
    --*this;
    return tmp;
}
bool operator==(const __list_iterator& rhs){
    return ptr == rhs.ptr;
}
bool operator!=(const __list_iterator& rhs){
    return !(*this==rhs);
}

注意重载++和–的时候,默认是后置,如果要重载前置需要添加&符号。
后置返回的都是先保存当前节点,改变this指针后返回当前节点。
而前置只需要移动当前指针而后返回即可。
我们可以看见迭代器,主要是对ptr这个成员变量进行处理。

具体实现list:


    template<typename T>
    class list{
        protected:
            typedef __list_node<T> list_node; // 显示定义list_node类型
            typedef allocator<list_node> nodeAllocator; // 定义allocator类型
        public:
            typedef T                  value_type;
            typedef T&                 reference;
            typedef value_type*        pointer;
            typedef list_node*         link_type;
            typedef const value_type*  const_pointer;
            typedef size_t             size_type;
        public:
            typedef __list_iterator<value_type> iterator; // 迭代器类型重写
        private:
            link_type node; // 只要一个指针,便可表示整个环状双向链表
            // ......
    }

我们看private里的node指针,我们知道在单链表里,我们习惯用dummy node来表示一个虚拟的头部,这里我们用虚拟节点node来标识循环链表的首位连接点,其pre为最后一个节点,其next为第一个节点。
初始化的时候,只会有一个node节点,其pre和next都指向自己。

list的初始化/插入/删除

初始化:包含一个虚拟的节点node,其首尾都指向自己


void empty_initialize() {
  node = get_node(0);
  node->next = node; // next 指针指向自身
  node->prev = node; // prev 指针指向自身
}

link_type get_node() { return list_node_allocator:allocate(); }

insert函数:传参需要两个,一个val和一个迭代器以表明插入位置:
这需要做到:

  • 创建一个临时节点temp
  • 使temp的pre&next正确指向
  • 使原来pre的next指向temp,使原来next的pre指向temp

iterator insert(iterator position, const T& x) {
  lik_type tmp = create_node(x); // 创建一个临时节点
  tmp->next = position.node; // 将该节点的后继指针指向当前位置的节点
  tmp->prev = position.node->prev; // 将该节点的前驱指针指向当前位置的前驱节点
  (link_type(position.node->prev))->next = tmp; // 将前驱节点本来指向当前节点的后继指针改为指向该临时节点
  position.node->prev = tmp; // 同样,当前位置的前驱指针也要修改为指向该临时节点
  return tmp;
}

这在我们刷题的时候也经常用到:
穿针引线法:
注意一定要先把新节点的指针指上去之后,再把旧节点的指针指过来

我们再看删除erase:


iterator erase(iterator position) {
  link_type next_node = link_type(position.node->next);
  link_type prev_node = link_type(position.node->prev);
  prev_node->next = next_node;
  next_node->prev = prev_node;
  destroy_node(position.node);
  return iterator(next_node);
}

先将前后节点连在一块儿,再释放待删除节点。

有了Insert和erase,我们可以用这两个函数以及begin&end这两个迭代器,实现pop_front/pop_back/push_back,思路是在指定位置进行insert/erase

注意begin节点指向虚拟节点的next,即第一个node。而end指向虚拟节点,即最后一个node的next
比如:

void pop_front() {erase(begin())};
void pop_back() {
	iterator temp=end();
	erase(--temp);//注意要先--temp,因为end指向虚拟节点,并不是最后一个node
}
push_back(const T&x) {insert(end(),x);}

注意pop_back()要先–temp,因为end指向虚拟节点,并不是最后一个node

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值