链表总结之STL中迭代器失效问题

 

            这里主要介绍链表的基本知识,加深对链表的了解,以及关于链表的常见的面试题。最后介绍STL中的迭代器失效的问题

    一、链表基础知识

        1.概念

        链表是一种物理存储结构上非连续/非顺序的存储结构。链表的每个结点里面存储着下一个结点的指针,把存储数据元素的数据串链起来。

2.结点组成 

        数据域:存储数据元素

        指针域:存储下一个结点地址的指针域  

3.分类

        单链表:是一种逻辑上的线性结构,只有一条链,首尾不相连,方向不会改变。

        双向链表:指针域有两个指针,分别指向它的前驱结点和后继个结点。

        循环链表:尾结点的指针域的指针指向头结点(头尾相连形成一个环形结构) 

4.优缺点

    优点:

    1.创建结点:克服预先知道数据大小的缺点,充分利用计算机空间,实现灵活的内存动态管理。

    2.删除结点:删除结点时只需要修改结点的指针域,不需要将其他数据向前移动。

        注:优缺点一般是相对于顺序存储。

    缺点:

    1.访问结点:通过循环或递归访问到链表的数据,访问效率低于线性数据结构。

    2、存储方面:增加结点的指针域,空间开销比较大。

二、链表的实现

          1.单链表   

              单链表结构

 

 

                 插入结点 

        

 

 

 

Node* BuyNode(DataType x)
{
    Node* node = (Node* )malloc(sizeof(Node));
	node->data  = x;
	node->next = NULL;
	return node;
}
void PushFront(Node** pphead, DataType x)
{
	if(*pphead == NULL)
	{
		*pphead = BuyNode (x);
	}
	else
	{
		Node* node = BuyNode(x);
		node->next = *pphead;
		*pphead = node;
	}
}
void Insert(Node** pphead,Node* pos,DataType x)
{
	assert(pos);
	if(*pphead == pos)//头插
	{
		PushFront(pphead,x);
	}
	else
	{
		Node* prev = *pphead ;
		Node* tmp = BuyNode(x);
		while(prev->next != pos)
		{
			prev = prev->next ;
		} 
		tmp ->next = pos;
		prev->next = tmp;
	}
}

 

        删除结点

void Erase(Node** pphead,Node* pos)
{
	assert(pos);
	if(*pphead == pos)
	{
		PopFront (pphead);
	}
	else
	{
		Node* cur = *pphead;
		while(cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
	}
}

 

            2.双向链表          

 

 

            插入结点

        

void List::Insert(Node* pos,const DataType& x)
{
	//空链表、一个结点、一般情况
	Node* NewNode = new Node(x);
	if(_head == pos)
	{
		if(_head == NULL)
		{
			_head = _tail = NewNode;
		}
		else
		{
			NewNode->_next = _head;
			_head->_prev = NewNode;
			_head = NewNode;
		}
	}
	else 
	{
		Node* prev = pos->_prev ;
		prev->_next = NewNode;
		NewNode->_prev = prev;
		NewNode->_next = pos;
		pos->_prev = NewNode;
	}
}

 


 

        删除结点

    

void List::Erase(Node* pos)
{
	//头删、尾删、一般情况、NULL、一个结点
	if(_head == _tail)//一个结点
	{
		assert(_head);
		_head = _tail = NULL;
	}
	else if(pos == _head)//头删
	{
		_head = _head ->_next ;
		_head ->_prev = NULL;
	}
	else if(pos == _tail)//尾删
	{
		Node* tmp = _tail->_prev ;
		tmp->_next = NULL;
		_tail = tmp;
	}
	else //一般情况
	{
		Node* prev = pos->_prev ;
		Node* next = pos->_next ;
		prev->_next = next;
		next->_prev = prev;
	}
	delete pos;
}

 

 

 

 

 

            3.双向循环链表

        

                插入结点&删除结点

List()
		:_head(new Node(T()))
	{
		_head->_prev = _head;
		_head->_next = _head;
	}

template<class T>
void List<T>::Insert(Node* pos, const T& x)
{
	assert(pos);
	Node* newNode = new Node(x);
	Node* prev = pos->_prev;

	prev->_next = newNode;
	newNode->_prev = prev;
	newNode->_next = pos;
	pos->_prev = newNode;
}

template<class T>
void List<T>::Erase(Node* pos)
{
	assert(pos && pos != _head);

	Node* prev = pos->_prev ;
	Node* next = pos->_next ;

	delete pos;

	prev->_next = next;
	next->_prev = prev;
}

 

        三、STL中List

                1.各接口

                2.迭代器失效问题

                    3.实现(迭代器实现)

template<class T, class Ref, class Ptr>  //通过实例化的类型不同,实现不同的迭代器
struct _ListIterator  
{
	typedef _ListIterator<T,Ref, Ptr> Self;
	typedef ListNode<T> Node;
	Node* _node;

	_ListIterator(Node* node) //构造函数
		:_node(node)
	{}

	Ref operator* () //*操作符重载
	{
		return _node->_data;
	}

	Self& operator++()  //后置++
	{
		_node = _node->_next;
		return *this; 
	}

	Self operator++(int)//前置++
	{
		Self tmp(*this);
		_node = _node->_next;
		return tmp; //返回的是临时变量的值,所以用Self
	}

	Self& operator--() //后置--
	{
		_node = _node->_prev;
		return *this;
	}

	Self operator--(int) //前置--
	{
		Self tmp(*this);

		_node = _node->_prev;
		return tmp;
	}

	bool operator != (const Self&s )
	{
		return _node != s._node;
	}

	bool operator == (const Self&s)
	{
		return _node == s->_node;
	}
	
	Ptr operator->()
	{
		return &(_node->_data); //当指向结构体时,具有 -> 作用
	}

};

 

        总结:从单链表到双向循环链表,可以发现双向循环链表简化增加和删除操作,具体使用哪种链表还依情况而定。对于STL中的list可以说极大的方面了用户操作,迭代器也很好的实现了封装。

 

本文只是对链表的简单介绍,有关错误欢迎指出。

有关于链表的具体实现详见:https://github.com/zwjuan/List

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章