目录
前言
在刚开始进行算法刷题时,一定会遇到一个重要知识点——链表,主要是基于指针和结构体的应用,本博客整理了近期博主记录的链表基本操作,先打牢基础再开始刷题才会更流畅哦!
一、实战
基于c语言实现对链表的基本操作
1.创建链表
链表创建时要注意创建储存结点的结点域,用于指向结点便于后续增删改查操作
// 定义链表节点
typedef struct LNode {
int data; // 数据域
struct LNode* next; // 指针域
} LNode, * Linklist;
初学者误区:
Linklist L:定义的是指向链表的z指针,链表中定义了数据和指针
lNode l:定义的是指向结点的指针,是链表的一部分
2.创建链表
尾插法
创建链表与后续插入元素的代码相关性很强
// 创建链表(尾插法)
Linklist creat(Linklist L) {
int x;//用于输入数值
LNode* s, * r; // s为新节点,r为尾节点
L = (LNode*)malloc(sizeof(LNode)); // 为头节点申请空间
L->next = NULL; // 初始为空链表,避免为野指针
r = L; // 尾指针指向头节点
printf("请输入数据,以-1结束:\n");
scanf("%d", &x);
while (x != -1) {
s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
s->data = x; // 赋值
r->next = s; // 尾节点指向新节点
r = s; // 尾指针移动到新节点
scanf("%d", &x); // 读取下一个数据
}
r->next = NULL; // 尾节点指向NULL
return L;
}
头插法
// 创建链表(头插法)
Linklist creat(Linklist L) {
int x;
LNode* s; // s为新节点,r为尾节点
L = (LNode*)malloc(sizeof(LNode)); // 头节点
L->next = NULL; // 初始为空链表
printf("请输入数据,以-1结束:\n");
scanf("%d", &x);
while (x != -1) {
s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
s->data = x; // 赋值
s->next = L->next; //先找到头结点的下一个元素
L->next = s; //头结点前移
scanf("%d", &x); // 读取下一个数据
}
return L;
}
3.遍历链表
链表的第一个结点为L->next,并不是L所指的默认第一个结点,第一个结点为空
// 遍历链表
void trave(Linklist L) {
LNode* p = L->next; // 头节点指向的第一个节点
while (p) {
printf("%d\t", p->data); // 打印节点数据
p = p->next; // 指针后移
}
printf("\n");
}
4.查找元素
按照位置查找
返回所指位置结点的指针,再由p->data获取该位置数据值
// 获取第i个节点的指针
LNode* getelem(Linklist L, int i) {
if (i < 0) return NULL; // 非法位置
LNode* p = L;
int j = 0;
while (p && j < i) {
p = p->next;
j++;
}
return p;
}
按值查找
// 按值查找,返回位置
int locate(Linklist L, int e) {
LNode* p = L->next;//指向第一个结点
int pos = 1;//用于计算位置
while (p && p->data != e) {
p = p->next;
pos++;
}
if (p == NULL) return 0; // 没找到目标元素
return pos;
}
5.插入元素
// 在第i个位置插入元素e
int insert(Linklist L) {
int i, e;
printf("请输入要插入的位置和数据:\n");
scanf("%d %d", &i, &e);
if (i < 1) return 0; // 非法位置
LNode* p = getelem(L, i - 1); // 获取第i个节点的前一个节点
if (!p) return 0; // 非法位置
LNode* s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
if (!s) return 0; // 内存分配失败
s->data = e; // 赋值
s->next = p->next; // 新节点指向原来i位置的节点
p->next = s; // 前一个节点指向新节点
return 1;
}
6.删除结点
删除结点是也是先找到目标结点下一个结点
// 删除第i个位置的元素
int delete(Linklist L) {
int i;
printf("请输入要删除的位置:\n");
scanf("%d", &i);
if (i < 1) return 0; // 非法位置
LNode* p = getelem(L, i - 1); // 获取第i个节点的前一个节点
if (!p || !(p->next)) return 0; // 非法位置,或者没有下一个节点
LNode* q = p->next; // 待删除的节点
p->next = q->next; // 前一个节点指向下下个节点
free(q); // 释放内存
return 1;
}
7.完整menu
将上述操作整合到一个链表小程序里面,就可以进行实时操作选择啦~
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点
typedef struct LNode {
int data; // 数据域
struct LNode* next; // 指针域
} LNode, * Linklist;
// 创建链表(尾插法)
Linklist creat(Linklist L) {
int x;
LNode* s, * r; // s为新节点,r为尾节点
L = (LNode*)malloc(sizeof(LNode)); // 头节点
L->next = NULL; // 初始为空链表
r = L; // 尾指针指向头节点
printf("请输入数据,以-1结束:\n");
scanf("%d", &x);
while (x != -1) {
s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
s->data = x; // 赋值
r->next = s; // 尾节点指向新节点
r = s; // 尾指针移动到新节点
scanf("%d", &x); // 读取下一个数据
}
r->next = NULL; // 尾节点指向NULL
return L;
}
// 遍历链表
void trave(Linklist L) {
LNode* p = L->next; // 头节点指向的第一个节点
while (p) {
printf("%d\t", p->data); // 打印节点数据
p = p->next; // 指针后移
}
printf("\n");
}
// 获取第i个节点的指针
LNode* getelem(Linklist L, int i) {
if (i < 0) return NULL; // 非法位置
LNode* p = L;
int j = 0;
while (p && j < i) {
p = p->next;
j++;
}
return p;
}
// 按值查找,返回位置
int locate(Linklist L, int e) {
LNode* p = L->next;
int pos = 1;
while (p && p->data != e) {
p = p->next;
pos++;
}
if (p == NULL) return 0; // 没找到目标元素
return pos;
}
// 在第i个位置插入元素e
int insert(Linklist L) {
int i, e;
printf("请输入要插入的位置和数据:\n");
scanf("%d %d", &i, &e);
if (i < 1) return 0; // 非法位置
LNode* p = getelem(L, i - 1); // 获取第i个节点的前一个节点
if (!p) return 0; // 非法位置
LNode* s = (LNode*)malloc(sizeof(LNode)); // 创建新节点
if (!s) return 0; // 内存分配失败
s->data = e; // 赋值
s->next = p->next; // 新节点指向原来i位置的节点
p->next = s; // 前一个节点指向新节点
return 1;
}
// 删除第i个位置的元素
int delete(Linklist L) {
int i;
printf("请输入要删除的位置:\n");
scanf("%d", &i);
if (i < 1) return 0; // 非法位置
LNode* p = getelem(L, i - 1); // 获取第i个节点的前一个节点
if (!p || !(p->next)) return 0; // 非法位置,或者没有下一个节点
LNode* q = p->next; // 待删除的节点
p->next = q->next; // 前一个节点指向下下个节点
free(q); // 释放内存
return 1;
}
int main() {
Linklist L = NULL;
int num;
while (1) {
printf("\n*******0.退出 1.创建链表 2.遍历链表 3.插入元素 4.删除元素 5.获取元素 6.查找元素*******\n");
printf("请选择操作:");
scanf("%d", &num);
switch (num) {
case 0:
printf("退出程序。\n");
return 0;
case 1:
L = creat(L);
printf("链表创建成功。\n");
break;
case 2:
if (L) {
printf("链表元素:");
trave(L);
}
else {
printf("链表为空,请先创建链表。\n");
}
break;
case 3:
if (L) {
if (insert(L))
printf("插入成功。\n");
else
printf("插入失败。\n");
}
else {
printf("链表为空,请先创建链表。\n");
}
break;
case 4:
if (L) {
if (delete(L))
printf("删除成功。\n");
else
printf("删除失败。\n");
}
else {
printf("链表为空,请先创建链表。\n");
}
break;
case 5: {
int i;
printf("请输入要获取的元素位置:");
scanf("%d", &i);
LNode* node = getelem(L, i);
if (node)
printf("位置 %d 上的元素为 %d。\n", i, node->data);
else
printf("位置 %d 无效。\n", i);
break;
}
case 6: {
int e;
printf("请输入要查找的元素:");
scanf("%d", &e);
int pos = locate(L, e);
if (pos)
printf("元素 %d 在链表中的位置为 %d。\n", e, pos);
else
printf("元素 %d 未找到。\n", e);
break;
}
default:
printf("无效的选项,请重新选择。\n");
}
}
return 0;
}
总结
链表的一系列操作在逻辑上看是非常简单的,但是实际写代码操作时会遇到许多问题,快来敲爆键盘and刷爆leetcode吧!just go for it,希望可以帮助到学习链表或者指针和结构体的你!