目录
知识补充:
数据元素是数据的基本单位,通常作为一个整体进行处理。一个数据元素可由若干数据项组成,数据项是构成数据元素的不可分割的最小单位。例如,个人信息是一个数据元素,由姓名、性别、籍贯等数据项组成。
数据对象是具有相同性质的数据元素的集合。
数据类型是一个值的集合和定义在此集合上的一组操作的总称。(例如int类型,bool类型,结构体类型)
数据结构是相互之间存在一种或多种特定关系的数据元素的集合,包括3方面的内容:逻辑结构、储存结构和数据的运算。
一、线性表
线性表是n个具有相同特性的数据元素的有限序列。
其中,n为表长,当n=0时,线性表是一个空表。
除a1外,其他元素有且仅有一个直接前驱,除a(n)外,其他元素有且仅有一个直接后驱。
线性表在逻辑结构上是线性结构,是连续的,但是在物理结构上不一定是连续的。线性表在物理上储存时,分为顺序存储和物理存储,通常以数组和链式结构的形式存储。
线性表的特点:
- 元素个数有限
- 逻辑上是线性结构(即连续的一条线,但在物理结构上不一定连续),元素有先后次序。
- 表中元素是数据元素,每个元素都是单个元素。
- 元素的数据类型都相同,每个元素占有相同大小的储存空间。
二、顺序表
2.1 顺序表的概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般采用数组存储,在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组储存。
- 动态顺序表:使用动态开辟的数组储存。
2.2 顺序表的结构
2.2.1 静态顺序表结构
#define Size 100//定义顺序表的长度
typedef int SLDataType;//定义数据表的数据类型,要修改顺序表的数据类型时非常方便
typedef struct SeqList
{
SLDataType arr[Size];//顺序表的元素
size_t length;//顺序表的当前长度或有效数据的个数
}SqList;
2.2.2 动态顺序表结构
typedef int SLDataType;//定义数据表的数据类型,要修改顺序表的数据类型时非常方便
typedef struct SeqList
{
SLDataType* arr;//指向动态开辟的数组
size_t length;//顺序表的当前长度或有效数据的个数
size_t capicity;//容量空间的大小
}SqList;
2.3 接口实现
静态顺序表只适用于确定知道存储多少数据的场景,空间开多了浪费,开少了不够。所以现实中基本都是使用动态顺序表,根据需要动态地分配空间大小。
顺序表结构
//动态顺序表
typedef int SLDataType;//定义数据表的数据类型,要修改顺序表的数据类型时非常方便
typedef struct SeqList
{
SLDataType* a;//指向动态开辟的数组
size_t size;//顺序表的当前长度或实际存储数据的个数
size_t capacity;//容量空间的大小
}SL;
初始化顺序表
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
判断是否增容
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)//判断空间是否为0或者已满
{
size_t newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//如果容量为0,申请4个,否则申请容量的2倍
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));//realloc指向空指针的时候,作用和malloc相同
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);//退出
//此处不能用return-1,返回类型为void,就算返回类型为int,也只是退出这个当前函数
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
尾删
void SeqListPopBack(SL* ps)
{
//方式1
if (ps->size > 0)//防止越界
ps->size--;
//方式2
/*assert(ps->size > 0);//防止越界,如果条件为假,就会中止。
ps->size--;*/
//尾删删到最后是ps->a[ps->size],size为0,即使仍然指向这个元素,也不会打印了。
}
头插
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
//移动数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
头删
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
int begin = 1;
while(begin<ps->size)
{
ps->a[begin-1] = ps->a[begin];
begin++;
}
ps->size--;
}
查找
//查找,找到了返回下标,找不到,返回-1
int SeqListFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (x == ps->a[i])
return i;
}
return -1;
}
修改
void SeqListModify(SL* ps, SLDataType x, SLDataType y)
{
int ret = SeqListFind(ps, x);
if (ret != -1)
{
ps->a[ret] = y;
}
else
{
printf("要修改的数据不存在\n");
}
}
在指定的位置插入数据
//在指定的位置插入数据(根据下标)
void SeqListInsertPos(SL* ps, size_t n, SLDataType x)
{
assert(n >= 0 && n <= ps->size);
//考虑增容
SeqListCheckCapacity(ps);
//移动数据
for (int i= ps->size - 1;i>=n;i--)
{
ps->a[i+1] = ps->a[i];
}
ps->a[n] = x;
ps->size++;
}
删除指定数据
void SeqListDelete(SL* ps, SLDataType x)
{
//找到要删除数据的下标
int ret=SeqListFind(ps, x);
//往前移动数据
if (ret > 0)
{
for (int i = ret + 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
else if (ret = 0)
{
SeqListPopFront(ps);
}
else
printf("要删除的数据不存在\n");
}
完整代码:C-project: C语言的代码 - Gitee.com
2.4 顺序表相关题目
2.4.1 移除元素
力扣https://leetcode.cn/problems/remove-element/
思路1:
int removeElement(int* nums, int numsSize, int val){
for (int i = 0; i < numsSize; i++)
{
while (val == nums[i])
//此处while不能替换为if,否则无法判断移动后的数组的首个元素是否等于val,用if会跳过这个步骤。
{
for (int j = i; j < numsSize - 1; j++)
{
nums[j] = nums[j + 1];
}
numsSize--;
}
}
return numsSize;
}
运行结果:超出时间限制。
思路2:
int removeElement2(int* nums, int numsSize, int val)
{
int* tmp = (int*)malloc(numsSize * sizeof(int));
int n = 0;
//把不等于val的数放入临时数组
for (int i = 0; i < numsSize; i++)
{
if (val != nums[i])
{
tmp[n] = nums[i];
n++;
}
}
//把筛选出来的临时数组拷贝回去
for (int j = 0; j < n; j++)
{
nums[j] = tmp[j];
}
//释放空间
free(tmp);
tmp = NULL;
return n;
}
思路3:
int removeElement(int* nums, int numsSize, int val){
int src=0,dst=0;
while(src<numsSize&&dst<numsSize)
{
if(nums[src]==val)
{
src++;
}
else
{
nums[dst]=nums[src];
dst++;
src++;
}
}
return dst;
}
总结:思路3最符合题目要求。
2.4.2 删除有序数组中的重复项
力扣https://leetcode.cn/problems/remove-duplicates-from-sorted-array/
思路一时间开销太大,思路二不符合空间复杂度为O(1)的要求,且思路一和思路二的实现,与2.4.1的 思路一和思路二相似,此处不再重复实现。
int removeDuplicates(int* nums, int numsSize){
if(nums==NULL)
return 0;
int i=0,j=1,dst=0;
while(j<numsSize)
{
if(nums[i]==nums[j])
{
j++;
}
else
{
nums[dst]=nums[i];
dst++;
i=j;
j++;
}
}
nums[dst]=nums[i];
dst++;
return dst;
}
2.4.3 合并两个有序数组
力扣https://leetcode.cn/problems/merge-sorted-array/submissions/
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int i=m-1,j=n-1;
int x=m+n-1;
while(i<0&&j>=0)
{
nums1[x--]=nums2[j--];
}
while(i>=0&&j>=0)
{
if(nums1[i]<=nums2[j])
{
nums1[x--]=nums2[j--];
}
else
{
nums1[x--]=nums1[i--];
}
}
while(j>=0)//用j进行判断,不能用x,因为x有剩是2种情况。
{
nums1[x--]=nums2[j--];
}
}