链表简介
链表是采用链式存储方式实现的一种逻辑上类似于数组的数据结构,其元素之间的逻辑顺序是连续的,但在内存中的物理存储顺序是不连续的,因此不能使用下标的方式读取元素。为了实现数据元素的各种操作,我们把各元素封装成一个结构体——节点(下文都使用此称谓),每个节点内包括一个存储数据的数据成员m_nValue和一个指向该节点的指针m_pNext。
代码实现
节点定义
m_nValue用于存储数据,m_pNext用于指向下一节点,pLinkList为指向ListNode的指针。
typedef struct ListNode{
int m_nValue;
struct ListNode *m_pNext;
}*pLinklist;
封装链表类
——链表类声明
class LinkList
{
public: /*************构造函数************/
LinkList(); //构造空链表
LinkList(unsigned int count, int value); //构造具有count个值为value的链表
LinkList(const LinkList& L); //以另一个对象L构造
~ LinkList(){}; //析构函数,空函数
private: /*************数据成员************/
pLinklist pHead; //指向头节点指针
pLinklist pEnd; //指向尾节点指针
unsigned int _size; //链表长度
public: /*************公有接口************/
bool Empty() const; //检查链表是否为空,空返回true,否则返回false
unsigned int size() const; //返回表长度
void ClearList(); //清空链表
void push_back(int value); //将value插入到表尾
void pop_back(); //删除尾节点
int back() const; //返回尾节点值
int front() const; //返回头节点值
int GetElem(int i) const; //返回第i个位置的值,若i大于size返回0
void InsertElem(int i,int a); //将a插入到第i个位置
pLinklist Begin() const; //返回头节点位置
pLinklist End() const; //返回尾节点的下一个位置
LinkList& operator=(const LinkList& L); //赋值构造函数
};
——链表类各操作定义
构造函数:包括默认构造函数,接受两个参数的构造函数和copy构造函数
LinkList::LinkList() //默认构造函数,构造空表
{
pHead = nullptr;
pEnd = nullptr;
_size = 0;
}
LinkList::LinkList(unsigned int count, int value) //构造具有count个值为value的链表
{
if (count <= 0) //若count<=0,直接返回
return;
_size = 0;
pHead = (pLinklist)new ListNode;
pHead->m_nValue = value;
pHead->m_pNext = nullptr;
_size = count;
pLinklist p = pHead;
pLinklist r = nullptr;
for (int k = 0; k < _size-1; k++)
{
r = (pLinklist)new ListNode;
r->m_nValue = value;
r->m_pNext = nullptr;
p->m_pNext = r;
p = p->m_pNext;
}
pEnd = p;
}
LinkList::LinkList(const LinkList& L) //以另一个链表构造
{
if (L._size == 0) //若L为空表,执行默认构造函数
{
LinkList();
return;
}
_size = L._size;
pLinklist p, r;
p = L.pHead;
pHead = (pLinklist)new ListNode;
pHead->m_nValue = p->m_nValue;
pHead->m_pNext = nullptr;
r = pHead;
for (int i = 0; i < _size - 1; i++)
{
p = p->m_pNext;
pLinklist temp = (pLinklist)new ListNode;
temp->m_nValue = p->m_nValue;
temp->m_pNext = nullptr;
r->m_pNext = temp;
r = r->m_pNext;
}
pEnd = r;
}
Empty(),判断链表是否为空,空返回true,非空返回false。
bool LinkList::Empty() const
{
return (pHead == nullptr) ? true : false;
}
size(),返回链表长度。
unsigned int LinkList::size() const
{
return _size;
}
ClearList(),清空链表。
void LinkList::ClearList()
{
if (_size == 0)
return;
pLinklist r;
while (pHead!=pEnd)
{
r = pHead;
pHead = pHead->m_pNext;
delete r;
_size--;
}
r = pHead;
pHead = pEnd = nullptr;
delete r;
_size--;
}
push_back(int value),往表尾添加一个节点,节点值为value。
void LinkList::push_back(int value)
{
pLinklist p = (pLinklist)new ListNode;
p->m_nValue = value;
p->m_pNext = nullptr;
pEnd->m_pNext = p; //将p添加到当前尾节点指向的下一节点
pEnd = pEnd->m_pNext; //将尾指针指向p
_size++;
}
pop_back(),删除尾节点。
void LinkList::pop_back() //删除尾节点
{
pLinklist p = pHead;
while (p->m_pNext!=pEnd)
{
p = p->m_pNext;
}
pEnd = p; //将尾指针前移一个节点
p = p->m_pNext; //找到尾节点
delete p; //删除尾节点
pEnd->m_pNext = nullptr; //尾节点指针指向空
_size--;
}
front(),back(),返回头、尾节点值。
int LinkList::back() const //返回尾节点值
{
if (!pEnd)
{
return 0;
}
return pEnd->m_nValue;
}
int LinkList::front() const //返回头节点值
{
if (!pHead)
{
return 0;
}
return pHead->m_nValue;
}
GetELem(int i),返回某个位置的值,若不存在则返回0。
int LinkList::GetElem(int i) const //返回第i个位置的值,若i大于size返回0
{
if (!pHead || i > _size) //若链表为空或i大于链表长度,返回0
{
return 0;
}
int count = 1;
pLinklist p = pHead;
while (count<i)
{
p = p->m_pNext;
count++;
}
return p->m_nValue;
}
InsertELem(int i,int a),将a插入到第i位置,若不存在第i位置,则不插入。
void LinkList::InsertElem(int i, int a) //将a插入到第i个位置
{
if (!pHead || i > _size || i <= 0) //若不存在第i位置,则不插入
{
return;
}
if (i == 1)
{
pLinklist r = (pLinklist)new ListNode;
r->m_nValue = a;
r->m_pNext = pHead;
pHead = r;
_size++;
return;
}
int count = 1;
pLinklist p = pHead;
while (count < i - 1)
{
p = p->m_pNext;
count++;
}
pLinklist r = (pLinklist)new ListNode;
r->m_nValue = a;
r->m_pNext = p->m_pNext;
p->m_pNext = r;
_size++;
}
重载运算符‘=’,接受一个LinkList的引用,将‘=’右值L赋值给‘=’左值。注意返回值为LinkLis的引用,即返回this自身。
LinkList& LinkList::operator=(const LinkList& L)
{
if (&L == this) //若L为自身,直接返回
{
return *this;
}
if (L._size == 0) //若L为空,调用默认构造函数
{
LinkList();
return *this;
}
_size = L._size;
pLinklist p, r;
p = L.pHead;
pHead = (pLinklist)new ListNode;
pHead->m_nValue = p->m_nValue;
pHead->m_pNext = nullptr;
r = pHead;
for (int i = 0; i < _size - 1; i++)
{
p = p->m_pNext;
pLinklist temp = (pLinklist)new ListNode;
temp->m_nValue = p->m_nValue;
temp->m_pNext = nullptr;
r->m_pNext = temp;
r = r->m_pNext;
}
pEnd = r;
return *this;
}
封装迭代器类
——迭代器类声明
具有一个私有数据成员,指向节点的指针p。
class LinkList_iterator{
private:
pLinklist _index; //指向链表节点指针
public:
LinkList_iterator(); //默认构造函数,指向空
LinkList_iterator(const pLinklist p); //以另一迭代器构造
~LinkList_iterator(){}; //析构函数,空函数
public:
bool operator==(const LinkList_iterator& rhs) const; //判断this和rhs是否指向同一节点,是返回true,否返回false
bool operator!=(const LinkList_iterator& rhs) const; //判断this和rhs是否指向同一节点,否返回true,是返回false
int operator*() const; //提领该迭代器指向的值,即this->_index->m_nValue
LinkList_iterator& operator++(); //前置++,使迭代器指向下一节点
LinkList_iterator operator++(int); //后置++,使迭代器指向下一节点
};
为了使迭代器可以指向链表的节点并进行遍历,在链表类中添加如下代码。将迭代器类声明为链表类的friend。并为链表类添加两个函数begin()和end(),返回指向链表头节点和尾节点下一位置的迭代器。
class LinkList
{
~
~
~
friend class LinkList_iterator; //将LinkList_iterator声明为friend
public:
typedef LinkList_iterator iterator;
LinkList_iterator begin() const //返回指向链表头节点的迭代器
{
return LinkList_iterator(pHead);
}
LinkList_iterator end() const //返回指向链表尾节点下一位置的迭代器
{
return LinkList_iterator();
}
~
~
~
}
——迭代器类定义
两个构造函数:默认构造函数LinkList_iterator(),该迭代器指向空;LinkList_iterator(const pLinkList p),与指针p指向同一节点。
LinkList_iterator::LinkList_iterator()
{
_index = nullptr;
}
LinkList_iterator::LinkList_iterator(const pLinklist p)
{
_index = p;
}
重载运算符‘==’,判断运算符左值和右值是否指向同一节点,若指向同一节点返回true,否则返回false;重载运算符‘!=’,作用相同返回值相反。
bool LinkList_iterator::operator==(const LinkList_iterator& rhs) const
{
return _index == rhs._index;
}
bool LinkList_iterator::operator!=(const LinkList_iterator& rhs) const
{
return _index != rhs._index;
}
重载‘++’运算符,使迭代器指向下一节点,分为前置和后置。
LinkList_iterator& LinkList_iterator::operator++() //前置
{
_index = _index->m_pNext;
return *this;
}
LinkList_iterator LinkList_iterator::operator++(int) //后置
{
LinkList_iterator temp = *this;
_index = _index->m_pNext;
return temp;
}
重载运算符‘*’,提领迭代器指向的节点值。
int LinkList_iterator::operator*() const
{
return _index->m_nValue;
}
测试代码
int main()
{
LinkList L(3, 3);
LinkList L2;
L2 = L;
L2.push_back(4);
L2.push_back(5);
L2.pop_back();
L2.InsertElem(1, 7);
//L2.ClearList();
if (!L2.Empty())
{
cout << "链表长度:" << L2.size() << "\n" << "元素:";
LinkList::iterator iter = L2.begin();
while (iter != L2.end())
{
cout << *iter << " ";
iter++;
}
cout << "\n" <<"首元素:"<< L2.front() << " "<<"尾元素:" << L2.back();
}
getchar();
}
输出
链表长度:5
元素:7 3 3 3 4
首元素:7 尾元素:4
总结
链表读取任一元素都需要从头遍历,因此读取效率为O(n),低于数组的O(1),但在表内任一位置添加和删除元素时,不需要挪动其他元素位置,因此适合于经常添加和删除元素的场合,并且链表的长度是动态增加的,其物理存储位置不连续,因此空间利用率很高。