一、实现基础
- 代码基于C++的模板类实现,这样就能够自由选择链表的数据类型。
- 使用指针实现有头结点的单链表。
- 在链表类Node中只定义了数据结构和功能函数,head头结点在主函数中定义并作为函数参数,这一点较常用的链表实现有所区别。
二、实现功能
- 创建链表
- 返回链表长度
- 在链表尾部添加元素
- 插入元素
- 删除结点并返回该结点的值
- 清空链表
- 按链表值从尾到头的顺序打印链表(附加功能)
三、各功能详解
1、链表类 class Node
template <class T>
class Node
{
public:
T val;
Node *next;
Node(T x):val(x), next(NULL) {
} // 构造函数
void SetList(Node** head ); // 创建链表
int length(Node* head); // 返回链表长度
void append(T element, Node* head); // 在链表尾部添加元素
void insert(int pos, T element, Node* head); // 插入元素
T remove(int pos, Node* head, T errorFlag); // 删除结点并返回该结点的值
void clear(Node** head); // 清空链表
vector<int> printListFromTailToHead(Node* head); // 按链表值从尾到头的顺序打印
};
- 链表的结点值
val
为模板T
类型的,这样自己在创建链表时,就可以指定结点值为int/char
等各种类型,比较灵活。
2、创建链表
template <class T>
void Node<T>::SetList(Node** head){
Node <int> *cur, *temp;
*head = cur = temp = NULL;
for(int i = 0; i < Length; i++)
{
int t;
cin >> t;
temp = new Node<int>(0);
temp->val = t;
temp->next = NULL;
if(*head == NULL)
*head = temp;
else
cur->next = temp;
cur = temp;
}
}
-
为什么传参是
Node**
类型?head在主函数中的定义是
Node *head
,如果传参为*head
,会出现在SetList()
中head
指向的地址有变,但是在主函数中head
指向的地址没有变的情况。由于在主函数中初始化时head = NULL
,所以,如果传参为*head
,那么在SetList()
中确实head
指向了下一个结点的地址,但是主函数中head
指向的地址仍然为空,也就是没有创建链表。这是因为,函数参数传递的只能是数值,所以当指针作为函数参数传递时,传递的是指针的值,而不是地址,所以在函数中不能改变实际的指针指向的地址,解决该问题,将指针指向的地址作为参数传给函数即可,也就是传参为
Node**
。
3、返回链表长度
template <class T>
int Node<T>::length(Node* head){
Node<int> *cur = head;
int len = 0;
if(head == NULL)
return -1;
while(cur){
len++;
cur = cur->next;
}
return len;
}
4、在链表尾部添加元素
template <class T>
void Node<T>::append(T element, Node* head){
Node *temp, *cur = head;
temp = new Node<T>(0);
temp->val = element;
temp->next = NULL;
while(cur->next)
cur = cur->next;
cur->next = temp;
}
- 当找到链表的最后一个元素
tail
时(tail->next == NULL
),将原本tail
指向为空替换为指向新增加的结点temp
,再将temp->next
赋值为NULL
,即temp
成为了新的tail
。
5、插入元素
template <class T>
void Node<T>::insert(int pos, T element, N