數據結構與算法——線性表(鏈式描述)

概述

  • 鏈表特點:
    1、元素在內存中位置隨機(數組元素的地址是數學公式決定的)
    2、每個元素有一個明確的“鏈”指向線性表的下一個元素
  • 鏈表分類:
    1、單向鏈表:x.firstNode指向鏈表首節點,當x.firstNode=NULL時爲空表,最後節點的鏈指針爲NULL;
    2、單向循環鏈表:最後節點的鏈指針指向第一個節點;
    3、雙向鏈表:兩個指針域next和previous,最後節點的next指針爲NULL,首節點的previous指針爲NULL;
    4、雙向循環鏈表:首節點的previous指向尾節點,尾節點的next指針指向首節點。
    注:以上分類均默認沒有頭結點,如果引入頭結點,每個鏈表(包括空表)都至少包括一個節點(頭結點)

單向鏈表性質與實現

1、單向鏈表

  • 一個線性表的鏈式描述
    在這裏插入圖片描述
  • 鏈表的刪除操作
    在這裏插入圖片描述
    兩個步驟:
    (1)找到第二個節點
    (2)把第二個節點和第四個節點鏈接起來
  • 鏈表的插入操作
    在這裏插入圖片描述
    兩個步驟:
    (1)找到索引爲index-1的節點
    (2)改變前後節點的指針

2、C++語言實現

2.1 結構chainNode
存儲數據成員和指針:

template <class T>
struct chainNode
{
	// data members
	T element;
	chainNode<T>* next;

	// methods
	chainNode() {}
	chainNode(const T& element)
	{
		this->element = element;
	}
	chainNode(const T& element, chainNode<T>* next)
	{
		this->element = element;
		this->next = next;
	}
};

2.2 類chain
聲明代碼:

template<class T>
class chain : public linearList<T>
{
	friend linkedDigraph;
	friend linkedWDigraph<int>;
	friend linkedWDigraph<float>;
	friend linkedWDigraph<double>;
public:
	// constructor, copy constructor and destructor
	chain(int initialCapacity = 10);
	chain(const chain<T>&);
	~chain();

	// ADT methods
	bool empty() const { return listSize == 0; }
	int size() const { return listSize; }
	T& get(int theIndex) const;
	int indexOf(const T& theElement) const;
	void erase(int theIndex);
	void insert(int theIndex, const T& theElement);
	void output(ostream& out) const;

protected:
	void checkIndex(int theIndex) const;
	// throw illegalIndex if theIndex invalid
	chainNode<T>* firstNode;  // pointer to first node in chain
	int listSize;             // number of elements in list
};

實現代碼:

template<class T>
chain<T>::chain(int initialCapacity)
{// Constructor.
	if (initialCapacity < 1)
	{
		ostringstream s;
		s << "Initial capacity = " << initialCapacity << " Must be > 0";
		throw illegalParameterValue(s.str());
	}
	firstNode = NULL;
	listSize = 0;
}

template<class T>
chain<T>::chain(const chain<T>& theList)
{// Copy constructor.
	listSize = theList.listSize;

	if (listSize == 0)
	{// theList is empty
		firstNode = NULL;
		return;
	}

	// non-empty list
	chainNode<T>* sourceNode = theList.firstNode;
	// node in theList to copy from
	firstNode = new chainNode<T>(sourceNode->element);
	// copy first element of theList
	sourceNode = sourceNode->next;
	chainNode<T>* targetNode = firstNode;
	// current last node in *this
	while (sourceNode != NULL)
	{// copy remaining elements
		targetNode->next = new chainNode<T>(sourceNode->element);
		targetNode = targetNode->next;
		sourceNode = sourceNode->next;
	}
	targetNode->next = NULL; // end the chain
}

template<class T>
chain<T>::~chain()
{// Chain destructor. Delete all nodes in chain.
	while (firstNode != NULL)
	{// delete firstNode
		chainNode<T>* nextNode = firstNode->next;
		delete firstNode;
		firstNode = nextNode;
	}
}

