顺序表
逻辑结构:线性结构
存储结构:顺序存储结构 在内存当中存储是连续的 数组
思想:
数组的思想,比如像数组中插入一个数据,需要将插入位置后面的数据全部将后移动一位,让这个位置空开用来插入数据,要存储数据的时候是不是要获取他的下标所以要定义一个变量来存储下标。
顺序表:
逻辑结构:线性结构
存储结构:顺序存储结构 在内存当中存储是连续的 数组
顺序表操作:
结构体:
typedef struct
{
int data[N];
int last; //last代表的是数组中最后一个有效元素的下标
}seqlist_t;
1.创建空的顺序表
(1)首先的动态开辟一个空间
(2)判断空间是否开辟成功
(3)将下标初始化为-1
2.向指定位置插入数据
(1)首先进行容错判断,判断传入的指定位置是否 (<0 || >下标加一 || 判满)。
(2)循环遍历,找到指定位置,将指定位置的数据以及后面的数据向后移动一位。
(3)指定位置为空后,再将指定位置插入数据。
(4)将下标向后移动一位。
3.判断表是否满
判断下标加一是否等于N(刚开始定义的数组元素)。
4.指定位置删除数据
(1)首先进行容错判断,判断传入的指定位置是否 (<0 || >下标 || 判空)。
(2)循环遍历,找到指定位置,将指定位置后的数据以及后面的数据向前移动一位,覆盖指定位置。
(3)将下标向前移动一位。
5.判断表是否为空
判断下标是否为-1(注意:判满和判空都是利用了地址传递)
6.遍历顺序表
就是一个for循环遍历打印,终止为下标的最后一个数。
顺序表操作:
#include<stdio.h>
#include<stdlib.h>
#include "sqlist.h"
//创建一个空的顺序表
seqlist_t *CreateEpSeqlist()//返回的是申请空间的首地址
{
//1.动态申请一个结构体空间
seqlist_t *p=(seqlist_t *)malloc(sizeof(seqlist_t));
if(p == NULL)
{
perror("p malloc err");
return NULL;
}
//2.last初始化
p ->last = -1;//数组为空,没有有效元素
return p;
}
//判断顺序表是否为满,满返回1 未满返回0
int IsFullSeqlist(seqlist_t *p)
{
return p->last+1 == N;
}
//向顺序表的指定位置插入数据
int InsertIntoSeqlist(seqlist_t *p, int post,int data)
{
int i;
if(post < 0 || post > p->last+1 || IsFullSeqlist(p))
{
printf("insertIntoSeqlist failed!");
return -1;
}
for(i=p->last;i>=post;i--)
p->data[i+1]=p->data[i];
p->data[post]=data;
p->last++;
return 0;
}
//遍历顺序表sequence 顺序 list 表
void ShowSeqlist(seqlist_t *p)
{
for(int i=0;i<=p->last;i++)
printf("%d ",p->data[i]);
printf("\n");
}
//判断顺序表是否为空
int IsEpSeqlist(seqlist_t *p)
{
return p->last == -1;
}
//删除顺序表中指定位置的数据post删除位置
int DeletePostSeqlist(seqlist_t *p, int post)
{
if(post < 0 || post > p->last || IsEpSeqlist(p))
{
perror("deletePostSeqlist failed!\n");
return -1;
}
for(int i=post+1;i<=p->last;i++)
p->data[i-1]=p->data[i];
p->last--;
return 0;
}
//清空顺序表
void ClearSeqList(seqlist_t *p)
{
p->last = -1;
}
//修改指定位置的数据
int ChangePostSeqList(seqlist_t *p,int post,int data)
{
if(post < 0 || post > p->last)
{
perror("ChangePostSeqList err");
return -1;
}
p->data[post]=data;
return 0;
}
//查找指定数据出现的位置
int SearchDataSeqList(seqlist_t *p,int data)
{
for(int i=0;i<=p->last;i++)
if(p->data[i] == data)
return i;
return -1;
}
顺序表总结:
(1)顺序表在内存当中是连续存储的
(2)顺序表的长度是固定
(3)顺序表查找数据的时候方便,但插入和删除麻烦
单向链表
逻辑结构:线性结构
存储结构:链式存储结构 在内存当中存储是不连续的 解决了顺序表长度固定的问题,插入和删除麻烦的问题
思想:
定义节点来存放数据,如图的A,B,C,D都是一个节点,节点里的数据域来存放数据,指针域让所有的节点都链接起来这样就是一个存放数据的链表
1.创建空的链表
(1)首先的动态开辟结构体一个空间
(2)判断空间是否开辟成功
(3)将next域(指针域)初始化
2.向单项链表的指定位置插入数据插入
(1)首先进行容错判断,判断传入的指定位置是否 (<0 || >链表长度 不进行判满操作,因为链表长度不固定的)。
(2)定义一个新节点并开辟一个空间用来保存插入的数据,判断新节点是否开辟成功
(3)将新节点初始话将传入的数据给这个新节点的数据域,将新节点的指针域先置为NULL。
(4)将新节点插入指定位置,首先的定位到指定位置(一个for循环遍历到指定位置的前一个节点,让p=p->next域)。
(5)为了不让后面的数据丢失先连接尾部再连接头,也就是将新节点的指针域先指向下一个节点,再将指定位置的指针域去指向这个新节点。
3.链表遍历
用到了有头和无头单项链表的遍历
用一个while循环判断p->next域是否为空
有头是先指向后打印,无头是先打印后指向,但之前的将p->next赋给h(其实都一样)
4.链表长度
定义一个len,循环遍历p->next != NULL,len++.
5.删除单项链表中指定位置的数据
(1)首先进行容错判断,判断传入的指定位置是否 (<0 || >=链表长度 进行判空操作)。
(2)遍历到删除位置的前一个节点
(3)定义一个相同类型的指针用来接受当前位置地址,利用当前的next域去跨过被删除节点,再释放被删除节点。
6.链表判空
p->next != NULL
7.修改指定位置的数据
(1)首先进行容错判断,判断传入的指定位置是否 (<0 || >=链表长度 )。
(2)遍历到修改位置节点
(3)直接修改当前节点的数据域
8.查找指定数据出现的位置
定义一个i来存位置的下标
循环遍历,如果p->数据域等于参数data返回位置下标 i++;
9.删除单项链表中出现的指定数据
首先的定一个指针指向第一个节点的位置,循环遍历判断他的数据域是否与参数的data相同,若相同需要释放,但是释放的话链表后面的东西都没有了,所以的定义宁一个指针专门用来释放和删除指定位置数据一样跳过这个需要删除的节点接着指向下一个节点接着遍历。
10.转置链表(重点)
转置的意思就是和倒叙的意思差不多,因为他是一个有头的单项链表,所以要将
后面的节点一个一个放到头的后面,就实现了相当于倒叙的一个概念
首先的定义一个q指针去指向p的指针域,去接受后面的节点,将p->next先放空
要将后面的节点连接到头节点的尾部,首先要将第一个节点的后面的节点不丢失,就得新定义一个指针去指向后面的然后再将第一个节点放在头的后面
11.清空单项链表
就是将链表的每一个节点一个一个的删除所以得定义一个指针用来依次跨过每个节点并将其释放,当最后一个节点为NULL的时候结束。
链表的总结:解决了顺序表长度固定的问题,插入和删除麻烦的问题,但我觉得链表比顺序表稍微难一点
链表操作:
#include<stdio.h>
#include<stdlib.h>
#include "linklist.h"
//创建一个空的单项链表
link_node_t *CreateEpLinkList()
{
link_list_t p=(link_list_t)malloc(sizeof(link_node_t));
if(p==NULL)
{
perror("CreateEpLinkList err");
return NULL;
}
p ->next = NULL;
return p;
}
//链表长度
int LengthLinkList(link_node_t *p)
{
int length=0;
while(p->next != NULL )
{
p = p->next;
length++;
}
return length;
}
//插入
int InsertIntoPostLinkList(link_node_t *p,int post,datatype data)
{
//容错
if(post <0 || post > LengthLinkList(p))
{
perror("InsertIntoPostLinkList err");
return -1;
}
//创建新节点
link_list_t pnew = NULL;//指向新节点
//将头指针移动到插入位置的前一个节点
for(int i=0;i<post;i++)
p=p->next;
//创建新节点保存插入数据
pnew = (link_list_t)malloc(sizeof(link_node_t));
if(pnew == NULL)
{
perror("pnew malloc err");
return -1;
}
pnew ->data = data;
pnew ->next = NULL;
//将新节点插入指定位置
pnew ->next = p->next;
p->next = pnew;
return 0;
}
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p)
{
return p->next == NULL;
}
//5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post)
{
if(post <0 || post >= LengthLinkList(p) || IsEpLinkList(p))
{
perror("DeletePostLinkList err");
return -1;
}
//遍历到删除位置的前一个节点
for(int i=0;i<post;i++)
p=p->next;
//开辟一个节点用来接受要删除的后面的那一个的地址
link_list_t pdel = NULL;
//指向被删除的节点
pdel=p->next;
//跨过被删除的节点
p->next=pdel->next;
free(pdel);
pdel=NULL;
return 0;
}
//3.遍历单向链表
void ShowLinkList(link_node_t *p)
{
while(p->next != NULL)
{
p = p->next;
printf("%d\t",p->data);
}
printf("\n");
}
//11.清空单向链表
void ClearLinkList(link_node_t *p)
{
link_list_t pdel = NULL;
while(p->next != NULL)
{
pdel = p->next;
p->next = pdel->next;
free(pdel);
pdel=NULL;
}
}
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data)
{
//容错判断
if(post < 0 || post >= LengthLinkList(p))
{
perror("ChangePostLinkList err");
return -1;
}
//将头指针遍历到修改的节点位置
for(int i=0;i<=post;i++)
p=p->next;
//修改数据
p->data = data;
return 0;
}
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data)
{
int i=0;
while(p->next != NULL)
{
p = p->next;
if(p->data == data)
return i;
i++;
}
return -1;
}
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data)
{
//定义指针q(用来遍历判断)和指针pdel(用来删除链接后面)
link_list_t pdel = NULL;
link_list_t q = p->next;
while(q != NULL)
{
if(q ->data == data)
{
pdel=q;
p->next=pdel->next;
free(pdel);
pdel=NULL;
q=p->next;
}
else
{
q=q->next;
p=p->next;
}
}
return 0;
}
void ReverseLinkList(link_node_t *p)
{
link_list_t temp = NULL;
//断开前,保存头节点的下一个节点的地址
link_list_t q = p->next;
//断开链接
p->next = NULL;
//遍历无头链表
while(q != NULL )
{
//提前保存q的下一个节点
temp = q->next;
//先连后面在连前面
q->next = p->next;
p->next = q;
//将q移动至无头链表的下一个节点
q = temp;
}
}