关于单链表的一些基本操作,以下为基本思路代码
首先看一张直观图
链表的结构体定义如下(为简便,这里的ElemType采用int):
//单链表
typedef struct LNode {
int data;
struct LNode *next;
} LinkList;
接下来肯定就是建立链表,而目前建立单链表分为两种方式,分别是"头插法"与"尾插法",首先看图示:
清楚明白:
- 头插法就是每次插入的节点在上一个节点之前,头节点之后
- 尾插法就是每次插入的节点在上一节点之后
我们对比来看节点的插入:
//头插法核心代码
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
s->next = L->next;//将s插入到下一节点之前
L->next = s;
//尾插法核心代码
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
r->next = s;//将s插入到r之后
r = s;
那么写成函数,就是如下所示:
/**
* 头插法建立单链表
* @param a 数组元素,对应data
* @param n 链表长度(除去头结点)
*/
void CreateListByHead(LinkList *L, const int a[], int n) {
LinkList *s;
//建立头节点
L->next = NULL;
for (int i = 0; i < n; ++i) {
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
s->next = L->next;//将s插入到下一节点之前
L->next = s;
}
}
/**
* 尾插法建立单链表
* @param a 数组元素,对应data
* @param n 链表长度(除去头结点)
*/
void CreateListByTail(LinkList *L, const int a[], int n) {
LinkList *r = L, *s;
for (int i = 0; i < n; ++i) {//循环建立数据节点
s = (LinkList *) malloc(sizeof(LinkList));
s->data = a[i];
r->next = s;//将s插入到r之后
r = s;
}
r->next = NULL; //尾节点next域置为空
}
那么接下来的集中基本操作方法也就不再赘述,其实都是一些对链表的操作,指针的移动,临界条件的判断,我也都写在注释中了,尤其是第一个Delete方法写的最详细,可以仔细看一下哦,为了直观一点,我也附上代码图片(编译环境Clion)~
按照逻辑序号删除某节点
这里也要注意是否会断链的问题,free()不要释放错了,否则会有错误
/**
* 删除单链表中的第i个元素
* @param L 链表
* @param i 第i个元素
* @return 程序执行完毕状态
*/
int ListDelete(LinkList *L, int i) {
int j = 0;
LinkList *p = L, *q;
/*
*找到第i-1个节点 ,p!= NULL 是防止寻找的节点超过链表,
* 比如一共有四个节点,而i=6即想找到第6个节点,那么当j++变成5,
* p指向第五个不存在的节点后就会返回NULL
*此时循环结束,p == NULL
*/
while (j < i - 1 && p != NULL) {
j++;
p = p->next;
}
if (p == NULL)
return ERROR;
else {
q = p->next;
/*
* 如果p->next为空,则返回ERROR
* 此时是为了防止边界条件的情况
* 若链表一共有四个节点,而我们要找到第五个节点,
* 那么在之前的找到的i-1也就是第四个节点
* 此时q = p->next 返回的就是一个NULL值
*/
if (q == NULL)
return ERROR;
p->next = q->next;
free(q);
return TRUE;
}
}
在逻辑位置之后插入节点
这里注意,插入操作遵循***先右后左***原则,否则会断链,如图:
/**
* 在逻辑位置后插入节点
* @param L 传入链表
* @param i 逻辑位置
* @param e 插入数据
* @return 插入成功返回1,失败返回0
*/
int ListInsert(LinkList *L, int i, int e) {
int j = 0;
LinkList *p = L, *s;
while (j < i && p != NULL) {//查找第i个位置
j++;
p = p->next;
}
if (p == NULL)//如果为空返回错误值
return ERROR;
else {
s = (LinkList *) malloc(sizeof(LinkList));
s->data = e;
s->next = p->next;
p->next = s;
return TRUE;
}
}
按照元素值查找特定节点
这里默认链表中的元素是唯一的,如果想要查找不唯一的数值,就需要大家去拓展了,也不是很难,可以采用将查到的数据逻辑序号放到数组中返回等等等等方法~
/**
* 按照元素值查找特定节点并返回逻辑序号
* @param L 传入单链表
* @param e 传入特定元素
* @return 没有找到返回0,找到返回逻辑序号i
*/
int FindElement(LinkList *L, int e){
int i = 1; //不要忘了初始值!
LinkList *s = L; //指向开始节点
while (s != NULL && s->data != e){
s = s->next;
i++;
}
if (s == NULL )
return ERROR;
else
return i;
}
输出链表
/**
* 输出单链表
*/
void DispList(LinkList *L) {
while (L->next != NULL) {
printf("%d->", L->data);
L = L->next;
}
printf("%d", L->data);
}
最后附上测试用的main函数,为了方便就直接用数组赋值了
#include <stdio.h>
#include "linklist.h"
int main() {
int a[10];
for (int j = 0; j < 10; ++j) a[j] = j + 1;
LinkList *L = (LinkList *) malloc(sizeof(LinkList));
CreateListByTail(L, a, 10);
printf("建立完成后的单链表为: ");
DispList(L);
ListInsert(L, 5, 555);
printf("\n在第五个节点后插入555之后的单链表为: ");
DispList(L);
ListDelete(L, 3);
printf("\n删除第三个节点后的单链表为: ");
DispList(L);
int i = FindElement(L, 5);
if (i != 0)
printf("\n拥有此元素!在第%d个位置上!",i);
else
printf("\n没有此元素!!!!");
return 0;
}