一.线性表
二.顺序表
1. 概念及结构
定义:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数
组存储,在数组上完成数据的增删查改。
2.分类与特点
顺序表一般可以分为:
1). 静态顺序表:通过使用定长数组来存储数据。
2). 动态顺序表:通过使用动态开辟的数组存储数据。
静态顺序表:
优点:
1. 支持[]直接访问,查找元素方便。
2. 易于实现,操作简单。
缺点:
1. 数组的大小受到限制,必须提前规定好,无法进行扩容操作。
2. 在进行增删查改操作时需要频繁进行元素的移动,时间消耗与算法效率较为低下。
因此,一般在实际应用时,我们都采取动态顺序表而非静态顺序表。
三.具体接口实现
具体相关接口如下:
#pragma once
#include <stdio.h>
#include<stdlib.h>
#include <assert.h>typedef int sldatatype;
typedef struct Seqlist
{sldatatype* arr;sldatatype size;sldatatype capacity;
} sl;
void slinit(sl*);//初始化
void sldestroy(sl*);//销毁void slprint(sl*);//打印
void checkcapacity(sl*);//判断空间是否足够
void slpushback(sl*, sldatatype);//尾插
void slpushfront(sl*, sldatatype);//头插void slpopfront(sl*);//头删
void slpopback(sl*);//尾删//在指定位置插入和删除数据
void slinsert(sl*, sldatatype, int);
void slfrase(sl*, int);//查找指定数据int slfind(sl*, sldatatype);
首先我们定义一个结构体,里面包含一个int*的指针来充当数组存储数据
size用来存储数组元素个数。
capacity用来表示当前数组容积。
1.顺序表的初始化与销毁
void slinit(sl* ps)
{assert(ps);ps->arr = NULL;ps->capacity = ps->size = 0;
}void sldestroy(sl*ps)
{assert(ps);if (ps->arr){free(ps->arr);ps->arr = NULL;}ps->capacity = ps->size = 0;
}
分析:
1.首先暴力断言保证程序的安全性,避免传入空指针。
2.初始化时将数组置为空,并将数组的size和capacity置为0。
3. 销毁时需要注意由于每一个空间都是动态申请的,我们需要依次释放,避免引起内存泄漏。
4. 另外不可以直接释放数组arr,否则会导致后续元素无法访问。
为了后续便于测试,我们先构建一个打印函数。
void slprint(sl* ps)
{assert(ps);int i = 0;for (i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}
}
2.顺序表的插入
注意:
1.每次插入前都需要检查当前数组容积是否足以继续容纳!
2.在扩容时不可以用arr直接接收开辟的空间,否则一旦开辟失败,原先的arr也会被置为空,会导致后续也无法访问!
扩容操作检查如下:
void slcheckcapacity(sl* ps)
{assert(ps);if (ps->capacity == ps->size){//增容//若capacity为0,给个默认值,否则乘以2int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;sldatatype* tmp = (sldatatype*)realloc(ps->arr, 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;
}
在确认可以继续插入后,直接将数据置于数组末尾并更新元素个数即可。
头插:
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 slinsert(sl* ps, sldatatype x, int pos)
{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++;
}
此处的pos即为要插入节点的下标,插入的元素位于该节点之后。同样也需要移动元素。
3.顺序表的删除
注意:删除数据一定要检查数组元素个数是否为0,即size是否等于0!
尾删:
void slpopback(sl* ps)
{assert(ps && ps->size);ps->size--;//ps->arr[ps->size] = 0;多余了,没有必要
}
头删:
void slpopfront(sl* ps)
{assert(ps && ps->size);for (int i = 0; i <ps->size-1 ; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
在删除后需要将size--。
在指定位置删除:
void slfrase(sl* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);//包含了顺序表不为空的限定条件for (int i = pos; i <ps->size-1 ; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
该原理与在指定位置插入类似,在此不做过多阐述。
4.顺序表查找指定数据:
int slfind(sl* ps, sldatatype x)
{assert(ps);int i = 0;int flag = 0;for (i = 0; i < ps->size; i++){if(ps->arr[i]==x){flag = 1;break;}}if (flag)return 1;else{return -1;}
}
直接遍历查找即可,查找成功返回1,查找失败返回-1。
也可将函数返回值设置为布尔类型,或者sl*,直接返回要查找节点的地址。
总结:以上就是顺序表的大致内容讲解,快来看我的下一篇链表吧!