什么是数据结构
数据结构是用来处理数据和数据之间的逻辑关系,实现需要的功能的操作
1.数据结构主要研究数据之间的特定关系:
- 逻辑关系
- 存储关系
- 操作(数据运算):增删改查
2.1 逻辑关系
- 逻辑关系主要是指数据之间在逻辑上的关系,与存储位置无关,主要是指邻接关系,从属关系。
1)线性关系
- 一对一的关系,任何一个数据最多只能有一个直接前驱和一个直接后继。
- 例如:排队买东西。对应的数据结构:线性表,栈,队列
2)树形关系
- 一对多的关系,任何一个数据只能有一个直接前驱,但是可以有多个后继。
- 例如:族谱,公司组织架构图。对应数据结构:树,二叉树
3)图形关系
- 多对多的关系
- 例如:地铁图
2.2 存储关系
数据在内存中是如何存储的,实际位置的关系
1)顺序存储
- 数据在内存中会开辟一块连续的空间进行存储。一般使用数组来存储数据。(物理位置是相邻的)
2)链式存储
- 数据在内存中不需要开辟连续的空间存储。(物理位置不相邻)
本文重点
本文主要是用于自己复习,所以会对自己不清楚的地方进行一个梳理。
1、单链表
cs
//单链表
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
//结点的创建
struct list
{
char name[20];
int age;//数据域
int id;
struct list *next;//指针域
};
//单链表的创建--头结点创建
struct list * list_creat()
{
struct list *P=(struct list *)malloc(sizeof(struct list));
if(P==NULL)
{
printf("创建头结点失败\n");
return NULL;
}
memset(P->name,0,sizeof(P->name));
P->age=0;
P->id=0;
P->next=NULL;
return P;
}
//数据插入------头插法
bool list_head_insert(struct list *P,char name[],int age,int id )
{
if(P==NULL)
{
struct list*node=(struct list *)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
node=P->next;//如果没有节点,则创建一个节点,地址的指向更改指向新节点
strcpy(node->name,name);
node->age=age;
node->id=id;
node->next=NULL;
return true;
}
else
{
struct list *node=(struct list *)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
struct list *temp=P->next;
strcpy(node->name,name);
node->age=age;
node->id=id;
node->next=temp;
P->next=node;
return true;
}
}
//尾插法
bool list_tail_insert(struct list *P,char name[],int age,int id)
{
if(P==NULL)
{
struct list *node=(struct list *)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
strcpy(node->name,name);
node->age=age;
node->id=id;
P->next=node;
node->next=NULL;
return true;
}
else
{
int num;
struct list *node=(struct list*)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
while(P->next!=NULL)
{
P=P->next;
}
struct list *temp=P;
strcpy(node->name,name);
node->age=age;
node->id=id;
temp->next=node;
node->next=NULL;
return true;
}
}
//指定位置插入
bool list_len_insert(struct list *P,char name[],int age,int id,int len)
{
int i=0;
struct list *head=P;//防止头指针变更,创建新指针来代替头指针
while(head->next!=NULL)
{
head=head->next;
i++;
}
if(len<1 || i<len)
{
printf("输入长度错误\n");
return false;
}
else
{
struct list *node=(struct list*)malloc(sizeof(struct list));
strcpy(node->name,name);
node->age=age;
node->id=id;
node->next=NULL;
int k=0;
struct list *temp=P;
while(temp->next!=NULL)
{
k++;
if(k==len)
{
struct list *Pin=temp->next;
temp->next=node;
node->next=Pin;
break;
}
temp=temp->next;
}
return true;
}
}
//头部删除
bool list_head_del(struct list*P)
{
if(P==NULL)
{
printf("链表为空,删除失败\n");
return false;
}
struct list *temp=P;
struct list *node=P->next;
temp->next=node->next;
node->next=NULL;
free(node);
return true;
}
//尾部删除
bool list_tail_del(struct list *P)
{
if(P==NULL)
{
printf("链表为空,删除失败\n");
return false;
}
struct list *temp=P;
struct list *P_tail=P->next;
while(P_tail->next!=NULL)
{
temp=temp->next;
P_tail=P_tail->next;
}
temp->next=NULL;
//P_tail->next=NULL;
free(P_tail);
return true;
}
//指定位置删除
bool list_len_del(struct list *P,int len)
{
if(P==NULL)
{
printf("链表为空,删除失败\n");
return false;
}
int k=0;
struct list * node=P;
while(node->next!=NULL)
{
k++;
node=node->next;
}
if(len<1 || len>k)
{
printf("输入长度错误\n");
return false;
}
int num=0;
struct list *P_first=P;
struct list *P_two=P->next;
while(P_two->next!=NULL)
{
num++;
if(num==len)
{
struct list *P_node=P_two->next;
P_first->next=P_node;
P_two->next=NULL;
free(P_two);
return true;
}
P_first=P_first->next;
P_two=P_two->next;
}
return false;
}
//编历链表
bool list_erg(struct list *P)
{
if(P==NULL)
{
printf("链表为空,遍历失败\n");
return false;
}
while(P->next!=NULL)
{
P=P->next;
printf("name=%s,age=%d,id=%d\n",P->name,P->age,P->id);
}
return true;
}
int main(int argc, const char *argv[])
{
//输入数据进链表------头插法输入
struct list*P=list_creat();
list_head_insert(P,"zhangsan",20,1);
list_head_insert(P,"lisi",21,2);
list_head_insert(P,"wangwu",22,3);
list_head_insert(P,"liuer",24,4);
list_erg(P);//遍历链表
printf("``````````````````````````、n\n");
list_tail_insert(P,"E",29,9);//尾插法输入
list_tail_insert(P,"D",28,8);
list_tail_insert(P,"C",27,7);
list_tail_insert(P,"B",26,6);
list_tail_insert(P,"A",25,5);
list_erg(P);
printf("```````````````````````\n");
list_len_insert(P,"lp",21,10,3);//指定位置输入
list_erg(P);
printf("`````````````````````\n");
//删除头部
list_head_del(P);
list_erg(P);//
//删除尾部
printf("`````````````````````\n");
list_tail_del(P);
list_erg(P);
//指定位置删除
printf("`````````````````````\n");
list_len_del(P,2);
list_erg(P);
return 0;
}
输出如下
cs
name=liuer,age=24,id=4
name=wangwu,age=22,id=3
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
``````````````````````````、n
name=liuer,age=24,id=4
name=wangwu,age=22,id=3
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
name=A,age=25,id=5
```````````````````````
name=liuer,age=24,id=4
name=wangwu,age=22,id=3
name=lp,age=21,id=10
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
name=A,age=25,id=5
`````````````````````
name=wangwu,age=22,id=3
name=lp,age=21,id=10
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
name=A,age=25,id=5
`````````````````````
name=wangwu,age=22,id=3
name=lp,age=21,id=10
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
`````````````````````
name=wangwu,age=22,id=3
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
以上就只是单链表的操作,包括数据的插入(头插法,尾插法,指定位置插入),删除(头部删除,尾部删除,指定位置删除)
2、循环单链表
cs
//循环单链表
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
//结点的创建
struct list
{
char name[20];
int age;//数据域
int id;
struct list *next;//指针域
};
//循环单链表的创建--头结点创建
struct list * list_creat()
{
struct list *P=(struct list *)malloc(sizeof(struct list));
if(P==NULL)
{
printf("创建头结点失败\n");
return NULL;
}
memset(P->name,0,sizeof(P->name));
P->age=0;
P->id=0;
P->next=P;
return P;
}
//数据插入------头插法
bool list_head_insert(struct list *P,char name[],int age,int id )
{
if(P->next==P)
{
//struct list *Pin=P;
struct list*node=(struct list *)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
//对新建结点初始化
memset(node->name,0,sizeof(node->name));
node->age=0;
node->id=0;
node->next=NULL;
//对新建结点赋值
strcpy(node->name,name);
node->age=age;
node->id=id;
//连接头结点和新建结点
P->next=node;
node->next=P;
return true;
}
else
{
struct list *node=(struct list *)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
memset(node->name,0,sizeof(node->name));
node->age=0;
node->id=0;
node->next=NULL;
struct list *temp=P->next;
strcpy(node->name,name);
node->age=age;
node->id=id;
P->next=node;
node->next=temp;
return true;
}
}
//尾插法
bool list_tail_insert(struct list *P,char name[],int age,int id)
{
if(P->next==P)
{
struct list *node=(struct list *)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
node->next=NULL;
strcpy(node->name,name);
node->age=age;
node->id=id;
P->next=node;
node->next=P;
return true;
}
else
{
struct list *node=(struct list*)malloc(sizeof(struct list));
if(node==NULL)
{
printf("结点创建失败\n");
return false;
}
memset(node->name,0,sizeof(node->name));
node->age=0;
node->id=0;
node->next=NULL;
struct list *Pin=P;//定义新指针,避免直接操作头指针
while(Pin->next!=P)
{
Pin=Pin->next;
}
struct list *temp=Pin;
strcpy(node->name,name);
node->age=age;
node->id=id;
temp->next=node;
node->next=P;//最后一个结点指向头结点
return true;
}
}
//指定位置插入
bool list_len_insert(struct list *P,char name[],int age,int id,int len)
{
int i=0;
struct list *head=P;//防止头指针变更,创建新指针来代替头指针
while(head->next!=P)
{
head=head->next;
i++;
}
if(len<1 || i<len)
{
printf("输入长度错误\n");
return false;
}
else
{
struct list *node=(struct list*)malloc(sizeof(struct list));
strcpy(node->name,name);
node->age=age;
node->id=id;
node->next=NULL;
int k=0;
struct list *temp=P;
while(temp->next!=P)
{
k++;
if(k==len)
{
struct list *Pin=temp->next;
temp->next=node;
node->next=Pin;
break;
}
temp=temp->next;//从头结点的下一个结点开始遍历
}
return true;
}
}
//头部删除
bool list_head_del(struct list*P)
{
if(P->next==P)
{
printf("链表为空,删除失败\n");
return false;
}
struct list *temp=P;
struct list *node=P->next;
temp->next=node->next;
node->next=NULL;
free(node);
return true;
}
//尾部删除
bool list_tail_del(struct list *P)
{
if(P->next==P)
{
printf("链表为空,删除失败\n");
return false;
}
struct list *temp=P;
struct list *P_tail=P->next;
while(P_tail->next!=P)
{
temp=temp->next;
P_tail=P_tail->next;
}
temp->next=P;//让倒数第二个结点指向头结点
P_tail->next=NULL;//最后一个结点指向空指针,防止野指针
free(P_tail);
return true;
}
//指定位置删除
bool list_len_del(struct list *P,int len)
{
if(P->next==P)
{
printf("链表为空,删除失败\n");
return false;
}
int k=0;
struct list * node=P;
while(node->next!=P)
{
k++;
node=node->next;
}
if(len<1 || len>k)
{
printf("输入长度错误\n");
return false;
}
int num=0;
struct list *P_first=P;
struct list *P_two=P->next;
while(P_two->next!=P)
{
num++;
if(num==len)
{
struct list *P_node=P_two->next;
P_first->next=P_node;//让要删除的上一个结点指针指向要删除的下一个结点,即把要删除的指针拿出来
P_two->next=NULL;//让要删除的指针指向空指针,防止野指针
free(P_two);
return true;
}
P_first=P_first->next;
P_two=P_two->next;
}
return false;
}
//编历链表
bool list_erg(struct list *P)
{
if(P->next==P)
{
printf("链表为空,遍历失败\n");
return false;
}
struct list *temp=P;
while(temp->next!=P)
{
temp=temp->next;
printf("name=%s,age=%d,id=%d \n",temp->name,temp->age,temp->id);
}
return true;
}
int main(int argc, const char *argv[])
{
//输入数据到循环单链表中------头插法
struct list*P=list_creat();
list_head_insert(P,"zhangsan",20,1);
list_head_insert(P,"lisi",21,2);
list_head_insert(P,"wangwu",22,3);
list_head_insert(P,"liuer",24,4);
list_erg(P);
printf("``````````````````````````、n\n");
//输入数据到循环单链表中------尾插法
list_tail_insert(P,"E",29,9);
list_tail_insert(P,"D",28,8);
list_tail_insert(P,"C",27,7);
list_tail_insert(P,"B",26,6);
list_tail_insert(P,"A",25,5);
list_erg(P);
printf("```````````````````````\n");
//输入数据到循环单链表中------指定位置插入
list_len_insert(P,"lp",21,10,3);
list_erg(P);
printf("`````````````````````\n");
//删除头部
list_head_del(P);
list_erg(P);//
//删除尾部
printf("`````````````````````\n");
list_tail_del(P);
list_erg(P);
//指定位置删除
printf("`````````````````````\n");
list_len_del(P,2);
list_erg(P);
return 0;
}
输出
cs
//头插法插入数据
name=liuer,age=24,id=4
name=wangwu,age=22,id=3
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
``````````````````````````、n
//尾插法插入数据
name=liuer,age=24,id=4
name=wangwu,age=22,id=3
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
name=A,age=25,id=5
```````````````````````
//指定位置插入输入
name=liuer,age=24,id=4
name=wangwu,age=22,id=3
name=lp,age=21,id=10
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
name=A,age=25,id=5
`````````````````````
//头部删除法
name=wangwu,age=22,id=3
name=lp,age=21,id=10
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
name=A,age=25,id=5
`````````````````````
//尾部删除法
name=wangwu,age=22,id=3
name=lp,age=21,id=10
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
`````````````````````
//指定位置删除法
name=wangwu,age=22,id=3
name=lisi,age=21,id=2
name=zhangsan,age=20,id=1
name=E,age=29,id=9
name=D,age=28,id=8
name=C,age=27,id=7
name=B,age=26,id=6
3、双向链表和循环双向链表
需要读者自己去自己操作,这里我感觉浪费时间,直接进入栈和队列
4、栈
1、这里的栈不是我们说的堆区和栈区的栈,而是逻辑结构上像弹夹一样固定一端的顺序表,但是再存储方式上分为顺序栈和链栈,数据遵守先进后出原则。
1、顺序栈
- 用顺序表实现的栈。
- 所以栈的创建,入栈出栈的代码与操作顺序表一致,只不过要求只能在一端进行操作。
- 笔试题较多的是:要求写出满栈,空栈的条件。看清楚题目,pos是从0开始还是从-1开始。
cs
//顺序栈
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#define MAX 10
struct stack
{
int buf[MAX];
int top;//栈顶指针,但不是指针,指向栈顶元素
};
//创建栈
struct stack *stack_creat()
{
struct stack *P=(struct stack*)malloc(sizeof(struct stack));
if(P==NULL)
{
printf("创建失败\n");
return NULL;
}
//创建栈之后要进行数据初始化
memset(P->buf,0,sizeof(P->buf));
P->top=-1;
return P;
}
//栈是否为空
bool stack_empty(struct stack*P)
{
if(P->top==-1)
{
return true;
}
return false;
}
//栈是否为满
bool stack_full(struct stack *P)
{
if(P->top==MAX-1)
{
return true;
}
return false;
}
//入栈
bool stack_insert(struct stack*P,int value)
{
if(stack_full(P))
{
printf("栈已满,入栈失败\n");
return false;
}
P->top++;
P->buf[P->top]=value;
return true;
}
//出栈
bool stack_out(struct stack*P,int *value)//用一个地址来存储出栈的数据
{
if(stack_empty(P))
{
printf("栈为空,出栈失败\n");
return false;
}
*value=P->buf[P->top];
P->buf[P->top]=0;//出栈之后,对元素赋值为空
P->top--;//将栈顶指针后移
return true;
}
//访问栈顶元素
bool stack_top(struct stack *P,int *value)
{
if(stack_empty(P))
{
printf("栈为空,访问失败\n");
return false;
}
*value=P->buf[P->top];
return true;
}
//计算栈的长度
int stack_len(struct stack *P)
{
if(stack_empty(P))
{
printf("栈为空\n");
return 0;
}
int len=0;
int num=0;
int value;
while(P->top!=-1)
{
stack_out(P,&value);
len++;
}
return len;
}
int main(int argc, const char *argv[])
{
int value,len,top;
struct stack *P=stack_creat();
stack_insert(P,12);
stack_insert(P,13);
stack_insert(P,14);
stack_insert(P,15);
stack_out(P,&value);
stack_top(P,&top);
len=stack_len(P);
printf("len=%d,value=%d,top=%d\n",len,value,top);
return 0;
}
2.链栈
cs
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
struct Node
{
int data;
struct Node *next;
int top;
};
//另外一种方式
/*
struct Top
{
struct Node *top;
};
*/
//创建空栈
struct Node *Stack_Create()
{
struct Node *L=(struct Node *)malloc(sizeof(struct Node));
if(L==NULL)
{
printf("链栈创建失败!\r\n");
return NULL;
}
L->data=0;
L->next=NULL;
L->top=-1;
return L;
}
//栈判空
bool is_empty(struct Node *L)
{
if(L->top==-1)
{
return true;
}
return false;
}
//入栈:尾插法,栈只能再一端操作,如果使用尾插法插入,那么出战也只能在尾部出
bool push(struct Node *L,int data)
{
if(L->next==NULL)
{
struct Node *node=(struct Node *)malloc(sizeof(struct Node));
if(node==NULL)
{
return false;
}
node->data=data;
node->next=NULL;
L->next=node;
L->top++;//栈顶指针加1
return true;
}
struct Node *node=(struct Node *)malloc(sizeof(struct Node));
if(node==NULL)
{
return false;
}
node->data=data;//对创建的栈节点赋初值
node->next=NULL;
struct Node *tra=L;
while(tra->next!=NULL)//遍历找到尾部
{
tra=tra->next;
}
tra->next=node;//将上面创建的节点和尾节点链接
L->top++;
return true;
}
//出栈
bool pop(struct Node *L,int *data)
{
if(is_empty(L))
{
return false;
}
//第一种方式可以通过top,之后遍历链表找到尾结点
//第二种方式可以通过两个个指针,遍历完之后,一个指向尾结点的前一个结点,一个指向尾结点
struct Node *tra=L;//采用第二种方法
struct Node *temp=L->next;
while(temp->next!=NULL)
{
tra=tra->next;
temp=temp->next;
}
tra->next=NULL;
*data=temp->data;
free(temp);//释放栈顶元素
return true;
}
int main()
{
struct Node *stack=Stack_Create();
push(stack,10);
push(stack,20);
int value;
pop(stack,&value);
printf("出栈的数据:%d\r\n",value);
return 0;
}
今天有些累了,明天继续