名词解释
线性存储结构
- 顺序表,链表
链表(Linked List)
- 别名链式存储结构,单链表
- 用于存储逻辑关系为“一对一”的数据
- 与顺序表不同,不限制数据的物理存储状态
- 由一系列的节点(Node)组成
- 每个节点保存了数据元素和指向下一个节点的指针
- 链表中的节点可以动态的添加和移除
- 相较于静态数组,在插入和删除操作时具有更好的小v
- 有单链表,双向链表,循环链表三种
- 单链表,节点只有一个指针指向下一个节点
- 双向链表,在单链表的基础上增加了前向指针
- 循环链表,在单链表的基础上增加了尾节点指向头节点的指针
- 链表内存不是一段连续的存储空间,通过指针进行连接
- 链表需要通过遍历整个链表才能找到指定节点,因此对于频繁访问特定位置的数据,使用链表不是最优选择
节点
- 由数据域和指针域组成
- 数据元素本身,所在的区域被称为【数据域】
- 指向直接后继元素的指针,所在的区域被称为【指针域】
- 分为【头节点】【首元节点】【其他节点】
头节点
- 不存任何数据的空姐点,通常作为链表的第一个节点。
- 对于链表来说,头节点不是必需的
- 方便解决部分问题
首元节点
- 由于头节点的缘故,链表中第一个存有数据的节点被称为首元节点
头指针
- 一个普通的指针
- 永远指向链表第一个节点的位置
- 头指针用于指明链表的位置,便于后期查找链表并使用链表中的数据
- 链表有头节点时,头指针指向头节点;反之,若链表中没有头节点,则头指针指向首元节点
单链表
单链表常用于实现栈、队列和哈希表等数据结构,也常用于算法问题的解决。
优点
-
内存动态分配:单链表采用动态内存分配,可以根据需求动态地分配节点,不会浪费内存空间。
-
插入、删除快:单链表的插入和删除操作只需要改变指针域,不需要移动数据,因此效率较高。
-
灵活性强:单链表支持在链表中任意位置插入、删除结点,可以灵活地适应数据变化。
-
空间利用率高:单链表的每个结点只需要存储一个指针域和一个数据域,相比数组,空间利用率更高。
-
可以有效防止内存泄露:因为单链表的每个节点都是通过动态分配内存来创建的,所以可以避免不必要的内存泄露问题。
缺点
-
随机访问困难:单链表只能顺序访问,要访问其中某个元素,需要从头开始依次访问,直到找到所需元素,因此随机访问效率低下。
-
空间浪费:由于每个节点需要额外的指针来指向下一个节点,因此单链表的存储空间比同样数量的元素的数组要多。
-
删除节点需要额外操作:要删除某个节点,必须先找到其前驱节点,然后将其指向后继节点,这个过程需要额外的操作。
-
不能逆向访问:由于单链表的节点只有一个指向后继节点的指针,没有指向前驱节点的指针,因此不能逆向访问。
-
对于一些应用场景,单链表不能满足需求。例如要求访问某个节点前面的节点,就需要从头开始遍历,效率较低。
初始化
#include<stdio.h>
#include<stdlib.h>
typedef struct Link
{
char elem; //数据域
struct Link * next; //指针域,指向后继元素
}link; //link为节点名,每个节点都是一个link结构体
link * initLink();
link * initLink_2();
void display(link *p);
void display_2(link *p);
//无头结点链表
link * initLink()
{
link * p = NULL; //创建头指针
link * temp = (link *)malloc(sizeof(link));//创建首元节点
//首元节点初始化
temp->elem = 1;
temp->next = NULL;
// 头指针指向首元节点
p = temp;
//从第二个节点开始创建
for(int i = 2; i < 5; i++)
{
link *a = (link *)malloc(sizeof(link));
a->elem = i;
a->next = NULL;
temp->next = a;
temp = temp->next
}
return p;
}
//存储{1,2,3,4},有头节点链表
link * initLink_2()
{
link * p = (link *)malloc(sizeof(link)); //创建头节点
link * temp = p; //声明一个指针指向头节点
//生成链表
for(int i = 1; i < 5; i++)
{
link *a = (link *)malloc(sizeof(link));
a->elem = i;
a->next = NULL;
temp->next = a;
temp = temp->next;
}
return p;
}
//不带头节点
void display(link *p)
{
link *temp = p; //将temp指针重新指向头结点
//只要temp指针指向的结点的next不是NULL,就执行输出语句
while(temp)
{
printf("%d", temp->elem);
temp = temp->next;
}
printf("\n");
}
//带有头节点
void display(link *p)
{
link *temp = p;
while(temp->next)
{
temp = temp->next;
printf("%d", temp->elem);
}
printf("\n");
}
增删改查
#include<stdio.h>
#include<stdlib.h>
typedef struct Link
{
int elem;
struct Link * next;
}link;
link *initLink();
void displayLink(link *l);
link *addLink(link *l, int elem, int add);
link *deletLink(link *l, int add);
link *amendLink(link *l, int add, int newElem);
int selectLink(link *l, int elem);
//有头节点
link *initLink()
{
link *p = (link *)malloc(sizeof(link));//
link * temp = p;//声明一个指针指向头节点
for(int i = 1; i < 9; i++)
{
link *a = (link *)malloc(sizeof(link));
a->elem = i;
a->next = NULL;
temp->next = a;
temp = temp->next;
}
return p;
}
//有头节点
void displayLink(link *l)
{
link *temp = l;
//temp = temp->next;
while(temp->next)
{
temp = temp->next;
printf("%d", temp->elem);
}
printf("\n");
}
link *addLink(link *l, int elem, int add)
{
link * temp = l;
//temp = temp->next;
for(int i = 1; i < add; i++)
{
temp = temp->next;
if(NULL == temp)
{
printf("插入位置无效\n");
return l;
}
}
//创建插入节点
link *c = (link *)malloc(sizeof(link));
c->elem = elem;
//向链表插入元素
c->next = temp->next;
temp->next = c;
return l;
}
link *deletLink(link *l, int add)
{
link *temp = l;
for(int i = 1; i < add; i++)
{
temp = temp->next;
if(temp->next == NULL)
{
printf("没有该节点\n");
return l;
}
}
link *del = temp->next;
temp->next = temp->next->next;
free(del);
return l;
}
link *amendLink(link *l, int add, int newElem)
{
link *temp = l;
temp = temp->next;
for(int i = 1; i < add; i++)
{
temp = temp->next;
}
temp->elem = newElem;
return l;
}
int selectLink(link *l, int elem)
{
link *temp = l;
int i = 1;
while(temp->next)
{
temp = temp->next;
if(temp->elem == elem)
{
return i;
}
i++;
}
return -1;
}
int main()
{
//初始化
link *p = initLink();
displayLink(p);
//第4位插入数据5
p = addLink(p, 5, 4);
displayLink(p);
//删除元素3
p = deletLink(p, 3);
displayLink(p);
//查找元素2的位置
int address = selectLink(p, 2);
printf("%d\n", address);
//更改第3位置的数据为7
amendLink(p, 3, 7);
displayLink(p);
return 0;
}