✨✨小新课堂开课了,欢迎欢迎~✨✨
🎈🎈养成好习惯,先赞后看哦~🎈🎈
所属专栏:http://t.csdnimg.cn/oHJAK(数据结构与算法)
小新的主页:编程版小新-CSDN博客
1.什么是顺序表
顺序表的底层是数组,但它提供了很多现成的方法,开箱即用,就成了顺序表。简单来理解顺序表其实就是数组的升级版。顺序在物理结构上不一定连续,但在逻辑结构上是连续的,可以抽象成一个直线 。
2.顺序表的分类
顺序表可以分为静态顺序表和动态顺序表。
2.1顺序表的定义
静态顺序表:
#define N 100struct SeqList
{int arr[N];int size;//有效数据个数
};
动态顺序表:
typedef int SLDataType;//为了方便更改类型
typedef struct SeqList
{SLDataType* arr;int size;//有效数据个数int capacity;//空间大小
}SL;
静态顺序表的数组是定长的,给小了不够用,给大了浪费空间,而动态顺序表是可以动态增容的,相对于静态顺序表更加的灵活和节省空间,所以也更推荐使用动态顺序表。
3.顺序表的功能
1.顺序表的初始化
2.对顺序表进行尾插(在末尾处插入数据)
3.对顺序表进行尾删(删除末尾处的数据)
4.对顺序表进行头插(在开始位置插入数据)
5.对顺序表进行头删(删除在起始位置的数据)
6.在指定位置之前插入数据
7.删除指定位置的数据
8.打印顺序表
9.查找顺序表
10.修改顺序表
11.销毁顺序表
4.顺序表功能的实现
4.1顺序表的初始化
//顺序表的初始化
void SLInit(SL* ps)//传址调用
{ps->arr = NULL;ps->size = ps->capacity = 0;
}
易错点:这里一定要采用传址调用,因为采用传值调用的话,形参只是对实参的一份临时拷贝,出了{}以后就被销毁了。
4.2顺序表的尾插
在说明顺序表的尾插之前,我们要先想一个问题,我们既然要插入数据,就要考虑空间够不够的问题,所以数据在进行插入之前要检查空间够不够。
//检查是否用足够的空间
void SLCheck(SL* ps)
{if (ps->size == ps->capacity)//空间不够{//申请空间//三目操作符int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));//需要申请多大的空间if (tmp == NULL){perror("realloc fail");return;}//申请成功ps->arr = tmp;ps->capacity = newcapacity;}}
动态增容的时候,一般是成倍的扩增,采用2倍扩增是比较合适的。
这里大家可能会有疑问,这样开辟空间不会浪费空间吗?
答案是会的,但是这样浪费的空间相对于静态开辟的空间好一些。
在开辟空间的时候为什么要*sizeof(类型),因为单位是字节,要*sizeof(类型)才能得出真正所需要的空间大小。
判断了空间够不够之后,我们看一看如何进行尾插吧。
//顺序表的尾插
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);//插入之前要检查是否有空间SLCheck(ps);//插入ps->arr[ps->size++] = x;//细节之后置++
}
后置++,在这里使用的是真的很不错。
4.3顺序表的尾删
//顺序表的尾删
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);//顺序表不能为空--ps->size;
}
4.4顺序表的头插
//头插
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//插入之前要检查是否有足够的空间SLCheck(ps);//先让顺序表中已有的数据整体往后移动一位for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;//不要忘记++
}
这里之所以是从后往前移动是为了避免数据被覆盖。
4.5 顺序表的头删
//顺序表的头删
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--;
}
4.6在指定位置之前插入数据
//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);//插入之前要检查空间够不够SLCheck(ps);//先让pos及其之后的数据整体往后挪动一位for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];//如果不知道结束条件是什么就可以用最后一次挪动去推//ps->arr[pos+1]=ps->arr[pos]}ps->arr[pos] = x;ps->size++;
}
4.7在指定位置删除数据
//删除指定位置的数据
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--;}
4.8打印顺序表
void SLPrint(SL ps)
{for (int i = 0; i < ps.size; i++){printf("%d ", ps.arr[i]);}printf("\n");
}
4.9查找顺序表
//查找指定数据
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;}
4.10修改顺序表
//修改指定位置的数据
void SeqListModify(SL* ps, int pos, SLDataType x)
{assert(ps); //断言assert(ps->size > 0); //顺序表不能为空assert(pos >= 0 && pos < ps->size); //检查pos下标的合法性ps->arr[pos] = x; //修改pos下标处对应的数据
}
4.11销毁顺序表
//顺序表的销毁
void SLDestroy(SL* ps)
{if (ps->arr != NULL)//ps->arr{free(ps->arr);}ps->arr = NULL;//避免访问野指针ps->size = ps->capacity = 0;
}
5.完整代码
5.1SeqList.c
#include"SeqList.h"
//顺序表的初始化
void SLInit(SL* ps)//传址调用
{ps->arr = NULL;ps->size = ps->capacity = 0;
}
//顺序表的销毁
void SLDestroy(SL* ps)
{if (ps->arr != NULL)//ps->arr{free(ps->arr);}ps->arr = NULL;//避免访问野指针ps->size = ps->capacity = 0;
}//检查是否用足够的空间
void SLCheck(SL* ps)
{if (ps->size == ps->capacity)//空间不够{//申请空间//三目操作符int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//少了等号。一个是赋值SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));//需要申请多大的空间if (tmp == NULL){perror("realloc fail");return;}//申请成功ps->arr = tmp;ps->capacity = newcapacity;}}//打印顺序表
void SLPrint(SL ps)
{for (int i = 0; i < ps.size; i++){printf("%d ", ps.arr[i]);}printf("\n");
}//顺序表的尾插
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);//插入之前要检查是否有空间SLCheck(ps);//插入ps->arr[ps->size++] = x;//细节之后置++
}//头插
void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//插入之前要检查是否有足够的空间SLCheck(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, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);//插入之前要检查空间够不够SLCheck(ps);//先让pos及其之后的数据整体往后挪动一位for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1];//ps->arr[pos+1]=ps->arr[pos]}ps->arr[pos] = x;ps->size++;
}//删除指定位置的数据
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--;}
//顺序表的尾删
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size);//顺序表不能为空--ps->size;
}//顺序表的头删
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 SeqListModify(SL* ps, int pos, SLDataType x)
{assert(ps); //断言assert(ps->size > 0); //顺序表不能为空assert(pos >= 0 && pos < ps->size); //检查pos下标的合法性ps->arr[pos] = x; //修改pos下标处对应的数据
}//查找指定数据
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;}
5.2SeqList.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义顺序表//静态顺序表
//#define N 100
//
//struct SeqList
//{
// int arr[N];
// int size;//有效数据个数
//};//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{SLDataType* arr;int size;//有效数据个数int capacity;//空间大小
}SL;//顺序表的初始化
void SLInit(SL* ps);//顺序表的销毁
void SLDestroy(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 SLPrint(SL ps);//查找数据
int SLFind(SL* ps, SLDataType x);//修改指定位置的数据
void SeqListModify(SL* ps, int pos, SLDataType x);
下课了~
别忘了下次再来哦