🌈个人主页:小新_-
🎈个人座右铭:“成功者不是从不失败的人,而是从不放弃的人!”🎈
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
🏆所属专栏: 话说那些与C++的爱恨情仇 欢迎订阅,持续更新中~~~
✨让小新带着你快乐的学习吧~✨
目录
1、线性表的定义
2、顺序表分类
3.动态顺序表的实现
准备工作
1、文件的准备
2、顺序表的定义
3、扩容
4、打印当前顺序表
1、初始化和销毁
2、插入
2.1尾插
2.2头插
2.3任意位置之前插入
3、删除
3.1头删
3.2尾删
3.3任意位置删除
4、查找表中元素
完整代码
4、基于顺序表实现通讯录项目
4.1功能要求
4.2通讯录初始化
4.3添加联系人
4.4查看某联系人是否存在
4.5删除联系人
4.6打印通讯录
4.7修改联系人
4.7查找联系人
完整代码
5、经典顺序表OJ
1.经典算法OJ题1: 移除元素(点击进入题目)
2.经典算法OJ题2: 合并俩个有序数组(点击进入题目)
6. 顺序表的问题及思考
【知识框架】
1、线性表的定义
线性表(List):零个或多个数据元素的有限序列。
线性表的数据集合为{a1,a2,…,an},假设每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
线性表特点:
- 元素个数有限
- 元素具有先后顺序(逻辑上的先后顺序)
- 表中每个元素都是数据元素,每个元素都是单个元素
- 表中元素的数据类型相同(意味着每个元素占用存储空间大小一样)
- 表中元素具有抽象性,仅讨论元素间的逻辑关系,而不考虑元素究竟表示什么内容
注意:线性表是一种逻辑结构,表示元素间一对一的相邻关系。顺序表、链表是指存储结构
2、顺序表分类
动态顺序表:
3.动态顺序表的实现
准备工作
1、文件的准备
首先我们创建三个文件,一个是sqlist.c用于实现各种函数接口,一个为sqlist.h用于放置函数声明,另外一个test.c用于测试函数的正确性。他们三个文件最后链接实现我们的动态顺序表。
要实现的函数接口,就放在下面啦!
sqlist.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
//动态顺序表
typedef int SLDataType;
typedef struct Seqlist {
SLDataType* arr;//数据
int capacity;//顺序表空间大小
int size;//有效数据个数
}SL;
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps); //保持接口一致性
//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
//顺序表的头部/尾部删除
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//指定位置之前插入数据
//删除指定位置数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
2、顺序表的定义
首先在顺序表.h的文件里,把顺序表这个结构体先定义下来,定义完后,可以用typedef改名,然后,我们要想这个数组里必须是int型的吗,不一定,那我就用typedef提前把int重类型一下,这样我们就可以不仅仅停留在int,若想改成其他类型,直接在typedef后将int改成你想在顺序表放的类型就可以了。
3、扩容
在插入之前我们要想咱们现在初始化容量数为0,得扩容呀,那我们就扩容定义\n\n如何来扩容那,我们一般规定扩容到原空间的两倍,扩容用到的函数是realloc函数,但刚开始是0,我们可以用三目操作符来判断如果空间为0,我们要给它4个空间数,若不是0,直接capacity乘2,接着再使用新的容量数乘每个类型的字节数放到realloc里就是开辟了新的空间。至于为什么是2,有相当严格的证明,这里不再赘述。
代码如下:
void SLCheckCapacity(SL* ps)//判断是否扩容
{
if (ps->capacity == ps->size)
{//扩容
int newcapacity = ps->capacity = 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, 2 * newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//扩容成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
4、打印当前顺序表
逻辑比较简单,不再赘述
代码如下:
void SLPrint(SL* ps) //打印
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
1、初始化和销毁
分析:
初始化十分简单,将数组置为空,因为此时没有数据。顺序表的空间大小和有效数据个数置为0。我们的初始化就完成了。
void SLInit(SL* ps)//初始化
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
分析:销毁基本和初始化差不多,俗话说一切回到原点,只不过为了健壮性要断言ps指针,然后将其空间归还给操作系统。
void SLDestroy(SL* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
2、插入
2.1尾插
分析:
尾插比较简单,空间足够,直接在顺序表尾部插入即可。不够就得先扩容,再插入
代码如下:
void SLPushBack(SL* ps, SLDataType x)//尾插
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size] = x;
ps->size++;
}
2.2头插
分析:
头插,我们要先将原来的数据整体往后挪动一位,这要通过一个for循环来实现,然后让头结点的值等于我们要赋予的值。如果空间不够,就得先扩容
代码如下:
void SLPushFront(SL* ps, SLDataType x)//头插
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i-1];
}
ps->arr[0] = x;
ps->size++;
}
2.3任意位置之前插入
分析:
首先,要删除的顺序表不能为空。再通过for循环让pos位置以及pos之后的元素向后挪动一位,再讲pos位置赋值为要给定的值即可
代码如下:
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size ;i>pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
3、删除
3.1头删
分析:
首先,删除的顺序表肯定不为空,在通过for循环将元素整体往前挪动一位,有效数据个数size-1即可
代码如下:
void SLPopFront(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->arr[ps->size-1]; i++)
{
ps->arr[i] = ps->arr[i +1];
}
ps->size--;
}
3.2尾删
分析:
尾删其实很简单,只需将尾部元素置为0,size--即可。当然,顺序表为空,不能执行删除操作,所以要判断一下。
代码如下:
void SLPopBack(SL* ps)//尾部删除
{
assert(ps);
assert(ps->size);
ps->arr[ps->size] = 0;
ps->size--;
}
3.3任意位置删除
分析:
逻辑比较简单,首先判断是否为空。然后让pos之后的数据往前挪动一位,这样数据会被覆盖,也就完成了删除的效果。
代码如下:
void SLErase(SL* ps, int pos)
{
assert(ps);
SLCheckCapacity(ps);
for (int i = pos; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
4、查找表中元素
分析:遍历顺序表,找到了就返回元素下标,没找到返回-1.
代码如下:
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
}
}
return -1;
}
完整代码
sqlist.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"sqlist.h"
#include<assert.h>
void SLInit(SL* ps)//初始化
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLCheckCapacity(SL* ps)//判断是否扩容
{
if (ps->capacity == ps->size)
{//扩容
int newcapacity = ps->capacity = 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, 2 * newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//扩容成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
void SLPushBack(SL* ps, SLDataType x)//尾插
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size] = x;
ps->size++;
}
void SLPushFront(SL* ps, SLDataType x)//头插
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i-1];
}
ps->arr[0] = x;
ps->size++;
}
void SLPopBack(SL* ps)//尾部删除
{
assert(ps);
assert(ps->size);
ps->arr[ps->size] = 0;
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->arr[ps->size-1]; i++)
{
ps->arr[i] = ps->arr[i +1];
}
ps->size--;
}
void SLPrint(SL* ps) //打印
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
//指定位置之前插入数据
//删除指定位置数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size ;i>pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
SLCheckCapacity(ps);
for (int i = pos; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
}
}
return -1;
}
void SLDestroy(SL* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
4、基于顺序表实现通讯录项目
通讯录就是给顺序表套一层壳,实际上还是顺序表。只不过里面的数据类型变成了一个结构体
手机通讯录其实就是一个顺序表,顺序表的每一个成员都是一个结构体,结构体中包含联系人的
名字、性别、年龄、电话、地址等信息,只不过我们之前建立的顺序表里存储的是整形数据,而通讯录里存储的是结构体类型而已。
所以我们还要创建一个结构体来存储联系人信息。
#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;
4.1功能要求
1)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显示联系⼈信息
4.2通讯录初始化
void ContactInit(CON* con)
{
SlInit(con);
}
通讯录的初始化也就是顺序表的初始化,我们直接调用之前的顺序表初始化函数即可。
4.3添加联系人
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);
}
我们先定义一个联系人结构体,再添加联系人信息,最后调用尾插函数,把联系人尾插到通讯录中。
4.4查看某联系人是否存在
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的数。
4.5删除联系人
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函数,删除改下表对应的联系人。
4.6打印通讯录
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);
}
}
遍历数组,打印结构体成员信息。
4.7修改联系人
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);
}
如果要查找的联系人存在,打印该联系人的信息。
完整代码
contest.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
#include"sqlist.h"
#include<string.h>
void ContactInit(Contact* pcon)//实际初始化的还是顺序表
{
SLInit(pcon);
}
void ContactDesTroy(Contact* pcon)
{
SLDestroy(pcon);
}
//增加、删除、修改、查找、查看通讯录
int FindByName(Contact* pcon,char name[])
{
for (int i = 0; i < pcon->size; i++)
{
if (strcmp(pcon->arr->name, name) == 0)
{
return i;
}
}
return -1;
}
void ContactAdd(Contact* pcon)
{
Info info;
printf("请输入联系人姓名:\n");
scanf("%s", info.name);
printf("请输入联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入联系人性别:\n");
scanf("%s", info.gender);
printf("请输入联系人住址:\n");
scanf("%s", info.addr);
printf("请输入联系人电话:\n");
scanf("%s", info.tel);
SLPushBack(pcon, info);
}
void ContactDel(Contact* pcon)
{
//删除之前一定要先查找
//找到了,可以删除
//找不到,不能执行删除
printf("请输入要删除的联系人姓名:\n");
char name[NAME_MAX];
scanf("%s", name);
int findIndex = FindByName(pcon, name);
if (findIndex < 0) {
printf("要删除的联系人不存在!\n");
return;
}
//执行删除操作
SLErase(pcon, findIndex);
printf("联系人删除成功!\n");
}
void ContactModify(Contact* pcon)
{
char name[NAME_MAX];
printf("请输入要修改的联系人姓名:\n");
scanf("%s", name);
int findIndex = FindByName(pcon, name);
if (findIndex < 0)
{
printf("要修改的联系人不存在!\n");
return;
}
//找到了,执行修改操作
//int arr[10] int arr[findIndex] = 100
printf("请输入姓名:\n");
scanf("%s", pcon->arr[findIndex].name);
printf("请输入年龄:\n");
scanf("%d", &pcon->arr[findIndex].age);
printf("请输入性别:\n");
scanf("%s", pcon->arr[findIndex].gender);
printf("请输入电话:\n");
scanf("%s", pcon->arr[findIndex].tel);
printf("请输入地址:\n");
scanf("%s", pcon->arr[findIndex].addr);
printf("联系人修改成功!\n");
}
void ContactFind(Contact* pcon)
{
char name[NAME_MAX];
printf("请输入要查找的用户姓名:\n");
scanf("%s", name);
int findIndex = FindByName(pcon, name);
if (findIndex < 0) {
printf("该联系人不存在!\n");
return;
}
//找到了,打印一下查找的联系人信息
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%s %s %d %s %s\n",
pcon->arr[findIndex].name,
pcon->arr[findIndex].gender,
pcon->arr[findIndex].age,
pcon->arr[findIndex].tel,
pcon->arr[findIndex].addr
);
}
void ContactShow(Contact* pcon)
{
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
for (int i = 0; i < pcon->size; i++)
{
printf("%s %s %d %s %s\n",
pcon->arr[i].name,
pcon->arr[i].gender,
pcon->arr[i].age,
pcon->arr[i].tel,
pcon->arr[i].addr
);
}
}
contest.h
#pragma once
#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100
#include<stdio.h>//暂时加上
//通讯录数据类型
typedef struct PersonInfo
{
char name[NAME_MAX];
int age;
char gender[GENDER_MAX];
char tel[TEL_MAX];
char addr[ADDR_MAX];
}Info;
struct Seqlist;
typedef struct Seqlist Contact;
//使用顺序表的前置声明
//struct SeqList;
//typedef struct SeqList Contact;
//通讯里提供的操作
//通讯录的初始化和销毁
void ContactInit(Contact* pcon);//实际初始化的还是顺序表
void ContactDesTroy(Contact* pcon);
//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon);
void ContactDel(Contact* pcon);
void ContactModify(Contact* pcon);
void ContactFind(Contact* pcon);
void ContactShow(Contact* pcon);
5、经典顺序表OJ
1.经典算法OJ题1: 移除元素(点击进入题目)
int removeElement(int* nums, int numsSize, int val) {
int i=0,j=0;
while(j<numsSize)
{
if(nums[j]==val)
{
j++;
}
else{
nums[i]=nums[j];
i++;
j++;
}
}
return i;
}
2.经典算法OJ题2: 合并俩个有序数组(点击进入题目)
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int l1=m-1;
int l2=n-1;
int l3=m+n-1;
while(l1>=0&&l2>=0)
{
if(nums1[l1]>nums2[l2])
{
nums1[l3]=nums1[l1];
l3--;
l1--;
}
else
{
nums1[l3]=nums2[l2];
l3--;
l2--;
}
}
while(l2>=0)
{
nums1[l3]=nums2[l2];
l3--;
l2--;
}
}
6. 顺序表的问题及思考
文章来源:https://www.toymoban.com/news/detail-853236.html
最后,感谢大家的观看~~文章来源地址https://www.toymoban.com/news/detail-853236.html
到了这里,关于数据结构:线性表————顺序表的实现、项目和OJ题目(手把手教你写代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!