之前我们了解了链表中的知识,现在我们进行实现,以C语言为例。
1.我们都需要实现的功能
- 头文件Linklist.c
我们在头文件里声明要实现的函数
#pragma once
typedef int DataType;
// 节点的定义
typedef struct Linknode{
DataType data;
node* next;//因为next指针是指向下一个结点的,因此要定义成结构体的指针类型
}node;
//链表里面的方法
node* Linklist_node(DataType data);
void LinklistInit(node** head);//初始化
void Linklist_pushback(node** head, DataType data);//尾插
void Linklist_popback(node** head);//尾删
void Linklist_pushfront(node** head, DataType data);//头插
void Linklist_popfront(node** head);//头删
void Linklist_insert(node* pos,DataType data);//pos位置插入
void Linklist_Erase(node* pos);//pos位置删除
int Linklist_Size(node* head);//计算链表大小
node* Linklist_find(node* head, DataType data);//查找data
void Linklist_Destory(node** head);//链表的销毁
void Linklist_print(node* head);//输出链表
接下来,我们分别来实现不同功能。
- 尾插
在进行尾插时,我们要注意的时此时链表是否为空。
当链表为空时:
当链表不为空时:
//想要通过对形参修改达到外部实参的目的,在传参时就传递实参的地址
//在函数操作中,如果需要修改head的指向,不许传递二级指针
//初始化函数
void LinklistInit(node** head){
assert(head);
*head = NULL;
}
//添加新结点函数
node* Linklist_node(DataType data){//构造一个新结点
node* _node = (node*)malloc(sizeof(node));//链表上的结点我们一般用malloc从堆上进行申请
if (NULL == _node){//证明在这里我们申请空间失败
assert(0);
return NULL;
}
//申请成功后,给节点的值域进行赋值
_node->data = data;
_node->next = NULL;
return _node;
}
//尾插函数
void Linklist_pushback(node** head, DataType data){
assert(head);
if (NULL == *head){//空链表
*head = Linklist_node(data);
}
else{//非空链表
//1.找到最后一个结点;2.插入
node* cer = *head;
while (cer->next){
cer = cer->next;
}
cer->data = Linklist_node(data);//cer指向一个新结点
cer->next = NULL;
}
}
在这里,我们要理解一个问题,链表不存在和空链表的区别:
空链表是一种有效链表,知识链表中没有有效结点,head=NULL;
链表不存在是指连head都没有,如这种操作:Linklist_pushback(NULL,0)这是种非法操作。
- 尾删
在进行尾删是,我们也要注意链表的有效结点个数。
空链表:
非空链表:
/
/尾删函数
void Linklist_popback(node** head){
assert(head);
if (NULL == *head) return;
else if (NULL == (*head)->next){
//链表中只有一个结点
free(*head);//对链表进行释放,释放完后成为空链表
*head = NULL;
}
else{
node* cer = *head;
node* prev = NULL;
while (cer->next){
prev = cer;//保存前一个结点
cer = cer->next;
}
free(cer);
prev->next = NULL;
}
}
- 头插
头插时我们也可以分为两种情况。
空链表:
非空链表:在非空链表中,有两种插入方式,但是那一只才可以实现呢?
我们在这里就考虑,可知,第二种情况时不可行的,因为如果先将插入的数据给了head,那么后面的节点就无法找到。因此,我们应该使用第一种插入的方法。
//链表的头插
void Linklist_pushfront(node** head, DataType data){
assert(head);
node* newnode = Linklist_node(data);
newnode->next = *head;//这里时新加的结点始终指向原链表,这样就不会丢失原链表
*head = newnode;
}
- 头删
在进行头删时,我们考虑三种情况:空链表时,只有一个结点时,多个节点时;这三种情况。
//头删
void Linklist_popfront(node** head){
assert(head);
if (NULL == *head) return;
else if ((*head)->next == NULL){
free(*head);
*head = NULL;
}
else{
node* delnode = *head;//先标记要删除的结点
*head = delnode->next;
free(delnode);
}
}
- 任意位置插入
在任意位置进行插入,我们就必须的考虑插入的确切位置,以及位置插入的合法性。比如说,我们现在拿到一个pos=3,data=0;将他插入链表中,且当他插入时,是插入3位置之前,还是3位置之后?如果要插在3之前该如何插入?
因为现在接口已经给出,所以没有办法插入3之前,若要插入则可以进行数据的交换。
//在pos位置进行插入
void Linklist_insert(node* pos, DataType data){
node* newnode = NULL;
if (NULL == pos) return;
else{
newnode = Linklist_node(data);
newnode->next = pos->next;
pos->next = newnode;
}
}
- 任意位置删除
//pos位置的删除
void Linklist_Erase(node* pos){
//只能删除pos之后的结点
node* prev = NULL;
if (NULL == pos || NULL == pos->next) return;
else
{
prev = pos->next;
pos ->next= prev->next;
free(prev);
prev = NULL;
//pos->next=pos->next->next;
//free(pos);
}
}
- 计算大小
//计算链表的大小
int Linklist_Size(node* head){
int count = 0;
node* cer = head;
while (cer){
cer = cer->next;
count++;
}
return count;
}
- 查找data
node* Linklist_find(node* head, DataType data){
node* cer = head;
while (cer){
if (cer->data == data){
return cer;
}
cer = cer->next;
}
return NULL;
}
- 链表销毁
//链表的销毁,采用头删的方法进行销毁
void Linklist_Destory(node** head){
assert(head);
node* delnode = NULL;
while (NULL != *head){
delnode = *head;
*head = delnode->next;
free(delnode);
}
}
- 打印链表
//辅助方法,输出链表
void Linklist_print(node* head){
node*cer = head;
while (cer){
printf("%d--->", cer->data);
cer = cer->next;
}
printf("NULL");
printf("\n");
}
- 最终代码及其实现:
#include"Linklist.h"
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
//想要通过对形参修改达到外部实参的目的,在传参时就传递实参的地址
//在函数操作中,如果需要修改head的指向,不许传递二级指针
void LinklistInit(node** head){
assert(head);
*head = NULL;
}
node* Linklist_node(DataType data){//构造一个新结点
node* _node = (node*)malloc(sizeof(node));//链表上的结点我们一般用malloc从堆上进行申请
if (NULL == _node){//证明在这里我们申请空间失败
assert(0);
return NULL;
}
//申请成功后,给节点的值域进行赋值
_node->data = data;
_node->next = NULL;
return _node;
}
void Linklist_pushback(node** head, DataType data){
assert(head);
if (NULL == *head){//空链表
*head = Linklist_node(data);
}
else{//非空链表
//1.找到最后一个结点;2.插入
node* cer = *head;
while (cer->next){
cer = cer->next;
}
cer->next = Linklist_node(data);//cer指向一个新结点
}
}
//尾删函数
void Linklist_popback(node** head){
assert(head);
if (NULL == *head) return;
else if (NULL == (*head)->next){
//链表中只有一个结点
free(*head);//对链表进行释放,释放完后成为空链表
*head = NULL;
}
else{
node* cer = *head;
node* prev = NULL;
while (cer->next){
prev = cer;//保存前一个结点
cer = cer->next;
}
free(cer);
prev->next = NULL;
}
}
//链表的头插
void Linklist_pushfront(node** head, DataType data){
assert(head);
node* newnode = Linklist_node(data);
newnode->next = *head;//这里时新加的结点始终指向原链表,这样就不会丢失原链表
*head = newnode;
}
//头删
void Linklist_popfront(node** head){
assert(head);
if (NULL == *head) return;
else if ((*head)->next == NULL){
free(*head);
*head = NULL;
}
else{
node* delnode = *head;//先标记要删除的结点
*head = delnode->next;
free(delnode);
}
}
//在pos位置进行插入
void Linklist_insert(node* pos, DataType data){
node* newnode = NULL;
if (NULL == pos) return;
else{
newnode = Linklist_node(data);
newnode->next = pos->next;
pos->next = newnode;
}
}
//pos位置的删除
void Linklist_Erase(node* pos){
//只能删除pos之后的结点
node* prev = NULL;
if (NULL == pos || NULL == pos->next) return;
else
{
prev = pos->next;
pos ->next= prev->next;
free(prev);
prev = NULL;
//pos->next=pos->next->next;
//free(pos);
}
}
//计算链表的大小
int Linklist_Size(node* head){
int count = 0;
node* cer = head;
while (cer){
cer = cer->next;
count++;
}
return count;
}
node* Linklist_find(node* head, DataType data){
node* cer = head;
while (cer){
if (cer->data == data){
return cer;
}
cer = cer->next;
}
return NULL;
}
//链表的销毁,采用头删的方法进行销毁
void Linklist_Destory(node** head){
assert(head);
node* delnode = NULL;
while (NULL != *head){
delnode = *head;
*head = delnode->next;
free(delnode);
}
}
//辅助方法,输出链表
void Linklist_print(node* head){
node*cer = head;
while (cer){
printf("%d--->", cer->data);
cer = cer->next;
}
printf("NULL");
printf("\n");
}
void main(){
node* head = NULL;
LinklistInit(&head);
Linklist_pushback(&head,1);
Linklist_pushfront(&head, 0);
Linklist_pushback(&head, 2);
Linklist_pushback(&head, 3);
Linklist_pushback(&head, 4);
Linklist_pushback(&head, 5);
Linklist_print(head);
Linklist_popback(&head);
Linklist_print(head);
Linklist_popfront(&head);
Linklist_print(head);
Linklist_insert(Linklist_find(head,2), 5);
Linklist_print(head);
Linklist_Erase(head);
Linklist_print(head);
int x = Linklist_Size(head);
printf("size=%d\n", x);
Linklist_Destory(&head);
Linklist_print(head);
}
运行截图:
傻作者实现了无头结点单链表的功能,如果有什么错误,欢迎在评论区指正嗷。