数据结构大致包含以下几种存储结构:
- 线性表:还可细分为顺序表、链表、栈、队列。
- 树结构:包括普通树、二叉树、线索二叉树。
- 图储存结构。
2.1线性表基本概念
线性结构是一种最简单且常用的数据结构。线性结构的基本特点是节点之间满足线性关系。本章讨论的动态数组、链表、栈、队列都属于线性结构。他们的共同之处,是节点中有且只有一个开始节点和终端节点。按这种关系,可以把它们的所有节点排列成一个线性序列。但是,他们分别属于几种不同的抽象数据类型实现,它们之间的区别,主要就是操作的不同。
线性表是零个或者多个数据元素的有限序列,数据元素之间是有顺序的,数据元素个数是有限的,数据元素的类型必须相同
2.2线性表的顺序存储
通常线性表可以采用顺序存储和链式存储。这节课我们主要探讨顺序存储结构以及对应的运算算法的实现。
采用顺序存储是表示线性表最简单的方法,具体做法是:将线性表中的元素一个接一个的存储在一块连续的存储区域中,这种顺序表示的线性表也成为顺序表。
2.2.1线性表顺序存储(顺序表)的设计与实现
操作要点:
- 插入元素算法判断线性表是否合法
- 判断插入位置是否合法
- 判断空间是否满足
- 把最后一个元素到插入位置的元素后移一个位置
- 将新元素插入
- 线性表长度加1
- 获取元素操作
- 判断线性表是否合法
- 判断位置是否合法
- 直接通过数组下标的方式获取元素
- 删除元素算法
- 判断线性表是否合法
- 判断删除位置是否合法
- 将元素取出
- 将删除位置后的元素分别向前移动一个位置
- 线性表长度减
1.元素的插入
2.元素的删除
2.2.2优点和缺点
- 优点:
|
- 缺点:
|
2.2.3 顺序表的代码实现
1.创建三个文件
2.代码实现
//Sequence_List_Head.h部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
//1.定义结构体
struct SequenceList
{
void** pSeqList;
int Capacity;
int Size;
};
//2.初始化顺序表函数声明
struct SequenceList* InitSequenceList(int capacity);
//3.插入元素函数声明
void InsertSequenceList(struct SequenceList* seqlist, int pos, void* data);
//4.遍历顺序表函数声明
void DisplaySequenceList(struct SequenceList* seqlist, void(*Print)(void*));
//5.删除元素函数声明(按位置删除)
void DeleteSequenceList_pos(struct SequenceList* seqlist, int pos);
//6.删除元素函数声明(按值删除)
void DeleteSequenceList_value(struct SequenceList* seqlist, void* data, int(*Compare)(void*, void*));
//7.销毁数组函数声明
void DestroySequenceList(struct SequenceList* seqlist);
//Sequence_List_Function.c部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "Sequence_List_Head.h"
//2.初始化顺序表函数实现
struct SequenceList* InitSequenceList(int capacity)
{
if (capacity <= 0)
{
return NULL;
}
//申请空间
struct SequenceList* seqlist = malloc(sizeof(struct SequenceList));
if (seqlist == NULL)
{
return NULL;
}
//初始化空间
seqlist->pSeqList = malloc(sizeof(void*) * capacity);
seqlist->Capacity = capacity;
seqlist->Size = 0;
return seqlist;
}
//3.插入元素函数实现
void InsertSequenceList(struct SequenceList* seqlist, int pos, void* data)
{
if (seqlist == NULL)
{
return;
}
if (data == NULL)
{
return;
}
if (pos<0 || pos>seqlist->Size)
{
pos = seqlist->Size;
}
if (seqlist->Size == seqlist->Capacity)
{
int newCapacity = 2 * (seqlist->Capacity);
void** newpSeqList = realloc(seqlist->pSeqList, sizeof(void*) * newCapacity);
if (newpSeqList == NULL)
{
return;
}
else
{
seqlist->pSeqList = newpSeqList;
seqlist->Capacity = newCapacity;
}
}
for (int i = seqlist->Size - 1; i >= pos; i--)
{
//数据向后移动
seqlist->pSeqList[i + 1] = seqlist->pSeqList[i];
}
seqlist->pSeqList[pos] = data;
seqlist->Size++;
}
//4.遍历顺序表函数实现
void DisplaySequenceList(struct SequenceList* seqlist, void(*Print)(void*))
{
if (seqlist == NULL)
{
return;
}
if (Print == NULL)
{
return;
}
for (int i = 0; i < seqlist->Size; i++)
{
Print(seqlist->pSeqList[i]);
}
}
//5.删除元素函数实现(按位置删除)
void DeleteSequenceList_pos(struct SequenceList* seqlist, int pos)
{
if (seqlist == NULL)
{
return;
}
if (pos<0 || pos>seqlist->Size - 1)
{
return;
}
//数据前移
for (int i = pos; i < seqlist->Size - 1; i++)
{
seqlist->pSeqList[i] = seqlist->pSeqList[i + 1];
}
seqlist->Size--;
}
//6.删除元素函数实现(按值删除)
void DeleteSequenceList_value(struct SequenceList* seqlist, void* data, int(*Compare)(void*, void*))
{
if (seqlist == NULL)
{
return;
}
if (data == NULL)
{
return;
}
for (int i = 0; i < seqlist->Size; i++)
{
if (Compare(seqlist->pSeqList[i], data))
{
DeleteSequenceList_pos(seqlist, i);
break;
}
}
}
//7.销毁数组函数实现
void DestroySequenceList(struct SequenceList* seqlist)
{
if (seqlist == NULL)
{
return;
}
if (seqlist->pSeqList != NULL)
{
free(seqlist->pSeqList);
seqlist->pSeqList = NULL;
}
free(seqlist);
seqlist = NULL;
}
//Sequence_List_Main.c部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "Sequence_List_Head.h"
//测试代码
//1.自定义数据类型
struct Person
{
char name[64];
int age;
};
//2.打印函数
void myPrint(void* data)
{
struct Person* p = data;
printf("姓名:%s 年龄:%d\n", p->name, p->age);
}
//3.对比函数
int myCompare(void* data1, void* data2)
{
struct Person* p1 = data1;
struct Person* p2 = data2;
return (strcmp(p1->name, p2->name) == 0) && (p1->age == p2->age);
}
int main()
{
//初始化动态数组
struct SequenceList* seqlist = InitSequenceList(5);
//准备数据
struct Person p1 = { "亚瑟", 18 };
struct Person p2 = { "妲己", 20 };
struct Person p3 = { "安琪拉", 19 };
struct Person p4 = { "凯", 21 };
struct Person p5 = { "孙悟空", 999 };
struct Person p6 = { "李白", 999 };
printf("插入数据前: 容量:%d 大小:%d\n", seqlist->Size, seqlist->Size);
//插入数据
InsertSequenceList(seqlist, 0, &p1);
InsertSequenceList(seqlist, 0, &p2);
InsertSequenceList(seqlist, 1, &p3);
InsertSequenceList(seqlist, 0, &p4);
InsertSequenceList(seqlist, -1, &p5);
InsertSequenceList(seqlist, 2, &p6);
//遍历数据
DisplaySequenceList(seqlist, myPrint);
printf("插入数据后: 容量:%d 大小:%d\n", seqlist->Capacity, seqlist->Size);
//按位置删除
DeleteSequenceList_pos(seqlist, 2);
printf("---------------------\n");
DisplaySequenceList(seqlist, myPrint);
printf("删除数据后: 容量:%d 大小:%d\n", seqlist->Capacity, seqlist->Size);
//按值删除
struct Person p = { "亚瑟", 18 };
DeleteSequenceList_value(seqlist, &p, myCompare);
printf("---------------------\n");
DisplaySequenceList(seqlist, myPrint);
printf("删除数据后: 容量:%d 大小:%d\n", seqlist->Capacity, seqlist->Size);
//销毁数组
DestroySequenceList(seqlist);
return 0;
}
输出结果
插入数据前: 容量:0 大小:0
姓名:凯 年龄:21
姓名:妲己 年龄:20
姓名:李白 年龄:999
姓名:安琪拉 年龄:19
姓名:亚瑟 年龄:18
姓名:孙悟空 年龄:999
插入数据后: 容量:10 大小:6
---------------------
姓名:凯 年龄:21
姓名:妲己 年龄:20
姓名:安琪拉 年龄:19
姓名:亚瑟 年龄:18
姓名:孙悟空 年龄:999
删除数据后: 容量:10 大小:5
---------------------
姓名:凯 年龄:21
姓名:妲己 年龄:20
姓名:安琪拉 年龄:19
姓名:孙悟空 年龄:999
删除数据后: 容量:10 大小:4
2.3线性表的链式存储(单向链表)
前面我们写的线性表的顺序存储(顺序表)的案例,最大的缺点是插入和删除时需要移动大量元素,这显然需要耗费时间,能不能想办法解决呢?链表。
链表为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。
1.单链表
(1)线性表的链式存储结构中,每个节点中只包含一个指针域,这样的链表叫单链表。
(2)通过每个节点的指针域将线性表的数据元素按其逻辑次序链接在一起(如图)。
2.概念解释:
(1)表头结点
链表中的第一个结点,包含指向第一个数据元素的指针以及链表自身的一些信息
(2)数据结点
链表中代表数据元素的结点,包含指向下一个数据元素的指针和数据元素的信息
(3)尾结点
链表中的最后一个数据结点,其下一元素指针为空,表示无后继。
2.3.1线性表的链式存储(单链表)的设计与实现
- 插入操作
node->next = current->next;
current->next = node;
- 删除操作
current->next = ret->next;
2.3.2优点和缺点
- 优点:
|
- 缺点:
|
2.3.3单链表的代码实现
1. 创建三个文件
2.代码部分
//1.Single_Linked_List_Head.h 部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
typedef void* pSingleLinkedList;
//1.定义节点结构体
struct LinkNode
{
//数据域;
void* data;
//指针域
struct LinkNode* next;
};
//2.链表结构体
struct SingleLinkedList
{
//头节点
struct LinkNode Header;
//链表长度
int Size;
};
//3.初始化链表函数声明
pSingleLinkedList Init_LinkList();
//4.插入链表函数声明
void InsertLinkList(pSingleLinkedList TmpLinkList, int pos, void* data);
//5.遍历链表函数声明
void DisplaySingleLinkedList(pSingleLinkedList TmpLinkList, void(*Print)(void*));
//6.按位置删除链表函数声明
void DeleteSingleLinkedListBypos(pSingleLinkedList TmpLinkList, int pos);
//7.按值删除链表函数声明
void DeleteSingleLinkedListByvalue(pSingleLinkedList TmpLinkList, void* data, int(*Compare)(void*, void*));
//8.返回链表长度函数声明
int size_LinkList(pSingleLinkedList TmpLinkList);
//9.清空链表函数声明
void ClearLinkList(pSingleLinkedList TmpLinkList);
//10.销毁链表函数声明
void DestroyLinkList(pSingleLinkedList TmpLinkList);
//2.Single_Linked_List_Function.c部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "Single_Linked_List_Head.h"
//3.初始化链表函数实现
pSingleLinkedList Init_LinkList()
{
struct SingleLinkedList* MyLinkList = malloc(sizeof(struct SingleLinkedList));
if (MyLinkList == NULL)
{
return NULL;
}
MyLinkList->Header.data = NULL;
MyLinkList->Header.next = NULL;
MyLinkList->Size = 0;
return MyLinkList;
}
//4.插入链表函数声明
void InsertLinkList(pSingleLinkedList TmpLinkList, int pos, void* data)
{
if (TmpLinkList == NULL)
{
return ;
}
if (data == NULL)
{
return ;
}
//将TmpLinkList还原成struct SingleLinkedList数据类型
struct SingleLinkedList* myList = TmpLinkList;
if (pos<0 || pos>myList->Size)
{
//无效位置就做强制尾插
pos = myList->Size;
}
//找到插入节点的位置
struct LinkNode* pCurrent = &myList->Header;
for (int i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
//创建新节点
struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
if (newNode != NULL)
{
newNode->data = data;
newNode->next = NULL;
}
else
{
return;
}
//建立节点关系
newNode->next = pCurrent->next;
pCurrent->next = newNode;
//更新链表长度
myList->Size++;
}
//5.遍历链表函数实现
void DisplaySingleLinkedList(pSingleLinkedList TmpLinkList, void(*Print)(void*))
{
if (TmpLinkList == NULL)
{
return;
}
if (Print == NULL)
{
return;
}
struct SingleLinkedList* mylist = TmpLinkList;
struct LinkNode* pCurrent = mylist->Header.next;
for (int i = 0; i < mylist->Size; i++)
{
Print(pCurrent->data);
pCurrent = pCurrent->next;
}
}
//6.按位置删除链表函数声明
void DeleteSingleLinkedListBypos(pSingleLinkedList TmpLinkList, int pos)
{
if (TmpLinkList == NULL)
{
return;
}
struct SingleLinkedList* mylist = TmpLinkList;
if (pos<0 || pos>mylist->Size - 1)
{
return;
}
//找到待删除节点的前节点
struct LinkNode* pCurrent = &mylist->Header;
for (int i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
//记录待删除的节点
struct LinkNode* pDeleteNode = pCurrent->next;
//重新建立节点关系
pCurrent->next = pDeleteNode->next;
free(pDeleteNode);
pDeleteNode = NULL;
//更新链表长度
mylist->Size--;
}
//7.按值删除链表函数实现
void DeleteSingleLinkedListByvalue(pSingleLinkedList TmpLinkList, void* data, int(*Compare)(void*, void*))
{
if (TmpLinkList == NULL)
{
return;
}
if (data == NULL)
{
return;
}
struct SingleLinkedList* mylist = TmpLinkList;
//创建两个辅助指针
struct LinkNode* pPrevNode = &mylist->Header;
struct LinkNode* pCurrent = pPrevNode->next;
for (int i = 0; i < mylist->Size; i++)
{
if (Compare(pCurrent->data, data))
{
pPrevNode->next = pCurrent->next;
free(pCurrent);
pCurrent = NULL;
mylist->Size--;
break;
}
pPrevNode = pCurrent;
pCurrent = pCurrent->next;
}
}
//8.返回链表长度函数实现
int size_LinkList(pSingleLinkedList TmpLinkList)
{
if (TmpLinkList == NULL)
{
return -1;
}
struct SingleLinkedList* mylist = TmpLinkList;
return mylist->Size;
}
//9.清空链表函数实现
void ClearLinkList(pSingleLinkedList TmpLinkList)
{
if (TmpLinkList == NULL)
{
return;
}
struct SingleLinkedList* mylist = TmpLinkList;
struct LinkNode* pCurrent = mylist->Header.next;
struct LinkNode* pNextNode = pCurrent->next;
for (int i = 0; i < mylist->Size; i++)
{
pNextNode = pCurrent->next;
free(pCurrent);
pCurrent = pNextNode;
}
mylist->Header.next = NULL;
mylist->Size = 0;
}
//10.销毁链表函数声明
void DestroyLinkList(pSingleLinkedList TmpLinkList)
{
if (TmpLinkList == NULL)
{
return;
}
ClearLinkList(TmpLinkList);
free(TmpLinkList);
TmpLinkList = NULL;
}
//3.Single_Linked_List_Main.c部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "Single_Linked_List_Head.h"
//测试链表
//1.定义数据结构体
struct Person
{
char name[64];
int age;
};
//2.打印函数实现
void Print(void* data)
{
struct Person* p = data;
printf("姓名:%s 年龄:%d\n", p->name, p->age);
}
//3.比较函数实现
int Compare(void* data1, void* data2)
{
struct Person* p1 = data1;
struct Person* p2 = data2;
return (strcmp(p1->name, p2->name) == 0) && (p1->age == p2->age);
}
int main()
{
//准备数据
struct Person p1 = { "亚瑟", 18 };
struct Person p2 = { "妲己", 20 };
struct Person p3 = { "安琪拉", 19 };
struct Person p4 = { "凯", 21 };
struct Person p5 = { "孙悟空", 999 };
struct Person p6 = { "李白", 999 };
//初始化链表
pSingleLinkedList mylist = Init_LinkList();
printf("链表长度为:%d\n", size_LinkList(mylist));
//插入数据
InsertLinkList(mylist, 0, &p1);
InsertLinkList(mylist, 0, &p2);
InsertLinkList(mylist, -1, &p3);
InsertLinkList(mylist, 0, &p4);
InsertLinkList(mylist, 1, &p5);
InsertLinkList(mylist, 0, &p6);
//遍历
DisplaySingleLinkedList(mylist, Print);
printf("链表长度为:%d\n", size_LinkList(mylist));
//按位置删除
DeleteSingleLinkedListBypos(mylist, 4);
printf("------------------\n");
DisplaySingleLinkedList(mylist, Print);
printf("链表长度为:%d\n", size_LinkList(mylist));
//按值删除
struct Person p = { "孙悟空", 999 };
DeleteSingleLinkedListByvalue(mylist, &p, Compare);
printf("------------------\n");
DisplaySingleLinkedList(mylist, Print);
printf("链表长度为:%d\n", size_LinkList(mylist));
//返回链表长度
printf("链表长度为:%d\n", size_LinkList(mylist));
//销毁
DestroyLinkList(mylist);
mylist = NULL;
return 0;
}
输出结果
链表长度为:0
姓名:李白 年龄:999
姓名:凯 年龄:21
姓名:孙悟空 年龄:999
姓名:妲己 年龄:20
姓名:亚瑟 年龄:18
姓名:安琪拉 年龄:19
链表长度为:6
------------------
姓名:李白 年龄:999
姓名:凯 年龄:21
姓名:孙悟空 年龄:999
姓名:妲己 年龄:20
姓名:安琪拉 年龄:19
链表长度为:5
------------------
姓名:李白 年龄:999
姓名:凯 年龄:21
姓名:妲己 年龄:20
姓名:安琪拉 年龄:19
链表长度为:4
链表长度为:4
2.3.4单链表的代码实现(企业版)
1.创建三个文件
2.代码部分
//1.Single_Linked_List_Enterprise_Edition_Head.h部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
typedef void* pLinkList;
//1.定义节点结构体
struct LinkListNode
{
//只维护指针域
struct LinkNode* next;
};
//2.定义链表结构体
struct LinkList
{
struct LinkListNode pHeader;
int _size;
};
//3.初始化链表函数声明
pLinkList InitLinkList();
//4.插入链表函数声明
void InsertLinkList(pLinkList List, int pos, void* data);
//5.遍历链表函数声明
void DisplayLinkList(pLinkList List, void(*Print)(void*));
//6.按位置删除节点函数声明
void DeleteLinkListNode(pLinkList List, int pos);
//7.销毁链表函数声明
void DestroyLinkList(pLinkList List);
//2.Single_Linked_List_Enterprise_Edition_Function.c部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "Single_Linked_List_Enterprise_Edition_Head.h"
//3.初始化链表函数实现
pLinkList InitLinkList()
{
struct LinkList* myList = malloc(sizeof(struct LinkList));
if (myList == NULL)
{
return NULL;
}
myList->pHeader.next = NULL;
myList->_size = 0;
return myList;
}
//4.插入链表函数声明
void InsertLinkList(pLinkList List, int pos, void* data)
{
if (List == NULL)
{
return;
}
if (data == NULL)
{
return;
}
struct LinkList* myList = List;
if (pos<0 || pos>myList->_size - 1)
{
//无效位置进行尾插
pos = myList->_size;
}
//用户数据前4个字节由我们来使用
struct LinkListNode* myNode = data;
//找插入节点的前驱节点
struct LinkListNode* pCurrent = &myList->pHeader;
for (int i = 0; i < pos - 1; i++)
{
pCurrent = pCurrent->next;
}
//更改指针指向
myNode->next = pCurrent->next;
pCurrent->next = myNode;
//更新链表长度
myList->_size++;
}
//5.遍历链表函数实现
void DisplayLinkList(pLinkList List, void(*Print)(void*))
{
if (List == NULL)
{
return;
}
struct LinkList* myList = List;
struct LinkListNode* myNode = myList->pHeader.next;
for (int i = 0; i < myList->_size; i++)
{
Print(myNode);
myNode = myNode->next;
}
}
//6.按位置删除节点函数实现
void DeleteLinkListNode(pLinkList List, int pos)
{
if (List == NULL)
{
return;
}
struct LinkList* mylist = List;
if (pos<0 || pos>mylist->_size - 1)
{
return;
}
//找待删位置的前驱节点
struct LinkListNode* pCurrent = &mylist->pHeader;
for (int i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
//记录待删除节点
struct LinkListNode* pDel = pCurrent->next;
//更改指针指向
pCurrent->next = pDel->next;
//更改指针长度
mylist->_size--;
}
//7.销毁链表函数实现
void DestroyLinkList(pLinkList List)
{
if (List == NULL)
{
return;
}
free(List);
List = NULL;
}
//3.Single_Linked_List_Enterprise_Edition_Main.c部分
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "Single_Linked_List_Enterprise_Edition_Head.h"
//测试
//1.定义数据结构体
struct Person
{
void* Node;
char name[64];
int age;
};
//2.打印函数实现
void Print(void* data)
{
struct Person* p = data;
printf("姓名:%s 年龄:%d\n", p->name, p->age);
}
int main()
{
//初始化链表
pLinkList mylist = InitLinkList();
//创建数据
struct Person p1 = { NULL,"aaa", 10 };
struct Person p2 = { NULL,"bbb", 20 };
struct Person p3 = { NULL,"ccc", 30 };
struct Person p4 = { NULL,"ddd", 40 };
struct Person p5 = { NULL,"eee", 50 };
//插入节点
InsertLinkList(mylist, 0, &p1);
InsertLinkList(mylist, 0, &p2);
InsertLinkList(mylist, 1, &p3);
InsertLinkList(mylist, -1, &p4);
InsertLinkList(mylist, 0, &p5);
//遍历链表
DisplayLinkList(mylist, Print);
//删除
DeleteLinkListNode(mylist, 3);
printf("-----------------------\n");
DisplayLinkList(mylist, Print);
//销毁
DestroyLinkList(mylist);
mylist = NULL;
return 0;
}
输出结果
姓名:eee 年龄:50
姓名:ccc 年龄:30
姓名:bbb 年龄:20
姓名:ddd 年龄:40
姓名:aaa 年龄:10
-----------------------
姓名:eee 年龄:50
姓名:ccc 年龄:30
姓名:bbb 年龄:20
姓名:aaa 年龄:10
2.4线性表的链式存储(双向链表)
链表有带头或不带头,循环或不循环的分类,下面仅以双向带头循环链表为例来实现链表的增删查改。
1.创建三个文件
2.代码
//1.List.h 部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
//1.定义结构体
typedef struct ListNode
{
int data;
struct ListNode* next;
struct ListNode* prev;
}LN;
//2.获得新节点函数声明
LN* BuyLTNode(LN* pList);
//3.初始化结构体函数声明
void ListInit(LN** ppList);
//4.插入函数声明
void ListInsert(LN* pList);
//5.打印函数声明
void ListPrint(LN* pList);
//6.回收链表函数声明
void ListDestroy(LN* pList);
//7.头插函数声明
void ListPushFrant(LN* pList);
//8.头删函数声明
void ListPopFrant(LN* pList);
//9.尾插函数声明
void ListPushBack(LN* pList);
//10.尾删函数声明
void ListPopBack(LN* pList);
//11.删除节点函数声明
void ListErase(LN* pList);
//12.查找函数节点声明
void ListFind(LN* pList);
//2.ListFunc.c 部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "List.h"
//2.获得新节点函数实现
LN* BuyLTNode(int x)
{
LN* newnode = (LN*)malloc(sizeof(LN));
if (newnode == NULL)
{
printf("BuyLTNode::()%s\n", strerror(errno));
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
//3.初始化结构体函数实现
void ListInit(LN** ppList)
{
assert(ppList);
*ppList = BuyLTNode(0);
(*ppList)->next = *ppList;
(*ppList)->prev = *ppList;
}
//4.插入函数实现
void ListInsert(LN* pList)
{
assert(pList);
if (pList->next == pList)
{
printf("暂无节点,请输入数值进行插值:");
int x = 0;
scanf("%d", &x);
LN* newnode = BuyLTNode(x);
newnode->next = pList;
newnode->prev = pList;
pList->next = newnode;
pList->prev = newnode;
}
else
{
printf("请输入要在哪个数值前面插值:");
int y = 0;
scanf("%d", &y);
LN* cur = pList->next;
while (cur != pList)
{
if (cur->data == y)
{
break;
}
cur = cur->next;
}
if (cur == pList)
{
printf("无此节点,请重新输入:\n");
return;
}
printf("请输入要插入的值:");
int val = 0;
scanf("%d", &val);
LN* newnode = BuyLTNode(val);
newnode->prev = cur->prev; //与前节点连接
cur->prev->next = newnode;
newnode->next = cur; //与后节点连接
cur->prev = newnode;
}
printf("插入数值成功!\n");
}
//5.打印函数实现
void ListPrint(LN* pList)
{
assert(pList);
LN* cur = pList->next;
while (cur != pList)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
//6.回收链表函数实现
void ListDestroy(LN* pList)
{
assert(pList);
LN* Cur = pList->next;
LN* CurNext = Cur->next;
while (Cur != pList)
{
CurNext = Cur->next; //先保存下一个节点
Cur->next = NULL;
Cur->prev = NULL;
free(Cur);
Cur = NULL;
Cur = CurNext;
}
pList->next = NULL;
pList->prev = NULL;
free(pList);
pList = NULL;
printf("回收空间成功,退出链表\n");
}
//7.头插函数实现
void ListPushFrant(LN* pList)
{
assert(pList);
printf("请输入要头插的数值:");
int x = 0;
scanf("%d", &x);
LN* newnode = BuyLTNode(x);
newnode->next = pList->next; //与后节点连接
pList->next->prev = newnode;
newnode->prev = pList; //与哨兵位连接
pList->next = newnode;
printf("头插函数成功!\n");
}
//8.头删函数实现
void ListPopFrant(LN* pList)
{
assert(pList);
if (pList->next == pList)
{
printf("无节点,无法头删\n");
return;
}
LN* pos = pList->next;
pList->next->next->prev = pList;
pList->next = pList->next->next;
pos->next = NULL;
pos->prev = NULL;
free(pos);
printf("头删成功!\n");
}
//9.尾插函数实现
void ListPushBack(LN* pList)
{
assert(pList);
printf("请输入要尾插的数值:");
int x = 0;
scanf("%d", &x);
LN* newnode = BuyLTNode(x);
newnode->prev = pList->prev;
pList->prev->next = newnode;
newnode->next = pList;
pList->prev = newnode;
printf("尾插成功!\n");
}
//10.尾删函数实现
void ListPopBack(LN* pList)
{
assert(pList);
if (pList->next == pList)
{
printf("无节点,无法尾删\n");
return;
}
LN* pos = pList->prev;
pList->prev->prev->next = pList;
pList->prev = pList->prev->prev;
pos->next = NULL;
pos->prev = NULL;
free(pos);
printf("尾删成功!\n");
}
//11.删除节点函数实现
void ListErase(LN* pList)
{
assert(pList);
if (pList->next == pList)
{
printf("无节点,无法删除\n");
return;
}
printf("请输入要删除的数值:");
int x = 0;
scanf("%d", &x);
LN* cur = pList->next;
while (cur != pList)
{
if (cur->data == x)
{
break;
}
cur = cur->next;
}
if (cur == pList)
{
printf("无该节点,无法删除\n");
return;
}
cur->next->prev = cur->prev;
cur->prev->next = cur->next;
cur->next = NULL;
cur->prev = NULL;
free(cur);
printf("删除节点成功\n");
}
//12.查找函数节点实现
void ListFind(LN* pList)
{
assert(pList);
if (pList->next == pList)
{
printf("无节点,无法查找\n");
return;
}
printf("请输入要查找的数值:");
int x = 0;
scanf("%d", &x);
LN* cur = pList->next;
while (cur != pList)
{
if (cur->data == x)
{
break;
}
cur = cur->next;
}
if (cur == pList)
{
printf("无该节点\n");
return;
}
printf("数值%d的地址为%p\n", x, cur);
}
//3.List.c 部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <stdbool.h>
#include "List.h"
enum Option
{
退出链表,
头插,
头删,
尾插,
尾删,
随机插值,
删除数值,
查找数值,
打印链表
};
void menu()
{
printf("*****************************************\n");
printf("****** 1.头插 2.头删 ******\n");
printf("****** 3.尾插 4.尾删 ******\n");
printf("****** 5.随机插值 6.删除数值 ******\n");
printf("****** 7.查找数值 8.打印链表 ******\n");
printf("****** 0.退出链表 ******\n");
printf("*****************************************\n");
}
int main()
{
LN* pList = NULL;
ListInit(&pList);
int input = 0;
do
{
menu();
printf("请输入要进行的操作:");
scanf("%d", &input);
switch (input)
{
case (头插):
ListPushFrant(pList);
break;
case (头删):
ListPopFrant(pList);
break;
case (尾插):
ListPushBack(pList);
break;
case (尾删):
ListPopBack(pList);
break;
case (随机插值):
ListInsert(pList);
break;
case (删除数值):
ListErase(pList);
break;
case (查找数值):
ListFind(pList);
break;
case (打印链表):
ListPrint(pList);
break;
case (退出链表):
ListDestroy(pList);
break;
default:
printf("输入有误,请重新输入\n");
break;
}
} while (input);
return 0;
}