template<class T>
void chain<T>::checkIndex(int theIndex) const
{// Verify that theIndex is between 0 and listSize - 1.
	if (theIndex < 0 || theIndex >= listSize)
	{
		ostringstream s;
		s << "index = " << theIndex << " size = " << listSize;
		throw illegalIndex(s.str());
	}

}

template<class T>
T& chain<T>::get(int theIndex) const
{// Return element whose index is theIndex.
 // Throw illegalIndex exception if no such element.
	checkIndex(theIndex);

	// move to desired node
	chainNode<T>* currentNode = firstNode;
	for (int i = 0; i < theIndex; i++)
		currentNode = currentNode->next;

	return currentNode->element;
}

template<class T>
int chain<T>::indexOf(const T & theElement) const
{// Return index of first occurrence of theElement.
 // Return -1 if theElement not in list.

   // search the chain for theElement
	chainNode<T>* currentNode = firstNode;
	int index = 0;  // index of currentNode
	while (currentNode != NULL &&
		currentNode->element != theElement)
	{
		// move to next node
		currentNode = currentNode->next;
		index++;
	}

	// make sure we found matching element
	if (currentNode == NULL)
		return -1;
	else
		return index;
}

template<class T>
void chain<T>::erase(int theIndex)
{// Delete the element whose index is theIndex.
 // Throw illegalIndex exception if no such element.
	checkIndex(theIndex);

	// valid index, locate node with element to delete
	chainNode<T>* deleteNode;
	if (theIndex == 0)
	{// remove first node from chain
		deleteNode = firstNode;
		firstNode = firstNode->next;
	}
	else
	{  // use p to get to predecessor of desired node
		chainNode<T>* p = firstNode;
		for (int i = 0; i < theIndex - 1; i++)
			p = p->next;

		deleteNode = p->next;
		p->next = p->next->next; // remove deleteNode from chain
	}
	listSize--;
	delete deleteNode;
}

template<class T>
void chain<T>::insert(int theIndex, const T & theElement)
{// Insert theElement so that its index is theIndex.
	if (theIndex < 0 || theIndex > listSize)
	{// invalid index
		ostringstream s;
		s << "index = " << theIndex << " size = " << listSize;
		throw illegalIndex(s.str());
	}

	if (theIndex == 0)
		// insert at front
		firstNode = new chainNode<T>(theElement, firstNode);
	else
	{  // find predecessor of new element
		chainNode<T>* p = firstNode;
		for (int i = 0; i < theIndex - 1; i++)
			p = p->next;

		// insert after p
		p->next = new chainNode<T>(theElement, p->next);
	}
	listSize++;
}

template<class T>
void chain<T>::output(ostream & out) const
{// Put the list into the stream out.
	for (chainNode<T>* currentNode = firstNode;
		currentNode != NULL;
		currentNode = currentNode->next)
		out << currentNode->element << "  ";
}

// overload <<
template <class T>
ostream & operator<<(ostream & out, const chain<T> & x)
{
	x.output(out); return out;
}

注:鏈表在創建的時候不需要估計元素的最大個數;構造函數有一個表示初試容量的形參initialCapacity,目的是與arrayList相容

3、總結

(1)可以在上述簡單實現的基礎上添加鏈表的成員類Iterator,由於鏈表是單向的,所以只能定義一個向前迭代器
(2)如果想對鏈表操作進行擴充,首先要對抽象數據類型linearList進行擴充,比如添加clear和push_back(添加元素到表尾)等。
例如:爲了快速在表尾插入元素,可以增加數據成員lastNode(指向鏈表結尾點的指針)。需要完成的工作有:聲明一個數據成員lastNode;提供改進的erase和insert代碼;定義在linearList中剩餘的純虛函數;實現clear和push_back方法。

循環列表和頭節點

  • 兩條措施可以讓鏈表更加簡潔和高效:
  1. 單向鏈表尾節點和頭節點鏈接起來構成循環列表;
  2. 鏈表前面添加頭結點

循環鏈表:
在這裏插入圖片描述
帶頭結點的循環列表:
在這裏插入圖片描述

雙向列表

一個節點存在兩個指針,分別指向前驅節點和後繼節點
在這裏插入圖片描述

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