鏈表總結之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

 

 

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