赞
踩
数据结构是由“数据”和“结构”两词组合⽽来。
什么是数据?
常⻅的数值1、2、3、4.....、教务系统⾥保存的⽤⼾信息(姓名、性别、年龄、学历等 等)、⽹⻚⾥⾁眼可以看到的信息(⽂字、图⽚、视频等等),这些都是数据
什么是结构?
当我们想要使⽤⼤量使⽤同⼀类型的数据时,通过⼿动定义⼤量的独⽴的变量对于程序来说,可读性 ⾮常差,我们可以借助数组这样的数据结构将⼤量的数据组织在⼀起,结构也可以理解为组织数据的 ⽅式。
想要找到草原上名叫“咩咩”的⽺很难,但是从⽺圈⾥找到“咩咩”就很简单,⽺圈这样的结构有效将 ⽺群组织起来。
概念:数据结构是计算机存储、组织数据的⽅式。数据结构是指相互之间存在⼀种或多种特定关系 的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么⽅式构成,以及数据元素之间呈现的结构。
总结: 1)能够存储数据(如顺序表、链表等结构)
2)存储的数据能够⽅便查找
为什么需要数据结构?
如图中所⽰,不借助排队的⽅式来管理客⼾,会导致客⼾就餐感受差、等餐时间⻓、餐厅营业混乱等 情况。同理,程序中如果不对数据进⾏管理,可能会导致数据丢失、操作数据困难、野指针等情况。 通过数据结构,能够有效将数据组织和管理在⼀起。按照我们的⽅式任意对数据进⾏增删改查等操作。
最基础的数据结构:数组。
【思考】有了数组,为什么还要学习其他的数据结构?
假定数组有10个空间,已经使⽤了5个,向数组中插⼊数据步骤:
求数组的⻓度,求数组的有效数据个数,向下标为数据有效个数的位置插⼊数据(注意:这⾥是 否要判断数组是否满了,满了还能继续插⼊吗).....
假设数据量⾮常庞⼤,频繁的获取数组有效数据个数会影响程序执⾏效率。
结论:最基础的数据结构能够提供的操作已经不能完全满⾜复杂算法实现。
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使 ⽤的数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。 案例:蔬菜分为绿叶类、⽠类、菌菇类。线性表指的是具有部分相同特性的⼀类数据结构的集合。
• 顺序表和数组的区别
◦ 顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等功能。
• 顺序表分类
- typedef struct Slist
- {
- int arr[100];
- int size; //数组中元素个数
- }SL;
静态顺序表中,数组的大小是固定的。
静态顺序表缺陷:空间给少了不够⽤,给多了造成空间浪费
2.2.2 动态顺序表
- typedef struct Slist
- {
- int* arr; //由于不知道数组多大空间,所以先定义一个指向起始位置的指针
- int size; //数组中有效元素的个数,初始大小为0
- int OverSpaace; //数组的整个空间大小,初始大小为0
- }SL;
动态顺序表的空间就比较灵活,可以用动态内存管理函数开辟空间,从而改变OverSpaace的值。
所以我们接下来主要来探究动态顺序表元素的增删查改。
定义好顺序表的结构后,我们先给结构体成员赋一个初始值。
- void SlInit(SL* ps)
- {
- ps->arr = NULL;
- ps->size = 0;
- ps->OverSpaace = 0;
- }
注意:传参的时候应该传递结构体的地址,因为初始化我们要改变结构体成员的值,所以这里要传结构体的地址。
我们初始化完顺序表空间大小是0,所以如果想往顺序表中添加元素,在添加元素之前就应该对顺序表增容。
- void SlExpansion(SL* ps)
- {
- if (ps->size == ps->OverSpaace)
- {
- int NewOverSpaace = (ps->OverSpaace == 0) ? 4 : 2 * ps->OverSpaace;
- DataType* tmp = (DataType*)realloc(ps->arr, sizeof(DataType) * NewOverSpaace);
- if (tmp == NULL)
- {
- perror("realloc");
- return ;
- }
- ps->arr = tmp;
- ps->OverSpaace = NewOverSpaace;
- }
- }
这里我们先判断空间是否够用,如果空间不够用就进行增容。我们用realloc函数进行增容。增容后的空间大小在重新赋值给OverSpaace。
动态顺序表的打印和数组的打印是一样的。
- void SlPrint(SL* ps)
- {
- for (int i = 0; i < ps->size; i++)
- {
- printf("%d ", ps->arr[i]);
- }
- }
- void AddTail(SL* ps, DataType x)
- {
- assert(ps);
- SlExpansion(ps);
- ps->arr[ps->size++] = x;
-
- }
注意:在添加数据之前要判断需不需要增容。插入完成后有效数据个数+1(size++)
- void AddHead(SL* ps, DataType x)
- {
- assert(ps);
- SlExpansion(ps);
- for (int i = ps->size; i > 0; i--)
- {
- ps->arr[i] = ps->arr[i - 1];
- }
- ps->arr[0] = x;
- ps->size++;
- }
在头部添加数据之前,我们要把数组中的元素依次往后移动一个单位。然后再把数据插入到第一个节点中。移动的时候要注意是从后往前移动。插入完成后有效数据个数+1(size++)
- void DelHead(SL* ps)
- {
- assert(ps);
- for (int i = 0; i < ps->size-1; i++)
- {
- ps->arr[i] = ps->arr[i + 1];
- }
- ps->size--;
- }
删除头部的数据只需要把数组中的元素依次往前移动一位,这样第一个元素就被挤出去了,最后size--。
- void DelTail(SL* ps)
- {
- assert(ps);
- ps->size--;
- }
删除尾部数据直接size--就可以,这样尾部的数据也被挤出去了。
- void SpeLocInsert(SL* ps, int loc, int x)
- {
- assert(ps);
- assert(loc >= 0 && loc <= ps->size);
- for (int i = ps->size; i > loc; i--)
- {
- ps->arr[i] = ps->arr[i - 1];
- }
- ps->arr[loc] = x;
- ps->size++;
- }
把要插入数据的位置之后的元素依次往后移动一个单位,然后再把想插入的数据插入到指定位置中。插入完成后有效数据个数+1(size++)。
- void SpeLocDel(SL* ps, int loc)
- {
- assert(ps);
- for (int i = loc; i < ps->size-1; i++)
- {
- ps->arr[i] = ps->arr[i + 1];
- }
- ps->size--;
- }
把要删除数据的位置之后的元素依次往前一定一个单位,最后size--。
- int fund(SL* ps, int x)
- {
- assert(ps);
- for (int i = 0; i < ps->size; i++)
- {
- if (ps->arr[i] == x)
- {
- return i;
- }
- }
- return -1;
- }
遍历数组,如果要查找的数据存在就返回该元素的下标,否则返回一个小于0的数。
这样,顺序表的增删查改任务就完成了,哪学完之后有什么用呢?
我们下面实现一个手机通讯录的小项目。
手机通讯录其实就是一个顺序表,顺序表的每一个成员都是一个结构体,结构体中包含联系人的
名字、性别、年龄、电话、地址等信息,只不过我们之前建立的顺序表里存储的是整形数据,而通讯录里存储的是结构体类型而已。
所以我们还要创建一个结构体来存储联系人信息。
- #define MAX_NAME 20
- #define MAX_GENDER 10
- #define MAX_PHONE 20
- #define MAX_ADDR 30
- typedef struct contacts
- {
- char name[MAX_NAME];
- char gender[MAX_GENDER];
- int age;
- char phone[MAX_PHONE];
- char addr[MAX_ADDR];
- }contacts;
既然通讯录就是顺序表,那我们可以给顺序表改类型一个名字
- struct SList;
- typedef struct SList CON;
1)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显示联系⼈信息
- void ContactInit(CON* con)
- {
- SlInit(con);
- }
通讯录的初始化也就是顺序表的初始化,我们直接调用之前的顺序表初始化函数即可。
- void AddContact(CON* con)
- {
- contacts f;
- printf("请输入要添加的联系人姓名:");
- scanf("%s", f.name);
- printf("请输入要添加的联系人性别:");
- scanf("%s", f.gender);
- printf("请输入要添加的联系人年龄:");
- scanf("%d", &f.age);
- printf("请输入要添加的联系人电话:");
- scanf("%s", f.phone);
- printf("请输入要添加的联系人住址:");
- scanf("%s", f.addr);
- AddTail(con, f);
- }
我们先定义一个联系人结构体,再添加联系人信息,最后调用尾插函数,把联系人尾插到通讯录中。
- int FundByContact(CON* con, char name[])
- {
- assert(con);
- for (int i = 0; i < con->size; i++)
- {
- if (0 == strcmp(con->arr[i].name, name))
- {
- return i;
- }
- }
- return -1;
- }
遍历数组,如果遍历到了要查找的联系人的名字,就返回数组成员下标,否则返回一个小于0的数。
- void DelContact(CON* con)
- {
- assert(con);
- char name[MAX_NAME];
- printf("请输入要删除的联系人姓名:\n");
- scanf("%s", name);
- int fund = FundByContact(con, name);
- if (fund < 0)
- {
- printf("该联系人不存在");
- }
- SpeLocDel(con, fund);
- }
调用FundByContact函数,如果要删除的联系人存在,那么返回数组下标,在调用SpeLocDel函数,删除改下表对应的联系人。
- void PrintContact(CON* con)
- {
- printf("姓名 性别 年龄 电话 住址\n");
- for (int i = 0; i < con->size; i++)
- {
- printf("%s %s %d %s %s\n",
- con->arr[i].name,
- con->arr[i].gender,
- con->arr[i].age,
- con->arr[i].phone,
- con->arr[i].addr);
- }
- }
遍历数组,打印结构体成员信息。
- void ModifyContact(CON* con)
- {
- char name[MAX_NAME];
- printf("请输入要修改的联系人姓名");
- scanf("%s", name);
- int fund = FundByContact(con, name);
- if (fund < 0)
- {
- printf("该联系人不存在");
- }
- printf("请输入新的联系人姓名:\n");
- scanf("%s", con->arr[fund].name);
-
- printf("请输入新的联系人性别:\n");
- scanf("%s", con->arr[fund].gender);
-
- printf("请输入新的联系人年龄:\n");
- scanf("%d", &con->arr[fund].age);
-
- printf("请输入新的联系人电话:\n");
- scanf("%s", con->arr[fund].phone);
-
- printf("请输入新的联系人住址:\n");
- scanf("%s", con->arr[fund].addr);
-
- }
如果要修改的联系人存在,用fund接收返回的下标,再通过下标修改联系人信息。
4.7查找联系人
- void FundContact(CON* con)
- {
- assert(con);
- char name[MAX_NAME];
- printf("请输入要查找的联系人姓名:\n");
- scanf("%s", name);
- int fund = FundByContact(con, name);
- if (fund < 0)
- {
- printf("该联系人不存在");
- }
- printf("%s %s %d %s %s\n",
- con->arr[fund].name,
- con->arr[fund].gender,
- con->arr[fund].age,
- con->arr[fund].phone,
- con->arr[fund].addr);
- }
如果要查找的联系人存在,打印该联系人的信息。
至此,通讯录的基本功能就实现了。感谢大家观看。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。