C++实现单链表
原创
©著作权归作者所有:来自51CTO博客作者runixcn的原创作品,请联系作者获取转载授权,否则将追究法律责任
C++实现单链表
链表是一种以链式存储的线性表,由于内存空间可以不连续,使用十分灵活,与数组相比各有优缺点。
单链表需要有个标识指明起始位置以便于操作整个链表,可以是头指针也可以是头结点,只是实现上的不同,这里使用头结点的方式。
头结点内存结构和数据节点相同,只是头结点不保存数据。有了头结点之后,要操作的第一个数据节点就是头结点的下一个节点。
struct Node
{
T data;
Node* next;
};
//通过定义与头结点相同内存布局的头节点变量,避免操作头结点数据,使用 &m_header 即获得指向头结点的指针
struct
{
char reserved[ sizeof(T) ];
Node* next;
} m_header;
主要实现接口:
position(int i)
| 节点指针定位
|
insert(int i, const T& e)
| 指定位置插入
|
insert(const T& e)
| 从尾部插入
|
remove(int i)
| 删除第i个节点
|
get(int i, T& e)
| 获取第i个节点
|
get (int i)
| 获取第i个节点const版本
|
traverse()
| 遍历整个链表
|
如何在链表任一位置插入节点
假设除了头结点之外,有两个数据节点,那么可插入位置为三个
假设要在图中标识为0的位置插入节点,则插入位置是头结点的next
节点(重要)
同理在1的位置插入节点,则插入位置是头结点的next->next
节点
同理在2的位置插入节点,则插入位置是头结点的next->next->next
节点……
于是得出规律:
- 在第0个位置插入,直接跟在头结点后
- 在第1个位置插入,就从头结点跳过1个节点后插入
- 在第2个位置插入,就从头结点跳过2个节点后插入……以此类推
节点指针定位
根据上一节的分析,在第i个位置插入节点,需要跳过i个节点,由此定位待插入位置的前一个节点
Node* position(int i)
{
Node* ret = reinterpret_cast<Node*>(&m_header);
for ( int p = 0; p < i; p++ )
{
ret = ret->next;
}
return ret;
}
插入函数
通过position
函数得到目标位置的前一个位置后,就可以执行插入操作了,位置范围[0, m_length]
bool insert(int i, const T& e)
{
bool ret = ( 0 <= i && i <= m_length );
if( ret )
{
Node* node = new (std::nothrow) Node();
assert(nullptr != node);
Node* current = position(i);
node->data = e;
node->next = current->next;
current->next = node;
m_length++;
}
return ret;
}
//尾插重载版本
bool insert(const T& e)
{
return insert(m_length, e);
}
删除函数
同理删除节点时,也可以通过指针定位的方式,有点不同的是,删除时位置范围只有[0, m_length - 1]
bool remove(int i)
{
bool ret = ( 0 <= i && i <m_length );
if (ret)
{
Node* current = position(i);
Node *toDel = current->next;
current->next = toDel->next;
m_length--;
delete toDel;
}
return ret;
}
访问第i个节点
T get (int i) const
{
T ret;
if ( get(i, ret) )
{
return ret;
}
else
{
throw std::out_of_range("get list element out of range ...");
}
}
bool get(int i, T& e)
{
bool ret = ( (0 <= i) && (i < m_length) );
if (ret)
{
e = position(i)->next->data;
}
return ret;
}
遍历整个链表
void traverse()
{
Node* current = position(1);
while( current )
{
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
}
#include <iostream>
#include <cassert>
template<typename T>
class List
{
struct Node
{
T data;
Node* next;
};
struct
{
char reserved[ sizeof (T) ];
Node* next;
} m_header;
int m_length;
Node* position(int i)
{
Node* ret = reinterpret_cast<Node*>(&m_header);
for ( int p = 0; p < i; p++ )
{
ret = ret->next;
}
return ret;
}
public:
List()
{
m_header.next = nullptr;
m_length = 0;
}
bool insert(const T& e)
{
return insert(m_length, e);
}
bool insert(int i, const T& e)
{
bool ret = ( 0 <= i && i <= m_length );
if( ret )
{
Node* node = new (std::nothrow) Node();
assert(nullptr != node);
Node* current = position(i);
node->data = e;
node->next = current->next;
current->next = node;
m_length++;
}
return ret;
}
bool remove(int i)
{
bool ret = ( 0 <= i && i < m_length );
if (ret)
{
Node* current = position(i);
Node *toDel = current->next;
current->next = toDel->next;
m_length--;
delete toDel;
}
return ret;
}
T get (int i) const
{
T ret;
if ( get(i, ret) )
{
return ret;
}
else
{
throw std::out_of_range("get list element out of range ...");
}
}
bool get(int i, T& e)
{
bool ret = ( (0 <= i) && (i < m_length) );
if (ret)
{
e = position(i)->next->data;
}
return ret;
}
void traverse()
{
Node* current = position(1);
while( current )
{
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
}
int length() const
{
return m_length;
}
void clear()
{
while (m_header.next)
{
Node* toDel = m_header.next;
m_header.next = toDel->next;
m_length--;
delete toDel;
}
}
};
int main()
{
List<int> list;
for ( int i = 0; i < 5; ++i )
{
list.insert(i);
}
list.traverse();//0 1 2 3 4
list.insert(3, 100);
list.traverse();//0 1 2 100 3 4
list.remove(0);
list.remove(2);
list.traverse();//1 2 3 4
list.clear();
list.traverse();//空
return 0;
}
//运行结果
0 1 2 3 4
0 1 2 100 3 4
1 2 3 4
参考