问题:如何遍历单链表中的每一个元素?
当前单链表的遍历方法
LinkList list;
for (int i=0; i<5; ++i)
{
list.insert(0, i);
}
for (int i=0; i
遗憾的事实:
- 不能以线性的时间复杂度完成单链表的遍历
新的需求
- 为单链表提供新的方法,在线性时间内完成遍历
设计思路(游标)
- 在单链表的内部定义一个游标(Node *m_current)
- 遍历开始前将游标指向位置为0的数据元素
- 获取游标指向的数据元素
- 通过节点的 next 指针移动游标
提供一组遍历相关的函数,以线性的时间复杂度遍历链表。
函数 | 功能说明 |
move() | 将游标定位到目标位置 |
next() | 移动游标 |
current() | 获取游标所指向的数据元素 |
end() | 游标是否到达尾部(是否为空) |
遍历函数原型设计
- bool move(int i, int step = 1);
- bool end();
- T current();
- bool next();
编程实验:单链表的遍历
文件:LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include "List.h"
#include "Exception.h"
namespace DTLib
{
template
class LinkList : public List
{
public:
LinkList()
{
m_header.next = nullptr;
m_length = 0;
m_step = 0;
m_current = nullptr;
}
bool insert(const T &e) override // O(n)
{
return insert(m_length, e);
}
bool insert(int i, const T &e) override // O(n)
{
bool ret = ((0 <= i) && (i <= m_length));
if (ret)
{
Node *node = new Node();
if (node != nullptr)
{
Node *current = position(i);
node->value = e;
node->next = current->next;
current->next = node;
++m_length;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ...");
}
}
return ret;
}
bool remove(int i) override // O(n)
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
Node *current = position(i);
Node *toDel = current->next;
current->next = toDel->next;
delete toDel;
--m_length;
}
return ret;
}
bool set(int i, const T &e) override // O(n)
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
position(i)->next->value = e;
}
return ret;
}
T get(int i) const // O(n)
{
T ret;
if (!get(i, ret))
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
}
return ret;
}
bool get(int i, T &e) const override // O(n)
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
e = position(i)->next->value;
}
return ret;
}
int find(const T &e) override // O(n)
{
int ret = -1;
int i = 0;
Node *node = m_header.next;
while (node)
{
if (node->value == e)
{
ret = i;
break;
}
else
{
node = node->next;
++i;
}
}
return ret;
}
int length() const // O(1)
{
return m_length;
}
void clear() // O(n)
{
while (m_header.next)
{
Node *toDel = m_header.next;
m_header.next = toDel->next;
delete toDel;
--m_length;
}
}
bool move(int i, int step = 1) // O(n)
{
bool ret = ((0 <= i) && (i < m_length) && (step > 0));
if (ret)
{
m_current = position(i)->next;
m_step = step;
}
return ret;
}
bool end() // O(1)
{
return (m_current == nullptr);
}
T current() // O(1)
{
if (!end())
{
return m_current->value;
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ...");
}
}
bool next() // O(n)
{
int i = 0;
while ((i < m_step) && !end())
{
m_current = m_current->next;
++i;
}
return (i == m_step);
}
~LinkList() // O(n)
{
clear();
}
protected:
struct Node : public Object
{
T value;
Node *next;
};
mutable struct : public Object
{
char reserved[sizeof (T)];
Node *next;
}m_header;
int m_length;
int m_step;
Node *m_current;
Node *position(int i) const // O(n)
{
Node *ret = reinterpret_cast(&m_header);
for (int p=0; pnext;
}
return ret;
}
};
}
#endif // LINKLIST_H
文件:main.cpp
#include
#include "LinkList.h"
using namespace std;
using namespace DTLib;
int main()
{
cout << "main begin" << endl;
LinkList list;
for (int i=0; i<5; ++i)
{
list.insert(0, i);
}
for (list.move(0); !list.end(); list.next())
{
cout << list.current() << endl;
}
cout << "main end" << endl;
return 0;
}
输出:
main begin
4
3
2
1
0
main end
单链表内部的一次封装
virtual Node *create()
{
return new Node();
}
virtual void destory(Node *pn)
{
delete pn;
}
编程实验:内部的封装
LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include "List.h"
#include "Exception.h"
namespace DTLib
{
template
class LinkList : public List
{
public:
LinkList()
{
m_header.next = nullptr;
m_length = 0;
m_step = 0;
m_current = nullptr;
}
bool insert(const T &e) override // O(n)
{
return insert(m_length, e);
}
bool insert(int i, const T &e) override // O(n)
{
bool ret = ((0 <= i) && (i <= m_length));
if (ret)
{
Node *node = create();
if (node != nullptr)
{
Node *current = position(i);
node->value = e;
node->next = current->next;
current->next = node;
++m_length;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to insert new element ...");
}
}
return ret;
}
bool remove(int i) override // O(n)
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
Node *current = position(i);
Node *toDel = current->next;
current->next = toDel->next;
destroy(toDel);
--m_length;
}
return ret;
}
bool set(int i, const T &e) override // O(n)
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
position(i)->next->value = e;
}
return ret;
}
T get(int i) const // O(n)
{
T ret;
if (!get(i, ret))
{
THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
}
return ret;
}
bool get(int i, T &e) const override // O(n)
{
bool ret = ((0 <= i) && (i < m_length));
if (ret)
{
e = position(i)->next->value;
}
return ret;
}
int find(const T &e) override // O(n)
{
int ret = -1;
int i = 0;
Node *node = m_header.next;
while (node)
{
if (node->value == e)
{
ret = i;
break;
}
else
{
node = node->next;
++i;
}
}
return ret;
}
int length() const // O(1)
{
return m_length;
}
void clear() // O(n)
{
while (m_header.next)
{
Node *toDel = m_header.next;
m_header.next = toDel->next;
destroy(toDel);
--m_length;
}
}
bool move(int i, int step = 1) // O(n)
{
bool ret = ((0 <= i) && (i < m_length) && (step > 0));
if (ret)
{
m_current = position(i)->next;
m_step = step;
}
return ret;
}
bool end() // O(1)
{
return (m_current == nullptr);
}
T current() // O(1)
{
if (!end())
{
return m_current->value;
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ...");
}
}
bool next() // O(n)
{
int i = 0;
while ((i < m_step) && !end())
{
m_current = m_current->next;
++i;
}
return (i == m_step);
}
~LinkList() // O(n)
{
clear();
}
protected:
struct Node : public Object
{
T value;
Node *next;
};
mutable struct : public Object
{
char reserved[sizeof (T)];
Node *next;
}m_header;
int m_length;
int m_step;
Node *m_current;
Node *position(int i) const // O(n)
{
Node *ret = reinterpret_cast(&m_header);
for (int p=0; pnext;
}
return ret;
}
virtual Node *create()
{
return new Node();
}
virtual void destroy(Node *pn)
{
delete pn;
}
};
}
#endif // LINKLIST_H
问题:封装 create 和 destroy 函数的意义是什么?
To be continued...
小结
- 单链表的遍历需要在线性时间内完成
- 在单链表内部定义游标变量,通过游标变量提高效率
- 遍历相关的成员函数是相互依赖,相互配合的关系
- 封装节点的申请和删除操作更有利于增强扩展性
以上内容整理于狄泰软件学院系列课程,请大家保护原创!