目录
一、顺序表的概念
1.顺序表是线性表的一种,线性表包括:顺序表、链表、栈、队列、字符串...
2.顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构;
3.顺序表是对数组的封装,实现“增删查改”。
二、顺序表的分类
(1)静态顺序表
1.概念:使用定长数组存储元素;
2.缺点:空间少了不够用,空间多了浪费;
typedef int SLDatatype;
#define N 7
typedef struct Seqlist
{
SLDatatype arr[N];//定长数组
int size;//有效数据个数
}SL;
(2)动态顺序表
typedef int SLdatatype;
typrdef struct Seqlist
{
SLDatatype* arr;//数组
int capacity;//空间容量
int size;//有效数据个数
}SL;
三、顺序表的实现
(1)Seqlist.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//定义动态顺序表结构
typedef int SLDatatype;
typedef struct Seqlist
{
SLdatatype* arr;
int capacity;
int size;
}SL;
//初始化
void SLInit(SL*ps);
//销毁
void SLDestroy(SL*ps);
//打印
void SLPrint(SL* ps);
//插入数据
void SLPushFront(SL*ps, SLdatatype x);
void SLPushBack(SL*ps,SLDatatype x);
void SLInsert(Sl* ps, SLDatatype x ,int pos);
//删除数据
void SLPopFront(SL*ps);
void SLPopBack(SL*ps);
void SLErase(SL*ps, int pos);
//寻找数据
int SLFind(SL*ps,SLDatatype x);
(2)Seqlist.c
include "Seqlist.h"
//初始化
void SLInit(SL*ps)
{
ps->arr = NULL;
ps->size = PS->capacity = 0;
}
//销毁
void SLDestroy(SL*ps)
{
if(ps->arr != NULL)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//打印
void SLPrint(SL*ps)
{
for(int i = 0 ; i < ps->size ; i++)
{
printf("%d ",ps->arr[i]);
}
printf("\n");
}
//判断空间是否充足
void SLCapacityCheck(SL*ps)
{
if(ps->capacity == ps->size)
{
int nweCapacity = 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 SLPushFront(Sl*ps,int SLDatatype x)
{
assert(ps);
SLCapacityCheck(ps);
for(int i = ps->size - 1 ; i >= 0 ; i --)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->arr[0] = x;
ps->size++;
}
void SLPushBack(SL*ps , SLDatatype x)
{
assert(ps);
SLCapacityCheck(SL*ps);
ps->arr[ps->size] = x;
ps->size ++;
}
void SLInsert(SL*ps, SLDatatype x , int pos)
{
SLCapacityCheck(SL*ps);
assert(ps);
assert(pos >= 0 && pos <= ps->size);
for(int i = ps->size - 1 ; i >= pos ; i--)
{
ps->arr[i + 1] = ps->arr[i];
}
ps->arr[pos] = x;
ps->size++;
}
//删除数据
void SLPopFront(SL*ps)
{
assert(ps);
assert(ps->size);
for(int i = 1 ; i < ps->size ; i ++)
{
ps->arr[i - 1] = ps->arr[i];
}
ps->size--;
}
void SLPopBack(SL*ps)
{
assert(ps);
assert(ps->size);
ps->size --;
}
void SLErase(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 --;
}
//查找数据(返回位置)
int SLFind(SL*ps , SLDatatype x)
{
for(int i = 0 ; i < ps->size ; i++)
{
if(ps->arr[i] == x)
{
return i;
}
}
return -1;
}
(3)提示
初学时建议每写一个方法就进行测试(创建一个新的源文件test.c,这样不影响Seqlist.c),避免代码太多而不容易找到错误点。
四、顺序表算法题
1.移除元素
思路:创建两个变量src和dst指向数组第一个元素,由src查找等于val的元素,若不等于,将src指向的元素放入dst指向的位置中,dst向后走一位,以便放入新的元素,src继续向后查找。其中dst即为与val不同的元素个数。
int Find(int* nums , int numsSize , int val)
{
int src = 0;
int dst = 0;
while(src < numsSize)
{
if(nums[src] != val)
{
nums[dst] = nums[src];
dst ++;
}
src++;
}
return dst;
}
2.删除有序数组中的重复项
思路:创建两个变量dst、src分别指向数组第一个和第二个元素,由src遍历数组元素,若不等于,dst向后移一位,将src指向的元素放入dst位置,src继续遍历。其中dst+1为新数组的长度。
void Pop(int* nums , int numsSize)
{
int dst = 0;
int src = dst + 1;
while(src < numsSize)
{
if(nums[dst] != nums[src] && ++dst != src)
{
nums[dst] = nums[src];
}
src++;
}
return dst + 1;
}
3.合并两个有序数组
思路:创建变量l1、l2,分别指向nums1、nums2最后一个元素的位置,创建变量l3指向nums1数组的最后一个位置。让l1和l2指向的元素进行比较,将较大的一个放入l3指向的位置,直到l1或l2其中的一个越界时结束循环,若l2越界,还需将剩余的元素放入nums1的空位置。
void hebing(int* nums1 , int* nums2 , int m , int n )
{
int l1 = m - 1;
int l2 = n - 1;
int l3 = m + n - 1;
while(l1 >= 0 && l2 >= 0)
{
if(nums1[l1] < nums2[l2])
{
nums1[l3--] = nums2[l2--];
}
else
{
nums1[l3--] = nums1[l1--];
}
}
while(l2>= 0)
{
nums1[l3--] = nums2[l2--];
}
}
五、写在最后
总的来说,顺序表比数组要方便很多,但是存在一些问题:
1.在增容时需要申请新空间、拷贝数据、释放旧空间,会有不少的空间消耗;
2.增容一般为2倍增长,势必会有一定的空间浪费;
3.中间/头部的插入和删除,时间复杂度为O(n)。
如何解决这些问题呢?请期待下节:单链表~