数据结构
有一种或多种特定关系的数据元素的集合
DS=(D,R);
D:数据集合
R:关系集合
数据:
数据:信息的载体
数据元素:数据的组成单位,一般作为一个整体考虑
数据项:数据元素的组成单位
数据对象:用特定结构体定义的数组
数据类型:对数据元素取值范围与运算的限定
struct stu {
int age;
char name[32];
};
struct stu zc;//数据元素
zc.age=18;
strcpy(zc.name,“zhancgheng”);//数据项
struct stu zc【32】;//数据对象
关系:
逻辑结构:
线性:一对一的关系,除了第一个数据元素没有唯一前驱,最后一个数据元素没有唯一后继,其余的数
据元素都有唯一前驱和唯一后继
表、栈、队列
非线性
树:一对多的关系,除了第一个数据元素没有唯一前驱,其余的数据元素都有唯一前驱并且可以有多个
后继
图:多对多的关系
存储结构:
顺序存储:类似于数组,把逻辑上相邻的数据元素在物理地址上存储也相邻
链式存储:把逻辑上相邻的数据元素在物理地址上可以不相邻
数据域+指针域
数据域:存放数据
指针域:存放数据元素的下一个数据元素的地址
索引存储:在存储数据的同时,建立一个附加的索引表,即索引存储结构=数据文件+索引表。
散列存储:根据数据元素的特殊字段(称为关键字key),计算数据元素的存放地址,然后数据元素按地址
存放,所得到的存储结构为散列存储结构(或Hash结构)。
运算:
增、删、改、查、判空、判满、清空、销毁、排序
算法:是一个有穷规则(或语句、指令)的有序集合
冒泡排序、选择排序、插入排序、希尔排序、快速排序。。。。
怎么才是一个好的算法:
空间复杂度: 设算法对应问题的体积(或规模)为n,执行算法所占存储空间的量级为D(n),则D(n)为算
法的空间复杂度
9 int printN(int n)
10 {
11 if(n)
12 {
13 printN(n-1);
14 printf("%d\n",n);
15 }
16 return 0;
17 }//不好的算法,占用太多的空间地址
18 #if 0
19 int printN(int n)
20 {
21 int i=0;
22 for(i=1;i<=n;i++)
23 {
24 printf("%d\n",i);
25 }
26 return 0;
27 }
28 #endif
29 int main(int argc, char *argv[])
30 {
31 int n;
32 scanf("%d",&n);
33 printN(n);
34
35 return 0;
36 }
时间复杂度:
第一步,算出所有语句的执行次数,4n3+2n2+n
第二步,取最高次项4n³
第三步,去除最高次项的系数n³
int i,j,z;
for(i=0;i<n;i++)//n+1
{
for(j=0;j<n;j++)//n*(n+1)
{
for(z=0;z<n;z++)//n*n*(n+1)
{
printf("%d",z);//n*n*n
}
}
算法和数据结构的关系:依存关系,没有算法只谈数据结构,数据都是死的,算法是活的,抛开数据结
构,只谈算法,没有依赖的数据支撑,空谈
线性表:
顺序表
/*===============================================
* 文件名称:sqlist.c
* 创 建 者:
* 创建日期:2023年03月06日
* 描 述:
================================================*/
#include "sqlist.h"
//创建顺序表
sq_pnode create_sqlist()
{
sq_pnode L = (sq_pnode)malloc(sizeof(sq_node));
if(NULL == L)
{
printf("create is error\n");
}
L->len=0;//顺序表长度初始化为0
return L;
}
//判满
int full_sqlist(sq_pnode L)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(L->len == N)
return 1;
else
return -1;
}
//判空
int empty_sqlist(sq_pnode L)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(0 == L->len)
return 1;
else
return -1;
}
//插入
int insert_sqlist(sq_pnode L,int pos,data_t data)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
//判满
if(1==full_sqlist(L))
{
printf("L is full\n");
return -1;
}
if(pos<0||pos>N)
{
printf("pos is eroor\n");
return -1;
}
int i;
//把插入的位置空出来,插入位置后面所有数据元素往后移动一个,最后一个开始
for(i=L->len-1;i>=pos;i--)
{
L->buf[i+1]=L->buf[i];
}
L->buf[pos]=data;//给插入的元素赋值
L->len++;
return 0;
}
//打印顺序表
int show_sqlist(sq_pnode L)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(1 == empty_sqlist(L))
{
printf("L is empty\n");
return -1;
}
int i;
for(i=0;i<L->len;i++)
{
printf("buf[%d]=%c\n",i,L->buf[i]);
}
return 0;
}
//删除
int delete_sqlist(sq_pnode L,int pos)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(1 == empty_sqlist(L))
{
printf("L is empty\n");
return -1;
}
if(pos<0||pos>L->len)
{
printf("pos is error\n");
return -1;
}
int i;
for(i=pos;i<L->len;i++)//找到删除的位置,把后面的值依次往前赋
{
L->buf[i]=L->buf[i+1];
}
L->len--;
return 0;
}
//根据元素查位置
int search_sqlist(sq_pnode L,data_t data)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(1 == empty_sqlist(L))
{
printf("L is empty\n");
return -1;
}
int i;
for(i=0;i<L->len;i++)
{
if(data == L->buf[i])
return i;
}
puts("没有这个元素");
return 0;
}
data_t search_sqlist2(sq_pnode L,int pos)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(1 == empty_sqlist(L))
{
printf("L is empty\n");
return -1;
}
if(pos<0||pos>L->len)
{
printf("pos is error\n");
return -1;
}
return L->buf[pos];
}
//清空
int clear_sqlist(sq_pnode L)
{
if(NULL == L)
{
printf("L is NULL\n");
return 0;
}
if(1 == empty_sqlist(L))
{
printf("L is empty\n");
return -1;
}
int i;
while(1)
{
delete_sqlist(L,0);
作业:
逆序打印顺序表
从小到大排序顺序表
if(L->len == 0)
{ printf("L is empty333\n");
return 0;
}
}
}
//摧毁
int destory_sqlist(sq_pnode *L)
{
if(NULL == *L)
{
printf("L is NULL\n");
return 0;
}
free(*L);
*L=NULL;
return 0;
}
数据结构-第二天
复习:
数据、数据元素,数据项,数据对象
结构:
逻辑:线性、非线性
存储:顺序、链式
运算:增删改查、判空、判满、清空、摧毁、排序
算法:时间复杂度和空间复杂度
顺序表:
插入、删除、查、改、判空、判满、清空和摧毁、打印
7 #include <stdio.h>
8 #define A char *
9 typedef char* Z;
10 int main(int argc, char *argv[])
11 {
12 A a,b;
13 Z c,d;
14 printf("a=%ld\n",sizeof(a));
15 printf("b=%ld\n",sizeof(b));
16 printf("c=%ld\n",sizeof(c));
17 printf("d=%ld\n",sizeof(d));
18 return 0;
19 }
链表:一对一,逻辑上相邻的数据元素物理地址可以不相邻
结点=数据域+指针域
数据域:放的是数值数据
指针域:放的是当前数据元素的下一个数据元素的首地址
头结点:没有存放数据,只是为了方便操作链表
练习:根据数据改数据
有余力的同学可以去写双向循环链表
栈:
顺序栈:在满足一对一,逻辑上相邻,物理位置也相邻,还要满足先进后出
作业:把顺序栈的写完
数据结构-第三天
链表:节省空间,插入和删除简单
顺序表:浪费空间,改和查简单
链栈:链表的特殊的一种形式,遵循先进后出的原则
/*===============================================
* 文件名称:linkstack.c
* 创 建 者:
* 创建日期:2023年03月08日
* 描 述:
================================================*/
#include "linkstack.h"
//创建
lsl_pnode create_linkstack()
{
lsl_pnode S = (lsl_pnode)malloc(sizeof(lsl_node));
if(NULL == S)
{
printf("create\n");
return NULL;
}
S->next = NULL;
return S;
}
//判空
int empty_linkstack(lsl_pnode S)
{
if(NULL == S)
{
printf("S is NULL\n");
return -1;
}
if(NULL == S->next)
return 0;
else
return 1;
}
//求长度
int len_linkstack(lsl_pnode S)
{
if(NULL == S)
{
printf("S is NULL\n");
return -1;
}
if(0 == empty_linkstack(S))
{
printf("S is empty\n");
return -1;
}
int len=0;
S=S->next;
while(S)
{
S=S->next;
len++;
}
return len;
}
//打印
int show_linkstack(lsl_pnode S)
{
if(NULL == S)
{
printf("S is NULL\n");
return -1;
}
if(0 == empty_linkstack(S))
{
printf("S is empty\n");
return -1;
}
S=S->next;
while(S)
{
printf("%c->",S->data);
S=S->next;
}
puts("");
return 0;
}
//入栈
int in_linkstack(lsl_pnode S,lsl_data_t data)
{
if(NULL == S)
{
printf("S is NULL\n");
return -1;
}
//创建新结点
lsl_pnode p = create_linkstack();
p->data = data;
p->next = S->next;
S->next = p;
return 0;
}
//出栈
int out_linkstack(lsl_pnode S)
{
if(NULL == S)
{
printf("S is NULL\n");
return -1;
}
if(0 == empty_linkstack(S))
{
printf("S is empty\n");
return -1;
}
lsl_pnode p = create_linkstack();
p=S->next;
printf("data=%c\n",p->data);
S->next=p->next;
free(p);
p=NULL;
return 0;
}
队列:先进先出
线性队列:一对一,只能从队头出,只能从队尾进
循环队列:重复使用同一片空间地址
1、为什么要空一个出来,为什么是(tail+1)%N
为了区别判空和判满的条件
2、为什么判满不是tail==N-1
单向队列确实可以,但是循环队列不能满足所有情况
/*===============================================
* 文件名称:queue.c
* 创 建 者:
* 创建日期:2023年03月08日
* 描 述:
================================================*/
#include "queue.h"
//创建
que_pnode create_queue()
{
que_pnode Q = (que_pnode)malloc(sizeof(que_node));
if(NULL == Q)
{
printf("create\n");
return NULL;
}
Q->head = Q->tail = 0;
return Q;
}
//判空
int empty_queue(que_pnode Q)
{
if(NULL == Q)
{
printf("Q is NULL\n");
return -1;
}
if(Q->head == Q->tail)
return 0;
else
return 1;
}
//判满
int full_queue(que_pnode Q)
{
if(NULL == Q)
{
printf("Q is NULL\n");
return -1;
}
if((Q->tail+1)%N == Q->head)
return 0;
else
return 1;
}
//入队
int in_queue(que_pnode Q,que_data_t data)
{
if(NULL == Q)
{
printf("Q is NULL\n");
return -1;
}
if(0 == full_queue(Q))
{
printf("Q is full\n");
return -1;
}
Q->buf[Q->tail]=data;//队尾赋值
Q->tail = (Q->tail+1)%N;//队尾往下移动一个,考虑插入最后一个后要变为队头,从头开始入队
return 0;
}
//打印
int show_queue(que_pnode Q)
{
if(NULL == Q)
{
printf("Q is NULL\n");
return -1;
}
if(0 == empty_queue(Q))
{
printf("Q is empty\n");
return -1;
}
int i;
for(i=Q->head;i!=Q->tail;i++)
{
printf("%c-",Q->buf[i]);
}
puts("");
return 0;
}
//出队b-c-d-e-
que_data_t out_queue(que_pnode Q)
{
if(NULL == Q)
{
printf("Q is NULL\n");
return -1;
}
if(0 == empty_queue(Q))
{
printf("Q is empty\n");
return -1;
}
que_data_t data = Q->buf[Q->head];
Q->head=(Q->head+1)%N;
return data;
}
链式队列:
数据结构-第四天
复习:
线性结构:(全部都掌握)
一对一,第一个数据元素没有前驱,最后一个后继,其余的都有唯一前驱和后继
表:线性结构,无特殊要求
顺序:查询和修改比较方便,定长浪费空间
链式:插入和删除比较方便,空间灵活
栈:线性结构,必须要满足先进后出原则 ,出栈和入栈都从栈顶开始
顺序:定长浪费空间
链式:空间灵活
队列:线性结构,同时要满足先进先出,出队只能从队头出,入队只能从队尾入
顺序:定长浪费空间
链式:空间灵活
非线性结构:
树:
1、树的概念:
树(Tree)是n(n≥0)个节点的有限集合T,它满足两个条件 :
有且仅有一个特定的称为根(Root)的节点;
其余的节点可以分为m(m≥0)个互不相交的有限集合T1、T2、……、Tm,其中每一个集合又是一棵树,并称为其根的子树(Subtree)
层次:树从根开始为第一层,后面依次递增
度:要看有多少个后继,一棵树的度数是指该树中节点的最大度数。
父节点:子节点的上一级(要有后继),根节点没有父节点
子节点:有且只有一个父节点
叶子节点:没有后继的节点
兄弟节点:同一个节点的子节点
孙子节点和祖先节点:两个节点之间有一层或多层
2、树的逻辑:
树中任何节点都可以有零个或多个直接后继节点(子节点),但至多只有一个直接前趋节点(父节点),根节点没有前趋节点,叶节点没有后继节点。
3、二叉树(重点)
概念:由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成
性质:
二叉树第i(i≥1)层上的节点最多为2i-1个。
深度为k(k≥1)的二叉树最多有2k-1个节点。
在任意一棵二叉树中,树叶的数目比度数为2的节点的数目多一。
分类:
满二叉树(完美二叉树):深度为k(k≥1)时有2k-1个节点的二叉树。
完全二叉树:只有最下面两层有度数小于2的节点,且最下面一层的叶节点集中在最左边的若干位置上
满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树
完全二叉树就是满二叉树从右往左,从下往上依次减
遍历:
层次遍历:一层一层遍历
先序遍历:根左右
中序遍历:左根右
后序遍历:左右根
存储结构:
顺序:
链式:
作业:写完树的所有遍历