线性表 — 链式描述
1. 单向链表
在链式描述中,数据对象实例的每一个元素均用节点来描述,每个节点都明确包含另一个相关节点的位置信息。
单向链表:每个节点只有一个链,从左到右依次连接下一个,最后节点的链域为NULL。
链表的结构定义:
template <class T>
struct chainNode
{
//数据成员
T element; //数据域
chainNode<T> *next; //链域
//方法
chainNode() {}
chainNode(const T& element) {
this->element = element;
}
chainNode(const T& element, chainNode<T>* next) {
this->element = element;
this->next = next;
}
};
类chain用单向链表实现线性表
template <class T>
class chain : public linearList<T> {
public:
chain(int initialCapacity = 10);
chain(const chain<T>&);
~chain();
//抽象数据类型的方法
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;
private:
void chackIndex(int theIndex) const;
chainNode<T>* firstNode; //指向首元素
int listSize; //元素个数
};
构造函数和拷贝构造函数:
template <class T>
chain<T>::chain(int initialCapacity) {
if (initialCapacity < 1) {
ostringstream s;
s << "Initial capacity must be > 0!";
throw illwgalParameterValue(s.str());
}
firstNode = NULL;
listSize = 0;
}
//拷贝构造函数
template <class T>
chain<T>::chain(const chain<T>& theList) {
listSize = theList.listSize;
if (listSize == 0) {
firstNode = NULL;
return;
}
chainNode<T>* sourceNode = theList.firstNode;
firstNode = new chainNode<T>(sourceNode->element);
sourceNode = sourceNode->next;
chainNode<T>* targetNode = firstNode;
while (sourceNode != nullptr) {
targetNode->next = new chainNode<T>(sourceNode->element);
targetNode = targetNode->next;
sourceNode->next = sourceNode->next;
}
targetNode->next = nullptr;
}
析构函数:
析构函数要逐个清除链表的节点,实现方法是:重复清除链表的首个元素,直到链表为空。
//析构函数
template <class T>
chain<T>::~chain() {
while (firstNode != nullptr) {
chainNode<T>* nextNode = firstNode->next;
delete firstNode;
firstNode = nextNode;
}
}
get():返回索引为Index的元素。
template <class T>
T& chain<T>::get(int index) const {
checkIndex(index);
chainNode<T>* curNode = firstNode;
for (int i = 0; i < index; i++) {
curNode = firstNode->next;
}
return curNode->element;
}
indexOf(): 返回元素element首次出现的索引
template <class T>
int chain<T>::indexOf(const T& elememt) const {
chainNode<T>* curNode = firstNode;
index = 0;
while (curNode != nullptr && curNode->element != elememt) {
curNode = curNode->next;
index++;
}
if (curNode == nullptr) {
return -1;
} else {
return index;
}
}
erase():删除索引为index的元素。
可能有三种情况:
- index不合法
- index= 0,即删除首元素
- 删除其他元素。
template <class T>
void chain<T>::erase(int index) {
chackIndex(index);
chainNode<T>* delNode;
if (index == 0) {
delNode = firstNode;
firstNode = firstNode->next;
} else {
chainNode<T>* curNode = firstNode;
for (int i = 0; i < index - 1; i++) { //注意是index - 1
curNode = curNode->next;
}
delNode = curNode->next;
curNode->next = curNode->next->next;
}
listSize--;
delete delNode;
}
index(): 需要先找到索引为index - 1 的节点,然后在该节点后插入。
template <class T>
void chain<T>::insert(int index, const T& val) {
chackIndex(index);
if (index = 0) {
firstNode = new chainNode<T>(val, firstNode);
} else {
chainNode<T>* curNode = firstNode;
for (int i = 0; i < index - 1; i++) {
curNode = curNode->next;
}
curNode->next = new chainNode<T>(val, curNode->next);
}
listSize++;
}
输出:
template <class T>
void chain<T>::output(ostream & out) const {
for (chainNode<T>* curNode = firstNode; curNode != nullptr; curNode = curNode->next) {
out << curNode->element << " ";
}
}
//重载<<
template <class T>
ostream& operator<<(ostream& out, const chain<T>& x) {
x.output(out);
return out;
}
2. 迭代器
class iterator
{
public:
iterator(chainNode<T>* theNode = NULL)
{node = theNode;}
T& operator*() const {return node->element;}
T* operator->() const {return &node->element;}
iterator& operator++() // preincrement
{node = node->next; return *this;}
iterator operator++(int) // postincrement
{iterator old = *this;
node = node->next;
return old;
}
bool operator!=(const iterator right) const
{return node != right.node;}
bool operator==(const iterator right) const
{return node == right.node;}
protected:
chainNode<T>* node;
};
begin() and end()
class iterator;
iterator begin() {return iterator(firstNode);}
iterator end() {return iterator(NULL);}
clear() and push_back()
template<class T>
class extendedLinearList : linearList<T>
{
public:
virtual ~extendedLinearList() {};
virtual void clear() = 0;
virtual void push_back(const T& element) = 0;
};
//clear() 实现
template<class T>
inline void extendedChain<T>::clear()
{
this->listSize = 0;
while (this->first != nullptr)
{
auto next = this->first->next;
delete this->first;
this->first = next;
}
}
//push_back() 实现
template<class T>
void extendedChain<T>::push_back(const T& theElement)
{// Insert theElement at the end of the chain.
chainNode<T>* newNode = new chainNode<T>(theElement, NULL);
if (firstNode == NULL)
// chain is empty
firstNode = lastNode = newNode;
else
{ // attach next to lastNode
lastNode->next = newNode;
lastNode = newNode;
}
listSize++;
}
3. 循环链表和头结点
循环链表:在链表前边加一个头结点,然后将单向链表的尾结点和头结点相连,即可构成循环链表。使用头结点可使程序更简洁和素的更快。
template<class T>
class circularListWithHeader
{
public:
circularListWithHeader();
// some methods
int size() const {return listSize;}
int indexOf(const T& theElement) const;
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>* headerNode; // pointer to header node
int listSize; // number of elements in list
};
template<class T>
circularListWithHeader<T>::circularListWithHeader()
{// Constructor.
headerNode = new chainNode<T>();
headerNode->next = headerNode;
listSize = 0;
}
template<class T>
int circularListWithHeader<T>::indexOf(const T& theElement) const
{
headerNode->element = theElement;
// search the chain for theElement
chainNode<T>* currentNode = headerNode->next;
int index = 0; // index of currentNode
while (currentNode->element != theElement)
{
// move to next node
currentNode = currentNode->next;
index++;
}
// make sure we found matching element
if (currentNode == headerNode)
return -1;
else
return index;
}
双向链表
双向链表支持双向访问,每个节点都包含两个指针(next 和 previous),以此来决定向右查找还是向左查找。
源码见GitHub:线性表–链式描述源码