一、顺序表。
1.定义
顺序表也称线性表,顺序表可以随机访问其存储数据。
2.结构样式(物理)
3.顺序表初始化代码
//定义结构体,确定描述顺序表的属性
typedef int SeqTabData;
typedef struct SeqTab
{
SeqTabData* p;//顺序表起始地址
int capacity;//顺序表容量
int size;//顺序表存储已数据个数
}ST;
//初始化顺序表
void SeqTabInit(ST* SeqTab)
{
SeqTab->p = (SeqTabData*)malloc(sizeof(SeqTabData) * 4);
SeqTab->capacity = 4;//初始容量
SeqTab->size = 0;
}
4.实现顺序表增删查改
举指定位置插入数据为例:
//检查是否满容量
int STFullCheck(ST*SeqTab)
{
if (SeqTab->size == SeqTab->capacity) {
return 1;
}
else {
return 0;
}
}
//扩容
void AddCapicity(ST* SeqTab)
{
SeqTabData* tmp = (SeqTabData*)realloc(SeqTab->p, sizeof(SeqTabData) * SeqTab->capacity *2);
if (tmp==NULL) {
printf("增容失败\n");
}
else {
printf("增容成功\n");
SeqTab->p = tmp;
SeqTab->capacity *= 2;
}
}
//指定位置插入数据
void SeqTabInsert(ST* SeqTab, int pos, int x)//传参:顺序表起始地址、指定位置下标、放置的数据
{
assert(pos < SeqTab->size);
//记录数组最后一个数据的位置
int i = SeqTab->size-1;
//检查数组是否需要扩容
int ret = STFullCheck(SeqTab);
//已满,需要扩容
if (ret) {
AddCapicity(SeqTab);
//将要插入位置后边数据从最后一项开始,向后挪动1个单位直至i=pos
for (;i <= pos;i--) {
SeqTab->p[i + 1] = SeqTab->p[i];
}
SeqTab->p[pos] = x;
//数据个数+1
SeqTab->size++;
}
//未满,不需要扩容,数据挪动如上
else {
for (;i >= pos;i--) {
SeqTab->p[i + 1] = SeqTab->p[i];
}
SeqTab->p[pos] = x;
SeqTab->size++;
}
}
总结方法:通过正确挪动数据,改变顺序表数据的相对位置,切记从何处数据开始挪动!
二、链表
1.定义
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
2.逻辑结构
3.链表的不同结构
1. 单向、双向
2. 带头、不带头
3. 循环、非循环
现主要讨论单向链表、带头双向循环链表!
4、链表初始化代码、增删查改代码思路
//单向链表基本属性
typedef int SLIST_TYPE;
typedef struct SignalList
{
struct SignalList* Next;//下一个节点的地址
SLIST_TYPE Data;//存储数据
}SLTNode;
//指定位置后部插入
void SLTPushAfter(SLTNode** pphead,SLTNode* pos, int x)
{
//创建新节点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->Data = x;
newnode->Next = NULL;
//判断是否头节点处尾插
if ((*pphead)==pos) {
newnode->Next = (*pphead)->Next;
(*pphead)->Next= newnode;
}
else {
//从头节点处开始判断是否此节点为pos
SLTNode* previous = *pphead;
while (previous!= pos) {
//迭代
previous = previous->Next;
}
//插入
newnode->Next = previous->Next;
previous->Next = newnode;
}
}
单向链表总结:总体而言,单向链表增删查改主要需要通过遍历整个链表,找到指定位置进行新节点的链接,切记前后指针地址的赋值顺序!防止出现找不到参与链接节点的地址!
带头双向循环链表
//基本属性
typedef int ListType;
typedef struct DoubleCirList
{
struct DoubleCirList* pre;前一个结点地址
ListType Data;存储数据
struct DoubleCirList* Next;后一个地址结点
}DCList;
//创建新节点
DCList* BuyDCList(ListType x)
{
DCList* newnode = (DCList*)malloc(sizeof(DCList));
newnode->Data = x;
newnode->pre =NULL;
newnode->Next = NULL;
return newnode;
}
//初始化哨兵节点
DCList* Initinfo()
{
DCList* phead = (DCList*)malloc(sizeof(DCList));
phead->Data = NULL;
phead->pre = phead;
phead->Next = phead;
return phead;
}
//指定位置前方插入
void DCListInsertBefore(DCList* pos, ListType x)
{
DCList* newnode = BuyDCList(x);
DCList* pre = pos->pre;//先保存前一个结点,后续方便找到
newnode->pre = pre;
newnode->Next = pos;
pos->pre = newnode;
pre->Next = newnode;
}
总结:相较于单向链表,带头双向循环链表不需要遍历便可以实现数据的增删查改!同样要记住地址的赋值顺序!
三、顺序表与链表的优缺点
顺序表:
优点:
空间连续、支持随机访问
缺点:
1. 中间或前面部分的插入删除时间复杂度O(N)
2. 增容的代价比较大。
链表:
缺点:
以节点为单位存储,不支持随机访问,时间复杂度为O(N)
优点:
1. 任意位置插入删除时间复杂度为O(1)
2. 没有增容消耗,按需申请节点空间,不用了直接释放。
总之两者互补!
四、小结
两者都为基本数据结构,用于存储数据。二者优缺点互补。打好顺序表链表基础,对以后复杂数据结构的实现有着举足轻重的作用!