无头链表有两种,一种是二级指针形式,还有一种就是结构体再封装形式。结构体再封装通过抽象出链表的共有属性 即链表表头,表尾,和链表长度 来建立链表。
所以我们需要建立两个结构体。
其中一个结构体是节点结构体
typedef struct _Node{
int data;
struct _Node *next;
}Node;
另一个则是通过链表共有属性抽象出来的结构体
typedef struct _List{
Node* frontNode; //表头结点指针 代表表头节点
Node* tailNode; //表尾节点指针 代表表尾节点
int size;
}List;
通过初始化链表共有属性抽象出来的结构体 ,我们就可以得到一个暂无节点的链表表头指针和表尾指针,然后通过创建节点不断的往链表中插入数据就可以得到链表。不过需要注意的是:在增删改查的过程中,表头节点指针必须永远指向表头节点,表尾节点的指针也必须永远指向表尾。
这种方法的和采用二级指针的无头链表相比来说更容易理解(不过我觉得二级指针的看着亲切些哈哈)
具
#include <stdio.h>
#include <stdlib.h>
typedef struct _Node{
int data;
struct _Node *next;
}Node;
typedef struct _List{
Node* frontNode; //表头结点指针 代表表头节点
Node* tailNode; //表尾节点指针 代表表尾节点
int size;
}List;
//创建一个链表
List* createList(){
List* list = (List*)malloc(sizeof(List));
list->frontNode = NULL;
list->tailNode = NULL;
list->size = 0;
return list;
}
Node* createNewNode(int data){ //新建节点
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
void insertNodeByHead(List* list, int data){ // 头插法
Node* newNode = createNewNode(data);
if (list->size == 0){
list->frontNode = newNode;
list->tailNode = newNode;
}
else{
newNode->next = list->frontNode; //新节点指向头节点
list->frontNode = newNode; //把新节点作为头节点
}
list->size++;
}
void insertNodeByTail(List* list, int data){ //尾插
Node* newNode = createNewNode(data);
if (list->size == 0){
list->frontNode = newNode;
list->tailNode = newNode;
}
else{
list->tailNode->next = newNode;
list->tailNode = newNode;
}
list->size++;
}
void printList(List *list){ //打印
Node *pMoveNode = list->frontNode;
while (pMoveNode != NULL){
printf("%d ", pMoveNode->data);
pMoveNode = pMoveNode->next;
}
}
void deleteNodeByHead(List* list){ //头删
if (list->frontNode == NULL){
printf("链表为空,无法删除!\n");
}
else{
Node* deleteNode = list->frontNode;
list->frontNode = list->frontNode->next;
free(deleteNode);
deleteNode = NULL;
list->size--;
}
}
void deleteNodeByTail(List* list){ //尾删
int i = 0;
if (list->tailNode == NULL){
printf("链表为空,无法删除!\n");
}
else{
Node* pMoveNode = list->frontNode;
while (pMoveNode->next->next != NULL){
pMoveNode = pMoveNode->next;
}
free(list->tailNode);
list->tailNode = pMoveNode;
list->tailNode->next = NULL;
list->size--;
}
}
int main(){
List* list = createList();
insertNodeByHead(list, 10);
insertNodeByHead(list, 20);
insertNodeByHead(list, 30);
insertNodeByTail(list, -10);
deleteNodeByHead(list);
deleteNodeByTail(list);
printList(list);
return 0;
}
记录一下自己实现代码时犯的一些错误:实现尾删时错误的将 pMoveNode->next != NULL 当作是链表倒数第二个节点(实际是尾节点),导致打印时打印的是已经被释放的内存地址。