数据结构与算法 (3)顺序表的增删查改

在今天的学习中,将会介绍线性表的概念,以及顺序表的增删查改功能的实现,以及分析利弊


在这里插入图片描述

线性表

线性表是n个具有相同特性的数据元素的有限序列,是一种广泛使用的数据结构,常用的线性表有:顺序表、链表、栈、队列等等
线性表在逻辑上是线性结构,也就是一条连续的直线,但是实际上他有可能是连续的,还有可能是链接在一起的,例如链表。

顺序表

顺序表是用一段连续的存储单元依次存储数据元素的线性结构,听着很像数组,数组是连续的,而且是n个相同元素的集合,而事实上也是如此,顺序表就是用数组存储的,在数组上完成增删查改的工作。
在这里插入图片描述

顺序表又分为静态顺序表和动态顺序表,静态顺序表就是使用定长数组存储元素,动态就是随时扩容,两个都不难,咱直接用写动态顺序表,就把静态包括了。

动态顺序表实现过程

首先我们要有一个数据元素类型,这里我们可以采用结构体,因为我们要存储顺序表的地址,顺序表的有效元素有多少,因为是需要扩容的,我们还要知道顺序表现在的容量有多少。

typedef int DataType;
typedef struct SeqList
{
	DataType* arr; //存储动态开辟数组首地址
	size_t size;   //有效数据个数
	size_t capicity; //容量
}SeqList;

这里还重命名了int,实际上这是为了顺序表的通用性着想,如果我们原本顺序表中储存的是int类型的数据,我们现在不想让他存int了,想让它存字符类型的数据,这时候我们不需要把所有原本是int类型的元素都改成char,只需要把上面的int重命名变成char的重命名就可以了。
下面就进入到顺序表功能的实现:

1. 顺序表的初始化
2. 顺序表的销毁
3. 顺序表扩容
4. 顺序表的头插
5. 顺序表的头删
6. 顺序表的尾插
7. 顺序表的尾删
8. 顺序表的查找
9. 任意位置插入
10. 任意位置的删除
11. 打印链表

1.顺序表的初始化

void SeqListInit(SeqList* ps)
{
  assert(ps);
  ps->arr= NULL;
  ps->capicity= 0;
  ps->size = 0;
}

这个没什么说的,我们让指向数组的指针先为空,并给其他值初始化。

2.顺序表的销毁

void SeqListDestroy(SeqList* ps)
{
  assert(ps);
  free(ps->arr);
  ps->arr = NULL;
  ps->capicity = ps->size = 0;
}

销毁同样很好理解,注意前面的assert 首先储存顺序表信息的结构体指针要存在,不存在怎么销毁呢?因此我们使用断言 ,然后我们释放顺序表的值,因为本来就是动态申请来的内存,用free就直接释放掉了,然后把顺序表的容量以及有效数据数量变为0.

3.顺序表的扩容

void CheckCacpity(SeqList *ps)
{
  if(ps->size == ps->capicity)
  {
    size_t newcapacity = ps->capicity == 0 ? 4 : ps->capicity*2;
    ps->arr = (DataType*)realloc(ps->arr,newcapacity * sizeof(DataType));
    ps->capicity = newcapacity;
  }
}

这个要解释一下,realloc是什么意思,我们知道malloc是申请一片空间,然后把地址存下来,relloc是什么呢,relloc是对你原有的空间进行扩容,如果当前所在的这片空间装不下了,那就是换到另一片重充足的空间,然后把空间原有的内容拷贝进去。

在这里插入图片描述
在这个函数中就是,如果有效数字和容量相同了,这时候代表我们的空间已经满了,这就需要扩容,扩多少呢?扩大一倍。

4.顺序表任意位置插入

我们先不去看头插和尾插,我们先来看任意位置的插入,任意位置的插入知道了,那头插不就是第一个位置的插入和最后一个位置的插入吗,直接复用任意位置插入函数就可以了。

void SeqListInsert(SeqList* ps,size_t pos,DataType x)
{
  assert(ps);
  assert(pos <= ps->size);

  CheckCacpity(ps);
  size_t end = ps->size;
  while(end > pos)
  {
    ps->arr[end] = ps->arr[end -1];
    --end;
  }
  ps->arr[pos] = x;                                                                                                                                                                       
  ps->size++;
}
 

在每次插入操作之前我们都要扩容,检测一下顺序表中的容量还够不够了。顺序表的插入很简单,就是从后往前,把pos后的顺序表整个后移一位
在这里插入图片描述
大概就这样,最后让pos位置的数据等于我们给的值,就可以了。

5.任意位置的删除

先说整个的原因和之前一样,方便复用。

void SeqListErase(SeqList* ps,size_t pos)
{
   assert(ps);
   assert(pos < pa->size);

   size_t start = pos+1;
   while(start < ps->size)
   {
     ps->arr[start - 1] = ps->arr[start];
     ++start;
   }

   ps->size--;
}

原理和插入差不多,只不过这回我们是让后一个数据覆盖前一个数据,这样到pos位置,pos位置的数据就被覆盖了。

6.头插

因为之前写了任意位置的插入,那现在就可以复用我们写过的函数了

void SeqlistPushFront(SeqList* ps,DataType x)
{
  assert(ps);
  SeqListInsert(ps, 0,x);
}

7.尾插

void SeqListPushBack(SeqList* ps, DataType x)
{
  SeqListInsert(ps,ps->size,x);
}

8.头删

void SeqListPopFront(SeqList* ps)
{
  assert(ps);
  SeqListErase(ps,0);
}

9.尾删

void SeqListPopBack(SeqList*ps)
{
  assert(ps);

  SeqListErase(ps,ps->size-1);
}

