【数据结构】链表头插法和尾插法
1 什么是链表?
链表是一种常用的数据结构,由若干个节点组成,每个节点都有数据部分和指针部分:数据部分用来存储节点的值,指针则指向下一个节点。
2 节点的实现
struct ListNode{
// 数据
int val;
// 下一个链表节点的地址
ListNode *next;
};
3 链表的实现
- 链表的首节点称为头节点,最后一个节点称为尾节点
- 尾节点指向的是NULL
- 链表的实现,需要一个头节点的地址和当前节点的地址,因此我们需要使用两个指针存储头节点的地址(Head)和当前节点的地址(cur)
#include <iostream>
using namespace std;
struct ListNode{
// 数据
int val;
// 下一个链表节点的地址
ListNode *next;
};
int main() {
ListNode *L =new ListNode; //存储头节点的地址
L->val = 0; //头节点所存储的值
L->next = NULL; // 指向第二个节点的地址
ListNode *l1 = new ListNode;
l1->val = 10;
l1->next = NULL;
ListNode *l2 = new ListNode;
l2->val = 30;
l2->next = NULL;
// 此时三个节点并没有链接,下面我们对三个节点进行连接
// 将三个节点组成链表
L->next = l1; // 头节点的指针指向L1的地址
l1->next = l2;
// 遍历链表
ListNode *cur = L;
while (cur != NULL){
cout << cur->val << " " << endl;
cur = cur->next;
}
cout << endl;
return 0;
}
此时,三个节点的示意图如下所示:
4 将遍历链表的功能函数化
可以将
ListNode *cur = L;
while (cur != NULL){
cout << cur->val << " " << endl;
cur = cur->next;
}
cout << endl;
这一段代码封装成函数,已重复使用。
如下所示:
void printList(ListNode *L){
// 遍历链表
ListNode *cur = L;
while (cur != NULL){
cout << cur->val << " " << endl;
cur = cur->next;
}
cout << endl;
}
5 通过尾插法创建链表
- 判断链表是否尾空,如果是空,则创建新节点,把新节点的指针设为NULL,并让head指向新节点的地址
- 如果不为空,则找到尾节点,创建新节点,让尾节点的指针指向新创的节点的地址,并把新创建节点的指针设置尾NULL
如下所示:
在该函数中,如果不使用引用,而是简单地使用ListNode *L,函数内部对L的任何修改都仅仅是在函数的局部副本上进行的,对原来的指针没有影响。也就是说,外部传入的指针不会因为函数内部的操作而改变。
void InsertTail(ListNode *&L,int v){
// 判断是否为空链表
if(L == NULL){
// 为空链表
ListNode *node = new ListNode;
L = node;
L->val = v;
L->next = NULL;
return;
}
// 不为空链表
// 先找到尾节点,尾节点的next指向尾NULL
// 定义当前的指针cur,如果cur->next == NULL,说明已经找到最后一个节点了
ListNode *cur = L;
while(cur->next == NULL){
cur = cur->next;
}
ListNode *newNode = new ListNode;
newNode->val = V;
newNode->next = NULL;
cur->next = newNode;
}
6 通过头插法插入节点
步骤:
- 创建一个新的节点。
- 将新节点的 next 指针指向当前的头节点。
- 将头节点指针 L 更新为新节点。
void InsertHead(ListNode *&L,int v){
ListNode *newNode = new ListNode;
newNode->val = v;
newNode->next = L;
L = newNode;
}
下面通过实际的代码 来实现尾插法和头插法的实现:
#include <iostream>
using namespace std;
struct ListNode{
// 数据
int val;
// 下一个链表节点的地址
ListNode *next;
};
void printList(ListNode *L){
// 遍历链表
ListNode *cur = L;
while (cur != NULL){
cout << cur->val << " " << endl;
cur = cur->next;
}
cout << endl;
}
// 尾插法创建链表
void InsertTail(ListNode *&L,int v){
// 如果这个是空列表,说明L == null;
if(L == NULL){
ListNode *node = new ListNode;
L = node;
L->val = v;
L->next = NULL;
return;
}
// 先找到尾节点
// 尾节点的next是指向NULL
// 定义一个指针cur->next == NULL ,说明cur已经到最后一个节点了
ListNode *cur = L;
// 找到最后一个节点
while (cur->next != NULL){
cur = cur->next;
}
// 此时cur应该已经指向最后一个节点
// 先定义一个节点
ListNode *newNode = new ListNode;
newNode->val = v;
newNode->next = NULL; // 新节点的
cur->next = newNode;
}
// 定义头插法插入节点
void InsertHead(ListNode *&L,int v){
ListNode *newNode = new ListNode;
newNode->val = v;
newNode->next = L;
L = newNode;
}
int main() {
// 通过尾插法插入元素
ListNode *L = NULL;
InsertTail(L,0);
InsertTail(L,10);
InsertTail(L,20);
printList(L);// 打印出 0 10 20
// 使用头插法插入元素
InsertHead(L,90);
InsertHead(L,80);
printList(L);// 打印出80 90 0 10 20
return 0;
}