1.3 线性结构之链表
1.3.1 基础知识
一个数据域一个指针域
逻辑结构:链表是一个线性结构,由一系列结点(Node)
组成,每个结点包含一个数据元素和一个指向下一个结点的指针(Pointer)
。所有结点通过指针相连,形成一个链式结构。通常我们将链表中的第一个结点称为头结点
。
data :数据域,存放结点的值。
next :指针域,存放结点的直接后继的地址。
物理结构:与数组不同,链表中的结点需要自行组织,向系统申请很多分散在内存各处的结点,每个结点都保存了当前结点的数据和下一个结点的地址(指针),通过指针将结点串成一串。
具体如下:
链表的分类:
链表又分为单链表、双链表、循环单链表、循环双链表和静态链表。
相关概念
n个结点离散分配,彼此通过指针相连,每个结点只有一个前驱结点,每个结点只有一个后续结点,头结点没有前驱结点,尾结点没有后续结点。确定一个链表我们只需要头指针,通过头指针就可以把整个链表都能推出来。
1.3.2优缺点
优点:
插入和删除操作效率高
动态扩展性能更好
链表不需要像数组那样预先指定固定的大小,而是可以随时动态的增长或缩小。
链表是真正的动态数据结构,不需要处理固定容量的问题
缺点:
查找慢
由于链表中的结点不是连续存储的,无法像数组一样根据索引直接计算出每个结点的地址。必须从头结点开始遍历链表,直到找到目标结点,这导致了链表的随机访问效率较低。
额外的存储空间
链表的每个结点都需要存储指向下一个结点的指针,这会占用额外的存储空间。所以,相比于数组,链表需要更多的内存空间来存储相同数量的数据元素。
1.3.3 功能定义
初始化链表 |
---|
void initLinkedList(LinkedList *list) |
返回链表的长度 |
size_t getLength(const LinkedList *list) |
在指定位置插入元素 |
void insertAt(LinkedList *list, size_t index, int element) |
在末尾插入元素 |
void insertEnd(LinkedList *list, int element) |
删除指定位置的元素并返回被删除的元素 |
int deleteAt(LinkedList *list, size_t index) |
删除末尾元素 |
int deleteEnd(LinkedList *list) |
获取指定位置的元素 |
int getElementAt(const LinkedList *list, size_t index) |
修改指定位置的元素 |
void modifyAt(LinkedList *list, size_t index, int newValue) |
释放链表内存 |
| void destroyLinkedList(LinkedList *list)
1.3.4 代码实现
#include<stdio.h>
#include<stdlib.h>
/**
* 自定义链表结构
*/
//定义存储数据的结构体
typedef struct Node
{
int data;
struct Node *next;
}Node;
//定义一个虚拟头节点
typedef struct
{
int size; //记录单链表中存储数据的个数
Node *next
}LinkedList;
//明确:在包含虚拟头节点的情况下:首个保存数据的节点的索引为0
// 初始化链表
void initLinkedList(LinkedList *list){
//初始化LinkedList内部的成员
list->size = 0;
list->next = NULL;
}
// 返回链表的长度
size_t getLength(const LinkedList *list){
return list->size;
}
// 在指定位置插入元素
void insertAt(LinkedList *list, size_t index, int element){
//不合法的情况
if (index < 0 || index > list->size)
{
printf("输入的index非法!\n");
return;
}
//插入数据
//1.将数据封装到node结构体的变量中
Node *node = (Node *)malloc(1 * sizeof(Node));
node->data = element;
//找到index的位置进行插入操作
if (index ==0)
{
node->next = list->next;
list->next = node;
list->size++;
return;
}else if (index < list->size)
{
Node *currentnode = list->next;//指向有数据的首元素
for (int i = 0; i < index; i++)
{
currentnode = currentnode->next;
}
node->next = currentnode->next;
currentnode->next = node;
list->size++;
return;
}
}
// 在末尾插入元素
void insertEnd(LinkedList *list, int element){
insertAt(list,list->size,element);
}
// 删除指定位置的元素并返回被删除的元素
int deleteAt(LinkedList *list, size_t index){
if (index < 0 || index >= list->size)
{
printf("不合法!\n");
return;
}
Node * deleteNode = list->next;
int deleteElem = deleteNode->data;
if (index == 0)
{
list->next = deleteNode->next;
}else{
Node *currentNode = list->next;
for (int i = 0; i < index -1; i++)
{
currentNode = currentNode->next;
}
currentNode->next = deleteNode->next;
}
//获取要删除的node的数据
list->size --;
free(deleteNode);
return deleteElem;
}
// 删除末尾元素
int deleteEnd(LinkedList *list){
deleteAt(list,list->size-1);
}
// 获取指定位置的元素
int getElementAt(const LinkedList *list, size_t index){
if (index < 0 || index >= list->size)
{
printf("输入的index不合法!\n");
return -1;
}
Node *currentNode = list->next;
for (int i = 0; i < index; i++)
{
currentNode = currentNode->next;
}
return currentNode->next;
}
// 修改指定位置的元素
void modifyAt(LinkedList *list, size_t index, int newValue){
if (index < 0 || index >= list->size)
{
printf("输入的index不合法!\n");
return -1;
}
Node *currentNode = list->next;
for (int i = 0; i < index; i++)
{
currentNode = currentNode->next;
}
currentNode->data = newValue;
}
// 释放链表内存
void destroyLinkedList(LinkedList *list){
Node * currentNode = list->next;
for (int i = 0; i < list->size; i++)
{
Node * tempNode = currentNode;
currentNode = currentNode->next;
free(tempNode);
}
list->next = NULL;
list->size = 0;
}
int main(){
LinkedList list;
initLinkedList(&list);
insertAt(&list,0,10);
insertAt(&list,0,20);
insertAt(&list,0,30);
size_t count = getLength(&list);
printf("%d",count);
getchar();
return 0;
}