<>
单链表
定义 :单链表是由节点组成的,每个节点包含两部分,一部分是数据域,用于存储数据元素;另一部分是指针域,用于存储指向下一个节点的指针。通过指针将各个节点依次连接起来,形成一个链式结构,每个结点结点都有一个指针指向下一个结点,最后一个节点的指针通常指向空,表示链表的结束。
1.单链表结构体
c
//单链表的结构体
struct LNode{
ElemType data;//定义单链表结点类型
struct LNode *next;//定义指向下一个节点
};
typedef struct LNode LNode;
typedef struct LNode LinkList;
||
typedef struct LNode{
ElemType data;//定义单链表结点类型
struct LNode *next;//定义指向下一个节点
}LNode,*LinkList;
typedef关键字--数据类型重命名
typedef<数据类型><别名>
为了增加代码的维护性对部分代码进行包装
c
/**
* tool function
* 根据索引遍历链表,找到第i个节点
* @param temp_p 链表头指针
* @param i 要查找的位置
* @param temp_j 输出参数,记录实际遍历到的位置
* @return 找到的节点指针,如果位置不合法或链表为空则返回NULL
*/
LNode* traverseByIndex(LNode* temp_p, int i, int &temp_j){
LNode* p = temp_p;
int j = 0;
while (p!= NULL && j < i) {
p = p->next;
j++;
}
temp_j = j;
return p;
}
2.单链表的插入

c
/**
* 在单链表的第i个位置插入元素e
* @param L 链表头指针的引用
* @param i 插入位置(从1开始计数)
* @param e 要插入的元素
* @return 插入成功返回true,否则返回false
*/
bool LinkListInsert(LinkList &L, int i, ElemType e) {
int j = 0;
LNode* p = traverseByIndex(L, i - 1, j); // 遍历到第i-1个节点,为插入做准备
if (p == NULL) { // 非法区域,位置i超出链表范围或链表为空
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode)); // 申请要插入的结点
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
时间复杂度分析 :插入操作主要的时间开销在于找到第i - 1个节点,调用了traverseByIndex函数,最坏情况下需要遍历i - 1个节点,因此时间复杂度为O (i ),在最坏情况下(即i等于链表长度n)时间复杂度为O (n )。而插入节点本身的操作(修改指针指向)时间复杂度为O(1)。
注意 :p->next = s 与 s->next = p->next位置不可以调换 不然会出现后面的结点丢失的情况
3.单链表的删除

