一.通讯录管理系统前言
通讯录管理系统是一种用于管理联系人信息应用程序,它可以帮助用户实现方便的增加,删除,查找各种联系人信息,本次我们将通过学习顺序表专题,来实现通讯录管理系统。
学习顺序表专题需要我们大致掌握以下知识:
1.指针(包括二级指针等知识)
2.结构体
3.动态内存管理(malloc等函数)
接下来就让我们开启顺序表专题的学习吧!
二.数据结构之顺序表
2.1 什么是顺序结构
以下为百度百科官方解释:
在计算机科学中,数据结构是一种数据组织、管理和存储的格式。它是相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术相关 。
简单来说,我们将数据结构拆分为两个词语数据和结构。第一,什么是数据,数据可以是一堆数,如果他有实际意义,那他就可以被称为有效数据,我的理解是,描述事物的信息都可以被称为数据。第二,那什么是结构呢?有了数据,如果所有数据都混杂在一起,我们将难以理清每一个数据的意义,有了结构我们才能清晰的读取数据,简单点就是将数据进行整理排列成易于理解的顺序,就是结构。结合这两个词语,我们就能很好的理解数据结构。
代入计算机里,就是数据就结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据有哪部分构成,以什么方式构成,以及数据元素之间呈现的结构。
总结:
1)能够存储数据(如顺序表、链表等结构)
2)存储的数据能够⽅便查找
2.2顺序表
2.2.1线性表
线性表(
linear list
)是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使
⽤的数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.2.2顺序表的分类
•
顺序表和数组的区别
◦
顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝。
顺序表分为静态顺序表和动态顺序表。
静态顺序表(
使⽤定⻓数组存储元素
)
静态顺序表的缺点:无法灵活分配空间,空间给少了不够用,给多了会造成空间浪费。
动态顺序表
2.2.3 动态顺序表的实现
#define INIT_CAPACITY 4
typedef int SLDataType; //将数据类型名字重定义 可以灵活存储多种顺序类型 //动态顺序表 --按需申请
typedef struct SeqList
{
SLDataType* arr;
int size; //有效数据个数
int capacity; //空间容量
}SL; //对struct SeqList 重定义 相当于struct SeqList = SL
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
void SLInit(SL* ps)
{
assert(ps); //此处使用断言,当ps为空时程序会出错
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLInit(SL* ps); 动态顺序表的初始化
void SLDesTroy(SL* ps) {
if (ps->arr !=NULL) {
free(ps->arr);
}
ps->capacity = ps->size = 0;
}
void SLDesTroy(SL* ps) ; 动态顺序表的销毁
void SLPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
void SLPrint(SL* ps); 动态顺序表的打印
void SLCheckCapacity(SL* ps) {
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = realloc(ps->arr, newCapacity * sizeof(SLDataType));
//realloc 的返回值用临时指针变量存储 确认扩容成功后再用arr接收
if(tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//扩容成功
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void SLCheckCapacity(SL* ps); 动态顺序表的扩容
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
//当空间不够的情况下,需要增容
SLCheckCapacity(ps);
//空间足够直接插入
ps->arr[ps->size++] = x;
}
void SLPushBack(SL* ps, SLDataType x); 动态顺序表的尾插
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 SLPushFront(SL* ps, SLDataType x); 动态顺序表的头插
void SLPopBack(SL* ps) {
assert(ps);
assert(ps->size);
//顺序表不为空
ps->size--;
}
void SLPopBack(SL* ps); 动态顺序表的尾部删除
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
//顺序表不为空 执行挪动操作
for(int i = 0; i < ps->size-1; i++)
{
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
void SLPopFront(SL* ps) ; 动态顺序表的头部删除
void SLInsert(SL* ps,int pos, SLDataType x)
{
assert(ps);
assert(pos >=0 && pos<= ps->size);
//检查是否需要扩容
SLCheckCapacity(ps);
//pos及之后的数据往后挪动,pos空出来
for(int i = ps->size;i>pos;i--)
{
pos->arr[i] = ps->arr[i-1];
}
ps->arr[pos] = x;
ps->size++;
}
void SLInsert(SL* ps,int pos, SLDataType x); 动态顺序表的指定位置插入
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >=0 && pos < ps->size);
//pos 以后的数据往前挪动一位
for( int i = pos; i <ps->size-1; i++)
{
ps->arr[i] = ps-> arr[i+1];
}
ps->size--;
}