数据结构深入学习Day01
一、数据结构相关
1.数据结构:数据的逻辑结构、存储结构及操作
2.数据元素:是数据的基本单位,由若干个基本项组成(数据项)
3.节点:数据元素就叫节点
4.数据关系:线性、层次(树)、网状(图)
5.存储结构:
顺序、链式、索引、散列
6.算法原则:有穷性(时间消耗)、可行性、消耗空间小、可读性、可维护性、移植性
二、线性表
一对一,每个节点最多有一个前驱一个后继。
首节点无前驱,尾节点无后继。
顺序表插入删除元素:
插入时从后往前替换
删除时从前往后替换
#include <stdio.h>
int last = 5;//全局变量,last的值时刻代表数组中有效元素的个数
//将数组中的有效元素打印输出
void showArray(int* p)
{
int i;
for(i = 0; i < last; i++)
{
printf("%d ",p[i]);
}
printf("\n");
}
//int post 插入的位置 第几个
//int x 插入的数据
void insertIntoA(int* p, int post, int x)
{
//1.从有效元素位置下标--插入位置的下标整体向后移动一个位置 last-1 -- post-1
int i;
for(i = last-1; i >= post-1; i--)
p[i+1] = p[i];
//2.将新的数据插入
p[post-1] = x;//post-1 因为第post个位置,对应的下标是post-1
//3.有效元素的个数+1 last++
last++;
}
//删除指定位置的数据
void deleteFromA(int* p, int post)
{
//1.将从删除位置的后一个位置的下标--最后一个有效元素,整体向前移动一个位置
//post -- last-1
int i;
for(i = post; i <= last-1; i++)
p[i-1] = p[i];
//2.有效元素个数-1
last--;
}
int main(int argc, const char *argv[])
{
int a[100] = {11,22,33,44,55};
showArray(a);
//增加,在第3个位置,插入一个数1000
insertIntoA(a, 6, 1000);
showArray(a);//11 22 1000 33 44 55
deleteFromA(a, 6);//删除第3个位置的数据
showArray(a);//11 22 33 44 55
return 0;
}
三、typedef关键字(类型重定义,增强可移植性)
Typedef int ZZZ;
ZZZ a=10;//相当于int a=10;
typedef int size_t;
size_t a = 10; //等价于int a = 10
void *malloc(size_t size);//size_t 类型重定义
struct student
{
char name[20];//成员变量
int age;
int score;
};//一定要有分号
四、顺序表:结构体结合
顺序表操作:
(1)创建空顺序表
(2)向指定位置插入元素
(3)判断顺序表是否为满
(4)删除指定位置元素
(5)判断顺序表是否为空
(6)求表长
(7)清空顺序表
(8)查找指定元素的位置
(9)遍历顺序表
#include <stdio.h>
#include <stdlib.h>
//int last = 5;//全局
//int a[100] = {11,22,33,44,55};//数组,用来存储有效数据
#define N 100
typedef struct
{
int last;//last时刻代表有效元素的个数
int data[N];//用来存储有效数据
}seqlist_t;
//sequence 顺序
//list 表
//1.遍历顺序表,将所有的有效元素打印输出
void showSeqlist(seqlist_t* p)//实参初始化形参 seqlist_t* p = p(main函数中的p)
{
int i;
for(i = 0; i < p->last; i++)
{
printf("%d ",p->data[i]);
}
printf("\n");
}
//2.创建一个空的顺序表
seqlist_t* createEmptySeqlist()
{
seqlist_t* p = (seqlist_t*)malloc(sizeof(seqlist_t));//堆区
if(p == NULL)
{
printf("malloc failed!!\n");
return NULL;//提前结束函数
}
p->last = 0;//因为创建的是空的顺序表,所以last的值初始化为0,有效元素个数为0
return p;
}
//3. 向指定的位置插入数据
int insertIntoSeqlist(seqlist_t* p, int post, int x) //seqlist_t* q = p;
{
//0.容错判断,对插入位置是否合理,进行条件判断
if(post < 1 || post > p->last+1 || isFullSeqlist(p))
{
printf("insertIntoSeqlist failed!!\n");
return -1;//通常用-1来表达失败
}
//1.将最有一个有效元素的下标到插入位置的下标整体向后一个位置
int i;
for(i = p->last-1; i >= post-1; i--)
p->data[i+1] = p->data[i];
//2.插入数据
p->data[post-1] = x;
//3.有效元素个数+1
p->last++;
return 0;//用0来表达成功
}
//4.判断顺序表是否为满 满返回值是1,未满是0
int isFullSeqlist(seqlist_t* p)
{
/*
//方法一:有效元素个数 == 数组的长度,表满
if(p->last == N)
return 1;
else
return 0;
//方法二:
return p->last == N ? 1 : 0;
*/
//方法三
return p->last == N;//p->last == N 表达式为真,C语言中,真用1来表达, p->last == N表达式为假,C语言中假用0来表达
}
//5. 判断是否为空 空返回值是1,未空返回值0
int isEmptySeqlist(seqlist_t* p)
{
return p->last == 0 ? 1 : 0;
}
//6.删除指定位置的数据
int deleteFromSeqlist(seqlist_t* p, int post)
{
int i;
//0.容错判断 对删除位置是否合理,进行判断
if(post < 1 || post > p->last || isEmptySeqlist(p))
{
printf("deleteFromSeqlist failed!!\n");
return -1;
}
//1.将删除位置的后一个位置的下标到最后一个有效元素下标,整体向前移动一个位置,覆盖删除
for(i = post; i <= p->last-1; i++)
{
p->data[i-1] = p->data[i];
}
//2.有效元素个数-1
p->last--;
return 0;
}
//7.求顺序表的长度,长度就是有效元素的个数
int getLengthSeqlist(seqlist_t* p)
{
return p->last;
}
//8.清空顺序表
void clearSeqlist(seqlist_t* p)
{
p->last = 0;
}
//9.查找指定数据出现的位置,返回数组中的位置下标
//int x,代表被查找的数据
int searchDataSeqlist(seqlist_t* p, int x)
{
int i;
for(i = 0; i < p->last; i++)
{
if(p->data[i] == x)
return i;
}
return -1;//代表没有找到
}
int main(int argc, const char *argv[])
{
seqlist_t* p = createEmptySeqlist();//得到malloc申请结构体变量空间大小的首地址
insertIntoSeqlist(p, 1, 11);//将首地址告诉insertIntoSeqlist函数
insertIntoSeqlist(p, 2, 22);
insertIntoSeqlist(p, 3, 33);
insertIntoSeqlist(p, 4, 44);
insertIntoSeqlist(p, 5, 55);
insertIntoSeqlist(p, 3, 1000);
showSeqlist(p);//将首地址告诉showSeqlist函数
deleteFromSeqlist(p,3);
showSeqlist(p);
printf("len is %d\n",getLengthSeqlist(p));
printf("33的位置下标是%d\n",searchDataSeqlist(p,33));
clearSeqlist(p);
printf("len is %d\n",getLengthSeqlist(p));
free(p);
return 0;
}
//2.链表
逻辑结构: 线性结构
存储结构: 链式存储结构
链式存储结构: 内存当中是不连续存储的,通过指针将每个节点连接在一起
//node 节点的意思
//link 链接
//定义一个节点
typedef struct node_t //注意此处的node_t不能省略不写
{
int data;//数据域,用来保存有效数据的
struct node_t* next;//指针域 用来指向下一个节点的指针
}link_node_t;
sizeof(struct node_t) --> 8
link_node_t s;
单向链表可以分为 有头的单向链表 和 无头的单向链表
无头单向链表:链表中的每一个节点的数据域,都是有效的数据
有头单向链表:链表中的第一个节点的数据域是无效的,其他是有效的
###有头单向链表和无头的单向链表只是一个相对的概念
2.1 无头的单向链表
#include <stdio.h>
typedef struct node_t
{
int data;//数据域
struct node_t* next;//指针域
}link_node_t;
int main(int argc, const char *argv[])
{
//无头单向链表,链表中的每个节点的数据域,都是有效的数据
//1.定义5个节点
link_node_t a = {10,NULL};
link_node_t b = {20,NULL};
link_node_t c = {30,NULL};
link_node_t d = {40,NULL};
link_node_t e = {50,NULL};
//2.将5个节点连接在一起
a.next = &b;
b.next = &c;
c.next = &d;
d.next = &e;
//3.定义一个头指针,保存链表第一个节点的地址
link_node_t* p = &a;
//4.遍历无头单向链表
while(p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return 0;
}
五、链式存储结构
内存中不连续存储,通过指针将每个节点链接在一起
无头链表:
有头单向链表:第一个数据域无效
尾插法:要定义一个尾指针,永远指向当前链表的尾巴,即最后一个节点
写一个有头单向链表,一直输入学生成绩,存入链表中,直到输入-1 结束程序
每输入一个学生成绩,就malloc申请一个新的节点,将输入的成绩保存到数据域,并将该新节点链接到链表的尾巴
//要求:每一个链表的节点由动态内存分配malloc得到,包括头节点
// 编程思想:
//1.malloc创建一个链表的头节点,数据域不用装东西
//2.
//可以再定义一个指针变量,永远指向当前链表的尾巴
int score;
while(1)
{
scanf("%d",score);
if(score == -1)
break;
//3.申请节点大小的空间,将 score 保存到节点的 数据域score中,将这个节点连接链表的尾巴
}
//3.循环遍历打印链表
#include <stdio.h>
#include <stdlib.h>
typedef struct node_t
{
int data;
struct node_t* next;
}link_node_t;
int main(int argc, const char *argv[])
{
int score;//用来保存输入的学生成绩
//1.创建一个头节点,作为链表的头
link_node_t* ptail = NULL;//ptail尾指针,永远指向当前链表的尾巴
link_node_t* phead = malloc(sizeof(link_node_t));
if(phead == NULL)
{
printf("phead malloc failed!!\n");
return -1;
}
phead->next = NULL;//有可能输入第一个学生成绩就是-1,循环结束了,是一个空的链表,作为遍历有头链表结束条件
//由于最开始只有一个节点,既是头节点,也是尾节点
ptail = phead;
//2.循环输入学生成绩,输入一个成绩,就保存一个新创建节点的数据域中,插入在链表的尾巴上
while(1)
{
printf("请输入学生成绩:\n");
scanf("%d",&score);
if(score == -1)
break;
//创建新的节点,用来保存输入的成绩
link_node_t* pnew = malloc(sizeof(link_node_t)); //pnew永远指向新创建的节点
if(pnew == NULL)
{
printf("pnew malloc failed!!\n");
return -1;
}
//申请空间后,立刻赋值,将成绩存入到新创建的节点中
pnew->data = score;//输入的成绩,保存到新节点的数据域
pnew->next = NULL;
//将新的节点,插入在链表的尾巴上
//尾插法思想:尾指针,永远指向当前链表的尾巴,也就是最后一个节点,只要让最后一个节点的next指针,指向新的节点,就算插入成功
ptail->next = pnew;//插入在尾巴上
//将尾指针移动当前链表的尾巴,因为插入之后,链表变长了
ptail = pnew;//ptail = ptail->next;
}
//遍历有头单向链表
while(phead->next != NULL)
{
phead = phead->next;
printf("%d ",phead->data);
}
printf("\n");
return 0;
}
六、顺序表总结
1.逻辑结构是线性结构,存储结构是顺序存储结构,在内存当中是连续存储的
2.顺序表优点:查找方便,因为可以通过数组下标,直接访问元素
3.顺序表缺点:a:顺序表长度固定(本质是操作的数组)
b:插入和删除麻烦