1. 线性表
顺序表 链表(单向链表 单向循环链表 双向链表 双向循环链表)
栈(顺序栈 链式栈) 队列(顺序队列 链式队列)
1)逻辑结构:线性结构
2)存储结构:顺序存储、链式存储
3)特点:
一对一,每个节点最多有一个前驱和一个后继
首尾节点比较特殊,首节点无前驱,尾节点无后继
1.1顺序表
特点:内存连续,数组
1)逻辑结构:线性结构
2)存储结构:顺序存储
3)操作:增删改查
相关操作:
#ifndef _SEQLIST_H_
#define _SEQLIST_H_
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#define N 5
typedef struct
{
int data[N];
int last;//last代表的是数组中最后一个有效元素的下标
}seqlist_t;
//1.创建一个空的顺序表
seqlist_t *CreateEpSeqlist();//返回的是申请空间的首地址
//2.向顺序表的指定位置插入数据
int InsertIntoSeqlist(seqlist_t *p, int post,int data);//post第几个位置,data插入的数据
//3.遍历顺序表sequence 顺序 list 表
void ShowSeqlist(seqlist_t *p);
//4.判断顺序表是否为满,满返回1 未满返回0
int IsFullSeqlist(seqlist_t *p);
//5.判断顺序表是否为空
int IsEpSeqlist(seqlist_t *p);
//6.删除顺序表中指定位置的数据post删除位置
int DeletePostSeqlist(seqlist_t *p, int post);
//7.清空顺序表
void ClearSeqList(seqlist_t *p);
//8.修改指定位置的数据
int ChangePostSeqList(seqlist_t *p,int post,int data);//post被修改的位置,data修改成的数据
//9.查找指定数据出现的位置
int SearchDataSeqList(seqlist_t *p,int data);//data代表被查找的数据
#endif
具体实现:
#include"seqlist.h"
//1.创建一个空的顺序表
seqlist_t *CreateEpSeqlist() //返回的是申请空间的首地址
{ //1.动态开辟一个结构体大小空间
seqlist_t * p=(seqlist_t *)malloc(sizeof(seqlist_t));
if(p == NULL)
{
perror("malloc error");
return NULL;
}
//对last初始化,表示当前数据表为空
p->last = -1;
return p;
}
//2.判断顺序表是否为满,满返回1 未满返回0
int IsFullSeqlist(seqlist_t *p){
return p->last+1 == N;
}
//3.向顺序表的指定位置插入数据
int InsertIntoSeqlist(seqlist_t *p,int post,int data)
{
//1.容错判断,如果不能插入则错误提示
if(post>p->last+1 || post<0 || IsFullSeqlist(p))
{
perror("InsertIntoSeqlist err\n");
return -1;
}
//向指定位置插入元素
for(int i=p->last;i>=post;i--)
p->data[i+1]=p->data[i];
p->data[post]=data;
//最后一个有效元素的下标+1
p->last++;
return 0;
}//post第几个位置,data插入的数据
//4.遍历顺序表sequence 顺序 list 表
void ShowSeqlist(seqlist_t *p)
{
for(int i=0;i<=p->last;i++)
{
printf("%d ",p->data[i]);
}
printf("\n");
}
//5.判断顺序表是否为空
int IsEpSeqlist(seqlist_t *p)
{
return p->last==-1;
}
//6.删除顺序表中指定位置的数据post删除位置
int DeletePostSeqlist(seqlist_t *p, int post)
{
if(post>p->last || IsEpSeqlist(p) || post<0)
{
perror("delete error");
return -1;
}
for(int i=post;i<p->last;i++)
{
p->data[i]=p->data[i+1];
}
//有效数据元素-1
p->last--;
return 0;
}
//7.清空顺序表
void ClearSeqList(seqlist_t *p)
{
if( IsEpSeqlist(p))
{
perror("delete error");
}
p->last=-1;
// for(int i=0;i<=p->last;i++)
// {
// bzero(&(p->data[i]),1);
// }
}
//8.修改指定位置的数据
int ChangePostSeqList(seqlist_t *p,int post,int data)
{
if( IsEpSeqlist(p) || post>p->last || post<0)
{
perror("change error");
return -1;
}//判断位置是否存在
p->data[post]=data;
return 0;
}
//post被修改的位置,data修改成的数据
//9.查找指定数据出现的位置
int SearchDataSeqList(seqlist_t *p,int data)
{
if( IsEpSeqlist(p))
{
perror("search error");
return -1;
}//判断列表是否为空
int t=-1;
for(int i=0;i<=p->last;i++)
{
if(p->data[i]==data) t=i;
}
return t;
}//data代表被查找的数据,只能查找数据的最后出现位置,且未查到返回-1
顺序表总结:
(1)顺序表在内存当中是连续存储的
(2)顺序表的长度是固定 #define N 5
(3)顺序表查找数据的时候方便的,插入和删除麻烦
1.2链表
单向链表 单向循环链表 双向 双向循环
解决了:长度固定,插入删除麻烦的问题
1)逻辑结构:线性结构
2)存储结构:链式存储结构
3)操作:增删改查
1.2.1单向链表
**相关操作**
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;//数据域
struct node_t *next;//指针域,指向自身结构体的指针
}link_node_t,*link_list_t;
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList();
//2.向单向链表的指定位置插入数据
//p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p,int post, datatype data);
//3.遍历单向链表
void ShowLinkList(link_node_t *p);
//4.求单向链表长度的函数
int LengthLinkList(link_node_t *p);
//5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post);
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p);
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data);
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data);
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data);
//10.转置链表
void ReverseLinkList(link_node_t *p);
//11.清空单向链表
void ClearLinkList(link_node_t *p);
#endif
具体实现
#include "linklist.h"
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList()
{
link_node_t *first=(link_node_t *)malloc(sizeof(link_node_t));
if(first==NULL)
{
perror("malloc err\n");
return NULL;
}//开辟失败,错误返回
first->next=NULL;
first->data='\0';
return first;
}
//2.求单向链表长度的函数
int LengthLinkList(link_node_t *p)
{
int len=0;
while(p->next!=NULL)
{
p=p->next;
len++;
}
return len;
}
//3.向单向链表的指定位置插入数据
//p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p,int post, datatype data)
{
if(post<0 || post>LengthLinkList(p))
{
perror("Insert err");
return -1;
}
for(int i=0;i<post;i++)
{
p=p->next;
}
link_list_t pnew=(link_list_t)malloc(sizeof(link_node_t));
if(pnew==NULL)
{
perror("pnew err");
return -1;
}//每次新开辟空间都要判断是否生成
pnew->next=p->next;
pnew->data=data;
p->next=pnew;
return 0;
//新节点定义成指针类型,开辟新空间定义成全局变量存储防止函数内被释放
}
//4.判断单向链表是否为空 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("delete error");
return -1;
}
//头指针指向删除位置
for(int i=0;i<post;i++)
p=p->next;
//定义一个pdel指向被删除节点
link_list_t pdel=p->next;
//跨过被删除节点
p->next=pdel->next;
//释放pdel
free(pdel);
pdel=NULL;
return 0;
}
//6.遍历单向链表
void ShowLinkList(link_node_t *p)
{
while(p->next!=NULL)
{
p=p->next;
printf("%d ",p->data);
}
printf("\n");
}
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data)
{ //容错判断
if(IsEpLinkList(p) || post<0 || post>=LengthLinkList(p))
{
perror("Change error");
return -1;
}
while(post--)p=p->next;
p->next->data=data;
return 0;
}
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data)
{
if(IsEpLinkList(p))
{
perror("Search error");
return -1;
}
int i=-1,t=-1;
while(p->next!=NULL)
{
p=p->next;i++;
if(p->data==data)
{ printf("%d ",i);t++;}
}
if(t==-1)
printf("Not Search");
return 0;
}
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data)
{
link_list_t pdel;
if(IsEpLinkList(p))
{
perror("Delete error");
return -1;
}
while(p->next!=NULL)
{
if(p->next->data==data)
{
pdel=p->next;//指向被删除节点
p->next=pdel->next;//跨过被删除节点
free(pdel);//释放被删除节点
pdel=NULL;
}
else
p=p->next;
}
return 0;
}
//10.转置链表
void ReverseLinkList(link_node_t *p)
{
// if(IsEpLinkList(p))
// {
// perror("Reverse error");
// }
link_list_t q=p;
link_list_t t;
q=q->next;
p->next=NULL;//将头节点摘下来注意q=q->next 要定义在p之前,否则q也指向null
while(q!=NULL)
{
t=q;//每次循环让t到q的位置,即新节点位置
q=q->next;//q再向后走一个,到下一个新节点
t->next=p->next;//新节点指向上一掉落节点
p->next=t;//头节点指向当前掉落节点
}
}
//11.清空单向链表(只释放数据部分,头指针不释放)
void ClearLinkList(link_node_t *p)
{
link_list_t pdel=NULL;
while(p->next!=NULL)
{
pdel=p->next;//指向被删除节点
p->next=pdel->next;//p跨过被删除节点
free(pdel);//释放被删除节点
pdel=NULL;
}
}
单向循环链表
实现约瑟夫环问题
总结:
顺序表和链表的区别?
(1)顺序表在内存当中连续存储的(数组),但是链表在内存当中是不连续存储的,通过指针将数据链接在一起
(2)顺序表的长度是固定的,但是链表长度不固定
(3) 顺序表查找方便,但是插入和删除麻烦,链表,插入和删除方便,查找麻烦
1.2.1双向链表
#ifndef _DOUBLELIST_H_
#define _DOUBLELIST_H_
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;//数据域
struct node_t *next;//指向下一个节点的指针 next 下一个
struct node_t *prior;//指向前一个节点的指针 prior 前一个
}link_node_t,*link_list_t;
//将双向链表的头指针和尾指针封装到一个结构体里
//思想上有点像学的链式队列
typedef struct doublelinklist
{
link_list_t head; //指向双向链表的头指针
link_list_t tail; //指向双向链表的尾指针
int len;
}double_node_t,*double_list_t;
//1.创建一个空的双向链表
double_list_t createEmptyDoubleLinkList();
//2.向双向链表的指定位置插入数据 post位置, data数据
int insertIntoDoubleLinkList(double_list_t p, int post, datatype data);
//3.遍历双向链表
void showDoubleLinkList(double_list_t p);
//4.删除双向链表指定位置的数据
int deletePostDoubleLinkList(double_list_t p, int post);
//5.判断双向链表是否为空
int isEmptyDoubleLinkList(double_list_t p);
//6.求双向链表的长度
int lengthDoubleLinkList(double_list_t p);
//7.查找指定数据出现的位置 data被查找的数据
int searchPostDoubleLinkList(double_list_t p,datatype data);
//8.修改指定位置的数据,post修改的位置 data被修改的数据
int changeDataDoubleLinkList(double_list_t p,int post, datatype data);
//9.删除双向链表中的指定数据 data代表删除所有出现的data数据
int deleteDataDoubleLinkList(double_list_t p, datatype data);
#endif
具体实现
#include "doublelist.h"
//1.创建一个空的双向链表
double_list_t createEmptyDoubleLinkList()
{
double_list_t p=(double_list_t)malloc(sizeof(double_node_t));
if(p==NULL)
{
perror("p err");
return NULL;
}
p->tail=p->head=(link_list_t)malloc(sizeof(link_node_t));
if(p->head==NULL)
{
perror("tail err");
return NULL;
}
p->len=0;
p->head->next=p->head->prior=NULL;
return p;
}
//2.向双向链表的指定位置插入数据 post位置, data数据
int insertIntoDoubleLinkList(double_list_t p, int post, datatype data)
{
//1.容错判断
if(post<0 ||post>p->len)
{
perror("local error");
return -1;
}
//2.开辟新节点
link_list_t pnew=(link_list_t)malloc(sizeof(link_node_t));
if(pnew==NULL)
{
perror("pnew err");
return -1;
}
//初始化
pnew->data=data;
//插入位置判断
if(post==p->len)
{//尾插
p->tail->next=pnew;
pnew->prior=p->tail;
pnew->next=NULL;
p->tail=pnew;
}
else//中间插入
{
link_list_t temp;
//判断插入位置在前在后
if(post<p->len/2)
{
temp=p->head;
for(int i=0;i<=post;i++)
{
temp=temp->next;
}
}
else
{
temp=p->tail;
for(int i=0;i<p->len-post-1;i++)
{
temp=temp->prior;
}
}
//3.插入新节点
temp->prior->next=pnew;
pnew->prior=temp->prior;
pnew->next=temp;
temp->prior=pnew;
}
//长度+1
p->len++;
return 0;
}
//3.遍历双向链表
void showDoubleLinkList(double_list_t p)
{
printf("顺序遍历:\n");
link_list_t q=p->head;
for(int i=0;i<p->len;i++)
{
q=q->next;
printf("%d ",q->data);
}
printf("\n");
printf("逆序遍历:\n");
q=p->tail;
for(int i=0;i<p->len;i++)
{
printf("%d ",q->data);
q=q->prior;
}
printf("\n");
}
//4.判断双向链表是否为空
int isEmptyDoubleLinkList(double_list_t p)
{
return p->head == p->tail;//p->len==0
}
//5.删除双向链表指定位置的数据
int deletePostDoubleLinkList(double_list_t p, int post)
{
if(isEmptyDoubleLinkList(p) || post<0 || post >= p->len)
{
perror("del error");
return -1;
}
link_list_t pdel;
if(post == p->len-1)
{
pdel=p->tail;
p->tail=pdel->prior;
p->tail->next=NULL;
free(pdel);
pdel=NULL;
}
else
{
if(post<p->len/2)
{
pdel=p->head;
for(int i=0;i<=post;i++)
{
pdel=pdel->next;
}
}
else
{
pdel=p->tail;
for(int i=0;i<p->len-post-1;i++)
{
pdel=pdel->prior;
}
}
pdel->prior->next=pdel->next;
pdel->next->prior=pdel->prior;
free(pdel);
pdel=NULL;
}
p->len--;
return 0;
}
//6.求双向链表的长度
int lengthDoubleLinkList(double_list_t p)
{
return p->len;
}
//7.查找指定数据出现的位置 data被查找的数据
int searchPostDoubleLinkList(double_list_t p,datatype data)
{
if(isEmptyDoubleLinkList(p))
{
perror("search list empty");
return -1;
}
link_list_t temp=p->head;
int i=-1;
int flag=0;
printf("%d位置在:",data);
while(temp->next!=NULL)
{
temp=temp->next;
i++;
if(temp->data == data)
{
printf("%d ",i);flag++;
}
}
if(flag==0)
printf("no data");
printf("\n");
return 0;
}
//8.修改指定位置的数据,post修改的位置 data被修改的数据
int changeDataDoubleLinkList(double_list_t p,int post, datatype data)
{
if(isEmptyDoubleLinkList(p))
{
perror("change list empty");
return -1;
}
link_list_t temp;
if(post<p->len/2)
{
temp=p->head;
for(int i=0;i<=post;i++)
temp=temp->next;
}
else
{
temp=p->tail;
for(int i=0;i<p->len-post-1;i++)
temp=temp->prior;
}
temp->data=data;
return 0;
}
//9.删除双向链表中的指定数据 data代表删除所有出现的data数据
int deleteDataDoubleLinkList(double_list_t p, datatype data)
{
link_list_t pdel=p->head->next;
link_list_t temp=NULL;
while(pdel!=NULL)
{
//尾删
if(pdel->data == data)
{
if(pdel->next == NULL)
{
p->tail=p->tail->prior;
free(p->tail->next);
p->tail->next=NULL;
}
else //中间删
{
temp=pdel;
temp->prior->next=temp->next;
temp->next->prior=temp->prior;
pdel=temp->next;
free(temp);
temp=NULL;
}
p->len--;
}
else pdel=pdel->next;
}
return 0;
}