1、什么是数据结构:
- 数据结构是计算机中用于
组织
和存储数据
的方式或者方法,是数据之间存在的一种或者多种特定关系的数据元素集合。为编写出一个优良的程序,必须分析待处理对象的特性
与各个处理对象之间存在的关系。 -
作用:帮助用户去高效的处理数据
- 1、提高
时间
利用效率 - 2、提高
内存
利用效率
- 1、提高
-
数据:指客观事物的符号表示,所有能被计算机处理的符号的总称
- 整形、浮点型、结构体类型等等
-
结构:指现实中的数据都存在一定的隐含的关系,这个关系就称作为结构
- 1、
集合结构
:数据与数据之间除了属于同一集合,再无其他关系 - 2、
线性结构
:数据与数据之间呈现一对一关系 - 3、
树形结构
:数据与数据之间存在一对多关系 - 4、
图形结构
:数据与数据之间存在多对多关系
- 1、
数据结构的应用
- 数学或者代数系统
- 数据的一些计算
- 数据的一些表示
- 数据之间的一些算式关系
- 计算机硬件
- 数据的存储(列如:flash、数据库)
- 计算机软件
- 文件系统
- 信息检索
- 数据结构的组织
2、算法
高效的实现特定功能的
描述步骤
-
时间复杂度:衡量一个算法的运行速度
- 事前分析方法:
- 一个算法的运行时间是指一个
算法
在计算机上运行所耗费的时间
大致可以等于
计算机执行一种
简单操作
(如:赋值、比较、移动等等)所需要的时间
与
算法中
所进行的简单操作次数
的乘积
- 算法的运行时间 = 一个简单操作的时间 * 简单操作的次数
- 也可以算作:算法中每条语句执行时间之和
- 一个算法的运行时间是指一个
- 时间复杂度:
- 算法中基本
操作重复
执行的次数是
问题规模n的某个函数f(n)
算法的时间
度量,记作:T(n) = O(f(n))
- 他表示随着问题规模n的增大,算法执行时间的增长和f(n)增长率相同,称作算法的渐进时间复杂度,简称时间复杂度
- 时间复杂度定义中所指的基本语句是在算法执行过程中重复执行次数最多的语句,时间复杂度实际上是由嵌套层次最深的语句的频度决定的
- 算法中基本
- 事前分析方法:
在考虑算法的时间复杂度时,还需要考虑问题的规模和问题的形式,因此:
- 1、最坏时间复杂度:指在最坏的情况下,算法的时间复杂度
- 2、最好时间复杂度:指在最好的情况下,算法的时间复杂度
- 3、平均时间复杂度:指在所有可能输入实例等概率出现的情况下,算法的期望运行时间。
![在这里插入图片描述](https://img-blog.csdnimg.cn/4597616371394e9e9d76da7b5136c0a8.png)
空间复杂度:衡量一个算法所需要的额外空间,记作:
- S(n) = O(f(n))
- 其中n为问题大小的规模。算法要占据的空间包括:算法本身要占据的空间、输入输出、指令、常数、变量、算法在执行时所需要的辅助空间
3、线性表:
-
1、线性表是具有相同特性元素的一个有限序列,数据元素之间是线性关系。起始元素称为线性表的起点,终端元素称为线性表的终点。
- 特点如下:
- 1、存在唯一 一个被称为“第一个”的元素数据
- 2、存在唯一 一个被称为“最后一个”的元素数据
- 3、除第一个元素外,集合中的每个元素均只有一个前驱
- 4、除最后一个元素外,集合中的每个元素均只有一个后继
- 特点如下:
-
2、线性表的存储
- 1、顺序存储:使用计算机里面的一块
连续的内存空间
存放线性表中的所有元素 - 2、链式存储:使用计算机里面的一块内存空间(可以是连续,也可以是不连续的)放线性表中的所有元素
- 1、顺序存储:使用计算机里面的一块
-
3、线性表的顺序存储(顺序表)——————以数组为例
- 静态数组:后期不允许改变数组的大小
- int arr[10];
- 动态数组:
- malloc ()声明动态数组
- int *p = (int *) malloc(sizeof(int))
- 注意:使用malloc开辟动态空间之后需要手动使用free释放堆空间,以避免内存泄漏
- 静态数组:后期不允许改变数组的大小
4、线性表的 顺序表示结构
-
顺序表的优缺点:
- 1、存储密度大,可以随机存取表中的任一元素
- 2、在插入、删除某一元素时,需要移动大量其他元素
- 3、浪费大量存储空间
- 4、属于静态存储形式,数据元素的个数不能够自由扩充。
-
顺序表中
插入算法
的时间复杂度或者是计算删除算法
的时间复杂度:- 若插入在尾节点之后,则无需移动表中的元素,
特别快
- 若插入在首结点之前,则表中的元素全部后移,
特别慢
- 若删除尾节点,则无需移动表中的元素,
特别快
- 若删除首节点,则表中的n-1个元素全部前移,
特别慢
- 若插入在尾节点之后,则无需移动表中的元素,
练习:写出一个最佳先行顺序表
#include "stdio.h"
#include "stdlib.h"
typedef struct
{
int *lsit;//顺序表
int size;//顺序表中的元素个数
int capacity;//顺序表的容量
}BestLinerList;
//初始化顺序表
void initBestLinearList(BestLinerList *Bastlist, int capacity)
{
Bastlist->lsit = (int *)malloc(sizeof(int) * capacity);
if(Bastlist == NULL)
{
printf("顺序表开辟空间失败\n");
return;
}
Bastlist->size = 0;
Bastlist->capacity = capacity;
}
//销毁顺序表
void destroyBestLinearList(BestLinerList *Bastlist)
{
free(Bastlist->lsit);
Bastlist->lsit = NULL;
Bastlist->size = 0;
Bastlist->capacity = 0;
}
//在指定位置插入元素
void insertElement(BestLinerList *Bastlist, int index, int element)
{
//插入数据位置越界
if(index < 0 || index > Bastlist->size)
{
printf("插入位置无效\n");
return;
}
//数据满员,进行内存扩容
if(Bastlist->size >= Bastlist->capacity)
{
Bastlist->capacity *= 2;
Bastlist->lsit = (int *)realloc(Bastlist->lsit, sizeof(int ) * Bastlist->capacity);//对原有的顺序表进行扩容
if(Bastlist->lsit == NULL)
{
printf("扩容失败\n");
return ;
}
}
//(插入在比自己小的数后)
for(int i = 0; i < Bastlist->size; i++)
{
if(element > Bastlist->lsit[i])
{
index += 1;
}
}
//插入之后的位置往后移
for(int i = Bastlist->size; i > index; i--)
{
Bastlist->lsit[i] = Bastlist->lsit[i - 1];
}
Bastlist->lsit[index] = element;
Bastlist->size++;
}
//删除某个位置的元素
void deleteElement(BestLinerList *BastList, int index)
{
if(index < 0 || index > BastList->size)
{
printf("你删除的数据不在顺序表中\n");
return;
}
for(int i = index; i < BastList->size - 1; i++)
{
BastList->lsit[i] = BastList->lsit[i + 1];
}
BastList->size--;
//进行数据缩容
if(BastList->size < BastList->capacity / 2)
{
BastList->capacity /= 2;
BastList->lsit = (int *)realloc(BastList->lsit, sizeof(int) * BastList->capacity);
if(BastList->lsit == NULL)
{
printf("缩容失败\n");
return;
}
}
}
//打印出顺序表中的数据
void printBestLinearList(BestLinerList *BastList)
{
for(int i = 0; i < BastList->size; i++)
{
printf("List[%d] = %d\n", i, BastList->lsit[i]);
}
}
int main()
{
BestLinerList BastList;
initBestLinearList(&BastList, 10);
insertElement(&BastList, 0, 4);
insertElement(&BastList, 0, 3);
insertElement(&BastList, 0, 8);
insertElement(&BastList, 0, 4);
insertElement(&BastList, 0, 8);
insertElement(&BastList, 0, 9);
printBestLinearList(&BastList);//插入数据进行排序
printf("\n");
deleteElement(&BastList, 0);//删除0号数据
printf("\n");
printBestLinearList(&BastList);
destroyBestLinearList(&BastList);
return 0;
}
5、线性表的 链式表示结构
链式存储:使用计算机里面的一块内存空间(可以是连续,也可以是不连续的)放线性表中的所有元素
-
为了表示每个元素ai与其后继ai+1之间的逻辑关系,对于ai来说,除了本身的存储信息之外,还需存储一个指示其直接后继的位置信息(即直接后继的存储位置信息)。这两部分信息组成元素ai的存储映像,称为节点(node)。它包括两个域:其中存储元素数据的域称为数据域、存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或者链。
- 1、节点在存储器中的位置时任意的,即逻辑上相邻的数据不一定在物理上相邻。线性表的链式结构又称为非顺序映像或链式映像
- 2、访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余节点,所以寻找第一个节点和最后一个节点所花费的时间时不等的,这种存取方式称为顺序存取。
链表(Link List)的分类:单链表、双链表、循环链表
①、单链表:
- 节点:包含数据域和指针域
- 设置头节点的好处:
- 1、便于首元的处理:首元的位置保存在头结点的位置中,所以在链表的第一个位置上的操作和其他位置一致,无需进行特殊处理
- 2、便于空表和非空表的同意处理:无论链表是否为空,头指针都是指向头节点的非空指针,因此空表和非空表的处理也就统一了
- 3、头结点的数据与装的什么?头结点的数据域可以为空,也可以存放线性表的长度等附加i信息,但是头节点不能计入链表的长度值
单链表的实现:
#include "stdio.h"
#include <stdlib.h>
#include <string.h>
typedef struct
{
char name[32];
char study_Number[32];
char sex;
int age;
float score;
}data;
typedef struct Node
{
data Data;
struct Node *next;
}Node;
//创建空链表
Node* initList()
{
Node *list = (Node *)malloc(sizeof(Node));
if(list == NULL)
{
printf("开辟失败\n");
return NULL;
}
list->next = NULL;
return list;
}
//插入节点(头插)
void insrt_List_Head(Node *List, char *name, int age)
{
if(List->next == NULL)
{
Node *new = (Node *)malloc(sizeof(Node));
strcpy(new->Data.name, name);
new->Data.age = age;
List->next = new;
new->next = NULL;
return;
}
Node *new = (Node *)malloc(sizeof(Node));
strcpy(new->Data.name, name);
new->Data.age = age;
new->next = List->next;
List->next = new;
}
//插入节点(尾插)
void insrt_List_Tail(Node *List, char *name, int age)
{
while(List->next)//寻找最后一个节点
{
List = List->next;
}
Node *new = (Node *)malloc(sizeof(Node));
strcpy(new->Data.name, name);
new->Data.age= age;
List->next = new;
new->next = NULL;
}
//插入节点(指定位置)
void insert_List_Pos(Node *List, int index, char *name, int age)
{
int num = 0;
int index_Tra = 0;//记录当前的位置
Node *Tra = List;
if(Tra->next == NULL)
{
printf("链表为空\n");
return;
}
while(Tra->next)
{
Tra = Tra->next;
num++;//计算链表中节点的个数
}
if(index > num )//指定插入的节点超过链表的范围
{
Tra = List;
while(Tra->next)
{
Tra = Tra->next;
}
Node *new = (Node *)malloc(sizeof(Node));
strcpy(new->Data.name, name);
new->Data.age = age;
Tra->next = new;
new->next = NULL;
return;
}
else//在现有的节点中插入
{
Tra = List;
while(Tra->next)
{
if(++index_Tra == index)
{
Node *new = (Node *)malloc(sizeof(Node));
strcpy(new->Data.name, name);
new->Data.age = age;
new->next = Tra->next;
Tra->next = new;
return;
}
Tra = Tra->next;
}
}
}
//销毁链表
void delList(Node *lsit)
{
while(lsit->next)
{
free(lsit);
lsit->next = NULL;
lsit = lsit->next;
}
free(lsit);
}
//删除链表(头删除)
void deleteList_Head(Node *List)
{
if(List->next == NULL)
{
printf("链表为空\n");
return;
}
free(List->next);
List->next = List->next->next;
}
//删除链表(尾删除)
void deleteList_Tail(Node *List)
{
Node *lastNode = NULL;
Node *Tra = List;
if(Tra->next == NULL)
{
printf("链表为空\n");
return;
}
while(Tra->next)
{
lastNode = Tra;
Tra = Tra->next;
}
free(Tra);
lastNode->next = NULL;
}
//查找单链表中倒数第k个节点的数据
void deleteList_K(Node *List, int K)
{
Node *Tra = List;
Node *lastNode = NULL;
int Num = 0; //存储链表中节点的个数
int index = 0;
int len_List = 0;
while(Tra->next)
{
Num++;
Tra = Tra->next;
}
//求出要删除的位置
len_List = Num - K + 1;
if(len_List < 0)
{
printf("删除的数据不在链表范围内\n");
return;
}
Tra = List;
while(Tra->next)
{
lastNode = Tra;
if(++index == len_List)
{
free(Tra->next);
Tra->next = Tra->next->next;
return;
}
Tra = Tra->next;
}
}
//删除链表(按照数据删除)
void deleteList_Data(Node *List, int age)
{
if(List->next == NULL)
{
printf("链表为空\n");
return;
}
Node *del;
while(List->next)
{
if(List->next->Data.age == age)//寻找数据
{
del = List->next;
free(List->next);
List->next = List->next->next;//改变指向
del->next = NULL;
return;
}
List = List->next;
}
}
//倒转链表
Node* List_Func(Node *List)
{
Node *crrunt = List->next;
Node *nextNode = NULL;
Node *prevNode = NULL;
while(crrunt)
{
nextNode = crrunt->next;
crrunt->next = prevNode;
prevNode = crrunt;
crrunt = nextNode;
}
List->next = prevNode;
return List;
}
//显示链表
void show_List(Node *list)
{
if(list->next == NULL)
{
printf("链表为空\n");
return;
}
while(list->next)
{
list = list->next;//改变头指针的指向
printf("姓名:%s--年龄:%d\n", list->Data.name, list->Data.age);
}
}
int main(void)
{
Node *L;//头指针
L = initList();//头指针指向头节点
insrt_List_Head(L, "张1", 1);
insrt_List_Head(L, "张2", 2);
insrt_List_Tail(L, "张6", 6);
insrt_List_Tail(L, "张7", 7);
insert_List_Pos(L, 8, "张10", 10);
show_List(L);
printf("****\n");
printf("****\n");
List_Func(L);
show_List(L);
// deleteList_Head(L);
// printf("****\n");
// show_List(L);
// deleteList_Tail(L);
// printf("****\n");
// show_List(L);
deleteList_K(L, 2);
printf("****\n");
show_List(L);
}
②、单向循环链表
-
循环链表:是一种头尾相接的链表(即:表中最后一个节点的指针域指向头节点,整个链表形成一个环)
-
优点:从表中任意节点出发,都能找到表中的其他节点
-
注意:由于循环链表没有NULL指针,故涉及遍历操作时,其终止条件就不再像非循环链表那样判断P或者P->next是否为空,而是判断他们是否等于头指针
-
当循环链表要经常使用头节点和尾节点的时候,循环链表通常使用尾指针表示法更简单
-
头指针查找
-
尾指针查找
循环链表的基本操作实现
#include "stdio.h"
#include <stdlib.h>
//链表节点信息
typedef struct Node
{
int data;
struct Node * next;
}Node;
//创建循环链表
Node* listInit()
{
Node *P = (Node *)malloc(sizeof(Node));
if(P == NULL)
{
printf("开辟空间失败\n");
return NULL;
}
P->next = P;//指向本身
return P;
}
//头插法
void list_Insert_Head(Node *List, int data)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
new->next = List->next;
List->next = new;
}
//尾插法
void list_Insert_Tail(Node *List, int data)
{
Node *temp = List;
while(temp->next != List)
{
temp = temp->next;
}
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
temp->next = new;
new->next = List;
}
//头删除
void list_Delete_Head(Node *List)
{
Node *temp = List->next;//存储第一个节点
Node *tra = List;
if(tra->next == List)
{
printf("链表为空\n");
return;
}
tra->next = tra->next->next;
free(temp);
}
//尾删除
void list_Delete_Tail(Node *List)
{
Node *lastNode = List;
Node *temp = List;
if(temp->next == List)
{
printf("链表为空\n");
return;
}
while(temp->next != List)
{
lastNode = temp;//始终比temp少一个节点
temp = temp->next;
}
free(temp);
lastNode->next = List;
}
//遍历链表
void list_Show(Node *List)
{
if(List->next == List)
{
printf("链表为空\n");
return ;
}
Node *temp = List;
while(temp->next != List)
{
temp = temp->next;
printf("Data = %d\n", temp->data);
}
}
int main()
{
Node *L = listInit();
list_Insert_Head(L, 1);
list_Insert_Head(L, 2);
list_Insert_Head(L, 3);
list_Insert_Head(L, 4);
list_Show(L);
printf("*******\n");
list_Insert_Tail(L, 5);
list_Insert_Tail(L, 6);
list_Insert_Tail(L, 7);
list_Insert_Tail(L, 8);
list_Show(L);
printf("*******\n");
list_Delete_Head(L);
list_Show(L);
printf("*******\n");
list_Delete_Tail(L);
list_Show(L);
printf("*******\n");
}
③、双向链表
- 在单链表的每个节点里再加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称双向链表。
- 双向链表的对称性
- 注意:在插入和删除时,则需要同时修改两个方向的指针,两者操作的时间复杂度均为O(n)
双向链表的实现
#include "stdio.h"
#include "stdlib.h"
//创建链表节点的结构体信息
typedef struct Node
{
int data;
struct Node *prior;
struct Node *next;
}Node;
//创建双向链表
Node* list_Init()
{
Node *P = (Node *)malloc(sizeof(Node));
if(P == NULL)
{
printf("开辟失败\n");
return NULL;
}
//改变头结点的指向
P->prior = NULL;
P->next = NULL;
return P;
}
//双向链表插入(头部)
void list_Insert_Head(Node *List, int data)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
//先判断链表是不是空的
if(List->next == NULL && List->prior == NULL)
{
List->next = new;
new->prior = List;
new->next = NULL;
return;
}
//链表不为空,防止使用空指针
new->prior = List;
new->next = List->next;
List->next->prior = new;
List->next = new;
}
//双向链表尾插
void list_Insert_Tail(Node *List, int data)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
//先判断链表是不是空的
if(List->next == NULL && List->prior == NULL)
{
List->next = new;
new->prior = List;
new->next = NULL;
return;
}
//找到最后一个节点temp
Node *temp = List;
while(temp->next)
{
temp = temp->next;
}
temp->next = new;
new->prior = temp;
new->next = NULL;
}
//双向链表指定位置插入
void list_Insert_Pos(Node *List, int pos, int data)
{
int index = 0;
int Num = 0;
Node *temp = List;
while(temp->next != NULL)
{
Num++;
temp = temp->next;
}
if(pos > Num || pos < 1)//从1开始插入
{
printf("超出链表范围\n");
return;
}
temp = List;
while(List->next)
{
if(++index == pos)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
new->prior = List;
new->next =List->next;
List->next->prior = new;
List->next = new;
return;
}
List = List->next;
}
}
//双向链表头删
void list_Delete_Head(Node *List)
{
Node *temp = List->next;//用来释放内存用的
if(List->next == NULL && List->prior == NULL)
{
printf("链表为空\n");
return;
}
if(List->next->next == NULL)//表示节点只有一个了
{
List->next = NULL;
free(temp);
return;//直接返回
}
List->next->next->prior = List;//指向头节点
List->next = List->next->next;//头节点指向第二个节点
free(temp);
}
//双向链表尾删
void list_Delete_Tail(Node *List)
{
Node *temp = List;
if(List->next == NULL && List->prior == NULL)
{
printf("链表为空\n");
return;
}
while(temp->next)
{
temp = temp->next;
}
Node *lastNode = temp->prior;
lastNode->next = NULL;
free(temp);
}
//双向链表在指定位置删除
void list_Delete_Pos(Node *List, int pos)
{
Node *temp = List;
int Num = 0;
int index = 0;
if(List->next == NULL && List->prior == NULL)
{
printf("链表为空\n");
return;
}
while (temp->next)
{
Num++;
temp = temp->next;
}
if(pos > Num || pos < 1)
{
printf("删除的位置超出链表范围\n");
return;
}
temp = List;
while(temp->next)
{
temp = temp->next;
if(++index == pos)
{
temp->prior->next = temp->next;
if(temp->next == NULL)//表示删除的是最后一个节点
{
free(temp);
return;
}
temp->next->prior = temp->prior;
free(temp);
return;
}
}
}
//双向链表删除指定的第一个数据
void list_Delete_Data(Node *List, int data)
{
if(List->next == NULL && List->prior == NULL)
{
printf("链表为空\n");
return;
}
Node *temp = List;
while(temp->next)
{
temp = temp->next;
if(temp->data == data)
{
temp->prior->next = temp->next;
if(temp->next == NULL)//防止找到的数据是最后一个节点
{
free(temp);
return;
}
temp->next->prior = temp->prior;
free(temp);
return;
}
}
}
//双向链表遍历
void list_Show(Node *List)
{
if(List->next == NULL && List->prior == NULL)
{
printf("链表为空\n");
return;
}
while(List->next != NULL)
{
List = List->next;
printf("Data = %d\n", List->data);
}
printf("******\n");
}
int main()
{
Node *L = list_Init();
list_Show(L);
//头插
list_Insert_Head(L, 1);
list_Insert_Head(L, 2);
//尾插
list_Insert_Tail(L, 6);
list_Insert_Tail(L, 7);
//指定位置插
list_Insert_Pos(L, 2, 10);
list_Show(L);
//删除指定数据
list_Delete_Data(L, 7);
list_Show(L);
//头删除
list_Delete_Head(L);
list_Show(L);
//尾删除
list_Delete_Tail(L);
list_Show(L);
}
注意:
- 在进行删除节点的时候需要判断此节点是不是最后一个节点
④、双向循环链表:
- 双向循环链表的头节点的前向指针域存放的是双向循环链表的尾节点的地址
- 双向循环链表的尾节点的后向指针域存放的是双向循环链表的头节点的地址
双向循环链表的实现
#include "stdio.h"
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node *prior;
struct Node *next;
}Node;
//创建双向循环链表
Node* list_Init()
{
Node *P = (Node *)malloc(sizeof(Node));
if(P == NULL)
{
printf("开辟失败\n");
}
P->next = P;
P->prior = P;
return P;
}
//双向循环链表(头插)
void list_Insert_Head(Node *List, int data)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
Node *temp = List;
if(temp->next == List && temp->prior == List)//链表为空链表
{
List->next = new;
List->prior = new;
new->next = List;
new->prior = List;
return;
}
new->next = List->next;
new->prior = List;
List->next->prior = new;
List->next = new;
}
//双向循环链表(尾插)
void list_Insert_Tail(Node *List, int data)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
Node *temp = List;
if(temp->next == List && temp->prior == List)//链表为空链表
{
List->next = new;
List->prior = new;
new->next = List;
new->prior = List;
return;
}
new->next = List;
new->prior = List->prior;
List->prior->next = new;
List->prior = new;
}
//双向循环链表(指定位置插入)(从第一个节点到最后一个节点之间)
void list_Insert_Pos(Node *List, int Pos, int data)
{
int Num = 0;
int index = 0;
Node *temp = List;
while(temp->next != List)
{
temp = temp->next;
Num++;
}
if(Pos > Num || Pos < 1)
{
printf("插入的位置不在链表范围\n");
return;
}
//新节点在错误返回之后开辟,避免照成内存泄漏
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
temp = List;
while(temp->next != List)
{
if(++index == Pos)
{
new->next = temp->next;
new->prior = temp;
temp->next->prior = new;
temp->next = new;
return;
}
temp = temp->next;
}
}
//中间位置插入
int list_Middle(Node *List, int data)
{
int Num = 0;
Node *temp = List;
while (temp->next != List)
{
Num++;
temp = temp->next;
}
list_Insert_Pos(List, Num / 2 + 1, data);
}
//双向循环链表(头删)
void list_Delete_Head(Node *List)
{
Node *temp = List;
Node *tra = List->next;//存储第一个节点,用于释放
if(temp->next == List || temp->prior == List)
{
printf("链表为空\n");
return;
}
temp->next->next->prior = List;
temp->next = temp->next->next;
free(tra);
}
//双向循环链表(尾删)
void list_Delete_Tail(Node *List)
{
Node *temp = List;
Node *tra = List->prior;//存储最后一个节点,用于释放
if(temp->next == List || temp->prior == List)
{
printf("链表为空\n");
return;
}
temp->prior->prior->next = List;
temp->prior = temp->prior->prior;
free(tra);
}
//双向链表删除(指定位置)
void list_Delete_Pos(Node *List, int Pos)
{
Node *temp = List;
Node *tra = List;//存放需要删除的节点,用于释放
int Num = 0;
int index = 0;
if(temp->next == List || temp->prior == List)
{
printf("链表为空\n");
return;
}
//获取链表中的节点数
while(temp->next != List)
{
Num++;
temp = temp->next;
}
if(Pos > Num || Pos < 1)
{
printf("删除的位置不在链表范围\n");
return;
}
temp = List;
while(temp->next != List)
{
temp = temp->next;
if(++index == Pos)
{
tra = temp;
temp->next->prior = temp->prior;
temp->prior->next = temp->next;
free(tra);
return;
}
}
}
//双向链表删除(指定数据)
void list_Delete_Data(Node *List, int data)
{
Node *temp = List;
Node *tra = List;//存放需要删除的节点,用于释放
if(temp->next == List || temp->prior == List)
{
printf("链表为空\n");
return;
}
while(temp->next != List)
{
temp = temp->next;
if(temp->data == data)
{
tra = temp;
temp->next->prior = temp->prior;
temp->prior->next = temp->next;
free(tra);
return;
}
}
}
//遍历双向循环链表
void list_Show(Node *List)
{
Node *tra = List;
if(List->next == List && List->prior == List)
{
printf("链表为空\n");
return;
}
while(tra->next != List)
{
tra = tra->next;
printf("Data = %d\n", tra->data);
}
printf("*************\n");
}
int main()
{
Node *L = list_Init();
list_Show(L);
//头插
list_Insert_Head(L, 2);
list_Insert_Head(L, 1);
list_Show(L);
//尾插
list_Insert_Tail(L, 3);
list_Insert_Tail(L, 4);
list_Show(L);
//指定位置插入
list_Insert_Pos(L, 2, 10);
list_Show(L);
//中间插入
list_Middle(L, 100);
list_Show(L);
//头删
list_Delete_Head(L);
list_Show(L);
//尾删
list_Delete_Tail(L);
list_Show(L);
//指定位置删
list_Delete_Pos(L, 1);
list_Show(L);
//删除指定数据
list_Delete_Data(L, 100);
list_Show(L);
}
链式存储结构的优点:
- 1、节点的空间可以动态申请和释放
- 2、数据元素的逻辑次序靠节点的指针来指示,插入和删除时不需要移动数据元素
链式存储结构的缺点:
- 1、存储密度小,每个结点的指针域需要额外的占用存储空间,当每个节点的数据域占用的字节不多时,指针域所占用存储空间的比重就显得很大
- 2、链式存储结构式非随机存取结构,对任一节点的操作都要从头指针依指针链查找到该节点,这增加了算法的复杂度
6、练习
约瑟夫环历史故事:
约瑟夫,是一个古犹太人,曾经在一次罗马叛乱中担任将军,后来战败,他和朋友及另外39个人躲在一口井里,但还是被发现了。罗马人表示只要投降就不死,约瑟夫想投降,可是其他人坚决不同意。怎么办呢,他想到一个主意:
让41个人围成一个圆圈,从第一个人开始报数,数到3的那个人被旁边的人杀死。这样就可以避免自杀了,因为犹太人的信仰是禁止自杀的。结果一群人杀来杀去最后只剩下2个了,就是约瑟夫和他朋友,于是两人愉快地去投降了。
例如:
1 4 5 6 7 8 9 //找出胜利的数字
1开始数数,数到3的人自杀:
第一轮: 1 4 6 7 8 9 (5被杀,从后面一位开始继续数数,即从6开始数)
第二轮: 1 4 6 7 9 (8被杀,又从后面一位开始数,即从9开始数)
第三轮: 1 6 7 9 (4被杀,又从后面一位开始数,即从6开始数)
第四轮: 1 6 7 (9被杀,又从后面一位开始数,即从1开始数)
第五轮: 1 6 (7被杀,又从后面一位开始数,即从1开始数)
第六轮: 6 (1被杀,约瑟环结束,剩下6没被杀)
#include "stdio.h"
#include <stdlib.h>
//节点的信息
typedef struct Node
{
int data;
struct Node *prior;
struct Node *next;
}Node;
//获取链表中的个节点数
int list_GetNum(Node *List)
{
int Num = 0;
Node *temp = List;
while(temp->next != List)
{
Num++;
temp = temp->next;
}
return Num;
}
//创建双向循环链表
Node* list_Init()
{
Node *P = (Node *)malloc(sizeof(Node));
if(P == NULL)
{
printf("开辟失败\n");
}
P->next = P;
P->prior = P;
return P;
}
//双向循环链表(头插)
void list_Insert_Head(Node *List, int data)
{
Node *new = (Node *)malloc(sizeof(Node));
new->data = data;
Node *temp = List;
if(temp->next == List && temp->prior == List)//链表为空链表
{
List->next = new;
List->prior = new;
new->next = List;
new->prior = List;
return;
}
new->next = List->next;
new->prior = List;
List->next->prior = new;
List->next = new;
}
//功能函数
void list_Func(Node *List, int KillNum)
{
Node *temp = List;
Node *tra = List;//存放需要删除的节点,用于释放
Node *traNext = List;//存放被删除节点的下一个节点,第二次从这个结点开始
int Num = 0;
int index = 0;
if(temp->next == List || temp->prior == List)
{
printf("链表为空\n");
return;
}
//获取链表中的节点数
while(temp->next != List)
{
Num++;
temp = temp->next;
}
temp = List;
while(list_GetNum(List) != 1)
{
temp = temp->next;
index++;
if(temp == List)//不算头节点的
{
index --;
}
if(index == KillNum)
{
tra = temp;
traNext = temp->next;//第二次开始的节点
temp->next->prior = temp->prior;
temp->prior->next = temp->next;
free(tra);
index = 0;
temp = traNext;//从下个结点开始
index = 1;//因为本次已经加了
if(traNext == List)//防止被删除的下一个节点是头节点
{
index = 0;
}
}
}
}
//遍历双向循环链表
void list_Show(Node *List)
{
Node *tra = List;
if(List->next == List && List->prior == List)
{
printf("链表为空\n");
return;
}
while(tra->next != List)
{
tra = tra->next;
printf("Data = %d\n", tra->data);
}
printf("*************\n");
}
int main(void)
{
//头指针指向头节点
Node *L = list_Init();
list_Show(L);
//头插
list_Insert_Head(L, 9);
list_Insert_Head(L, 8);
list_Insert_Head(L, 7);
list_Insert_Head(L, 6);
list_Insert_Head(L, 5);
list_Insert_Head(L, 4);
list_Insert_Head(L, 1);
list_Show(L);
//找出最后存活的一个(步长为3)
list_Func(L, 3);
list_Show(L);
}