c
/**
* 删除单链表的第i个位置的元素,并将删除的元素值存储到e中
* @param L 链表头指针的引用
* @param i 删除位置(从1开始计数)
* @param e 用于存储删除的元素值
* @return 删除成功返回true,否则返回false
*/
bool ListDelete(LinkList &L, int i, ElemType &e) {
int j = 0;
LNode* p = traverseByIndex(L, i - 1, j);
if (p == NULL || j > i - 1) { // 指向空区域或i不合法,位置i超出链表范围或链表为空
return false;
}
LNode* q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
时间复杂度分析 :删除操作主要的时间开销在于找到第i - 1个节点,调用了traverseByIndex函数,最坏情况下需要遍历i - 1个节点,因此时间复杂度为(O(i)),在最坏情况下(即i等于链表长度n)时间复杂度为(O(n))。而删除节点本身的操作(修改指针指向和释放内存)时间复杂度为(O(1))。
4.获取链表元素(位置||元素)
c
/**
* 根据位置获取单链表中的元素
* @param L 链表头指针的引用
* @param i 要获取的元素位置(从1开始计数)
* @return 找到的节点指针,如果位置不合法或链表为空则返回NULL
*/
LNode* GetElemByIndex(LinkList &L, int i) {
int j = 0;
LNode* p = traverseByIndex(L, i, j);
return p; // p为NULL或者想查找的结点
}
/**
* 根据值获取单链表中的元素
* @param L 链表头指针的引用
* @param e 要查找的值
* @return 找到的节点指针,如果未找到则返回NULL
*/
LNode* GetElemByDate(LinkList &L, ElemType e) {
LNode* p = L;
while (p!= NULL && p->data != e) {
p = p->next;
}
return p; // p为NULL或者想查找的结点
}
GetElemByIndex函数:主要依赖于traverseByIndex函数,最坏情况下需要遍历i个节点,因此时间复杂度为(O(i)),在最坏情况下(即i等于链表长度n)时间复杂度为(O(n))。GetElemByDate函数:需要从链表头开始依次遍历,直到找到值为e的节点或者遍历到链表末尾,最坏情况下需要遍历整个链表,因此时间复杂度为(O(n))。
5.头插法
顾名思义就是不断在第一个位置去插入元素
c
/**
* 头插法创建单链表
* @param L 链表头指针的引用
* @return 创建好的链表头指针
*/
LinkList List_HeadInsert(LinkList &L){
LNode *s = NULL;
ElemType x = 0;
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;
}
6.尾插法
顾名思义就是不断在表尾位置去插入元素
c
/**
* 尾插法创建单链表
* @param L 链表头指针的引用
* @return 创建好的链表头指针
*/
LinkList List_RearInsert(LinkList &L){
LNode *s = NULL;
LNode *q = L;
ElemType x = 0;
printf("请输入插入的元素,输入-1停止插入\n");
scanf("%d", &x);
while (x!=-1) {
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
q->next = s;
s->next = NULL;
q = q->next;
scanf("%d", &x);
}
return L;
}
7.代码实现
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
// 单链表的结构体
typedef struct LNode {
ElemType data; // 定义单链表结点类型
struct LNode *next; // 定义指向下一个节点
} LNode, *LinkList;
// 单链表功能
bool InitList(LinkList &L); // 初始化单链表
int Length(LinkList L); // 求表长
LNode* GetElemByIndex(LinkList &L, int i); // 获取第i个结点
LNode* GetElemByDate(LinkList &L, ElemType e); // 根据值获得结点
bool LinkListInsert(LinkList &L, int i, ElemType e); // 在第i个结点插入
bool ListDelete(LinkList &L, int i, ElemType &e); // 删除第i个结点
LNode* traverseByIndex(LNode *temp_p, int i,int &temp_j);//temp_j:遍历到的位置
LinkList List_HeadInsert(LinkList &L);//头插法
LinkList List_RearInsert(LinkList &L);//尾插法
//遍历单链表
void traverse(LinkList L);
int main() {
LinkList L = NULL;
ElemType e = 0;
int choice, index;
InitList(L); // 初始化链表
LNode* nodeByIndex = NULL;
LNode* nodeByDate = NULL;
LNode* p = NULL;
LNode* q = NULL;
while (1) {
printf("\n请选择操作:\n");
printf("1. 求表长\n");
printf("2. 按位置获取元素\n");
printf("3. 按值获取元素\n");
printf("4. 插入元素\n");
printf("5. 删除元素\n");
printf("6. 遍历\n");
printf("7. 头插法\n");
printf("8. 尾插法\n");
printf("9. 退出\n");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("链表长度为:%d\n", Length(L));
break;
case 2:
printf("请输入要获取元素的位置:");
scanf("%d", &index);
nodeByIndex = GetElemByIndex(L, index);
if (nodeByIndex != NULL) {
printf("位置 %d 的元素值为:%d\n", index, nodeByIndex->data);
} else {
printf("位置不合法或链表为空\n");
}
break;
case 3:
printf("请输入要获取元素的值:");
scanf("%d", &e);
nodeByDate = GetElemByDate(L, e);
if (nodeByDate != NULL) {
printf("值为 %d 的元素已找到,其值为:%d\n", e, nodeByDate->data);
} else {
printf("未找到值为 %d 的元素\n", e);
}
break;
case 4:
printf("请输入要插入的位置:");
scanf("%d", &index);
printf("请输入要插入的元素值:");
scanf("%d", &e);
if (LinkListInsert(L, index, e)) {
printf("元素 %d 已成功插入到位置 %d\n", e, index);
} else {
printf("插入失败,位置不合法或链表为空\n");
}
break;
case 5:
printf("请输入要删除的位置:");
scanf("%d", &index);
if (ListDelete(L, index, e)) {
printf("已删除位置 %d 的元素,其值为:%d\n", index, e);
} else {
printf("删除失败,位置不合法或链表为空\n");
}
break;
case 6:
traverse(L);
break;
case 7:
L = List_HeadInsert(L);
break;
case 8:
L = List_RearInsert(L);
break;
case 9:
// 释放链表内存
p = L;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
return 0;
default:
printf("无效的选择,请重新输入\n");
break;
}
}
return 0;
}
bool InitList(LinkList &L) {
L = (LNode*)malloc(sizeof(LNode));// 头结点
L->next = NULL;// 相邻节点赋值为null
L->data = 0;// 头结点数据域用来记录表长度
return true;
}
int Length(LinkList L) {
int len = 0;
LNode* p = L;
while (p->next != NULL) {
p = p->next;
len++;
}
return len;
}
LNode* GetElemByIndex(LinkList &L, int i) {
int j = 0;
LNode* p = traverseByIndex(L,i,j);
return p; // p为NULL或者想查找的结点
}
LNode* GetElemByDate(LinkList &L, ElemType e) {
LNode* p = L;
while (p!= NULL && p->data != e) {
p = p->next;
}
return p; // p为NULL或者想查找的结点
}
bool LinkListInsert(LinkList &L, int i, ElemType e) {
int j = 0;
LNode* p = traverseByIndex(L,i-1,j);
if (p == NULL) { // 非法区域
return false;
}
LNode* s = (LNode*)malloc(sizeof(LNode));// 申请要插入的结点
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
bool ListDelete(LinkList &L, int i, ElemType &e) {
int j = 0;
LNode* p = traverseByIndex(L,i-1,j);
if (p == NULL || j > i - 1) { // 指向空区域或i不合法
return false;
}
LNode* q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
LinkList List_HeadInsert(LinkList &L){
LNode *s = NULL;
ElemType x = 0;
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;
}
LinkList List_RearInsert(LinkList &L){
LNode *s = NULL;
LNode *q = L;
ElemType x = 0;
printf("请输入插入的元素,输入-1停止插入\n");
scanf("%d",&x);
while(x!=-1){
s = (LNode*)malloc(sizeof(LNode));
s->data = x;
q->next = s;
s->next = NULL;
q = q->next;
scanf("%d",&x);
}
return L;
}
/**tool function*/
LNode* traverseByIndex(LNode* temp_p, int i,int &temp_j){
LNode* p = temp_p;
int j = 0;
while (p!= NULL && j < i) {
p = p->next;
j++;
}
temp_j = j;
return p;
}
void traverse(LinkList L){
LNode* p = L->next;
while(p!=NULL){
printf("%d\t",p->data);
p = p->next;
}
}