10.查找

int SeqListFind(SeqList* ps,DataType x)
{
  for(size_t i = 0; i < ps->size ;++i)
  {
    if(ps->arr[i]==x)
    {
      return i;
    }
  }
  return -1;
}

查找就遍历一遍我们的顺序表就可以了,找到就返回下标,找不到就返回-1

11.打印

void SeqListPrint(SeqList* ps)
{
  assert(ps);
  for(size_t i = 0;i < ps->size ; ++i)
  {
    printf("%d",ps->arr[i]);
  }
  printf("\n");
}

也很简单,就是迭代打印。

测试

代码写完了,我们总要测试一下吧

#include"SeqList.h"    
int main()    
{    
  SeqList ps  ;    
  SeqListInit(&ps);    
  SeqListInsert(&ps,0,1);    
  SeqListPushBack(&ps,2);    
  SeqListPushBack(&ps,2);    
  SeqListPushBack(&ps,2);    
  SeqListPushBack(&ps,2);    
  SeqListPrint(&ps);                                                                                                                                                                      
  return 0;    
}  

如果代码是正确的,应该打印出来12222,现在我们运行一下
在这里插入图片描述
可以看到啊,当我们编译完成运行的时候,他确实是12222,那我们的代码就成功了。

总结(顺序表优缺点)

那么到现在我们已经完成了顺序表的增删查改等功能的实现,有没有发现顺序表的优缺点呢?
优点:
可以直接访问到顺序表中的任意下标数据
缺点:
扩容时有可能要拷贝到新空间,释放旧空间,对空间消耗大,并且在我们扩容的时候,不一定所有空间都利用上了,可能会造成空间的浪费。

源码

SeqList.h

#ifndef __SeqList_h    
#define __SeqList_h     
#include<stdio.h>    
#include<stdlib.h>    
#include<assert.h>    
    
typedef int DataType;    
typedef struct SeqList    
{    
  DataType* arr; //存储动态开辟数组首地址    
  size_t size;   //有效数据个数    
  size_t capicity; //容量    
}SeqList;    
void SeqListInit(SeqList* ps);    
void SeqListDestroy(SeqList* ps);    
void CheckCacpity(SeqList *ps);    
void SeqListPushBack(SeqList* ps, DataType x);    
void SeqlistPushFront(SeqList* ps,DataType x);    
void SeqListPopFront(SeqList* ps);    
void SeqListPopBack(SeqList*ps);    
void SeqListPrint(SeqList* ps);                                                                                                                                                           
int SeqListFind(SeqList* ps,DataType x);    
    
void SeqListInsert(SeqList* ps,size_t pos,DataType x);    
    
void SeqListErase(SeqList* ps,size_t pos);    
#endif   

Seqfun.c

#include"SeqList.h"
void SeqListInit(SeqList* ps)
{
  assert(ps);
  ps->arr= NULL;
  ps->capicity= 0;
  ps->size = 0;
}
void SeqListDestroy(SeqList* ps)
{
  assert(ps);
  free(ps->arr);
  ps->arr = NULL;
  ps->capicity = ps->size = 0;
}
                                                                                 
void SeqListPrint(SeqList* ps)
{
  assert(ps);
  for(size_t i = 0;i < ps->size ; ++i)
  {
    printf("%d",ps->arr[i]);
  }
  printf("\n");
}

void CheckCacpity(SeqList *ps)
{
  if(ps->size == ps->capicity)
  {
    size_t newcapacity = ps->capicity == 0 ? 4 : ps->capicity*2;
    ps->arr = (DataType*)realloc(ps->arr,newcapacity * sizeof(DataType));
    ps->capicity = newcapacity;                                                                                                                                                           
  }
}
void SeqListPushBack(SeqList* ps, DataType x)
{
  SeqListInsert(ps,ps->size,x);
}                                                                                                                                                                                         
void SeqlistPushFront(SeqList* ps,DataType x)
{
  assert(ps);
  SeqListInsert(ps, 0,x);
}
void SeqListPopFront(SeqList* ps)
{
  assert(ps);
  SeqListErase(ps,0);
}
void SeqListPopBack(SeqList*ps)
{
  assert(ps);

  SeqListErase(ps,ps->size-1);
}
                                                                                 
int SeqListFind(SeqList* ps,DataType x)
{
  for(size_t i = 0; i < ps->size ;++i)
  {
    if(ps->arr[i]==x)
    {
      return i;
    }
  }
  return -1;
}
void SeqListInsert(SeqList* ps,size_t pos,DataType x)
{
  assert(ps);
  assert(pos <= ps->size);                                                                                                                                                                

  CheckCacpity(ps);
  size_t end = ps->size;
  while(end > pos)
  {
    ps->arr[end] = ps->arr[end -1];
    --end;
  }
  ps->arr[pos] = x;
  ps->size++;
}
                                                                           
void SeqListErase(SeqList* ps,size_t pos)
{
   assert(ps);
   assert(pos < ps->size);

   size_t start = pos+1;
   while(start < ps->size)
   {
     ps->arr[start - 1] = ps->arr[start];
     ++start;
   }

   ps->size--;
}

SeqList.c

#include"SeqList.h"    
int main()    
{    
  SeqList ps  ;    
  SeqListInit(&ps);    
  SeqListInsert(&ps,0,1);    
  SeqListPushBack(&ps,2);    
  SeqListPushBack(&ps,2);    
  SeqListPushBack(&ps,2);    
  SeqListPushBack(&ps,2);    
  SeqListPrint(&ps);    
  return 0;                                                                                                                                                                               
}    

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值