C--顺序表

目录

 一、定义

二、特点和存储结构

1.数据库的存储结构

三、实现

1.顺序表的创建

2.顺序表的初始化

3.顺序表的扩容、尾插和头插

4.顺序表的尾删、头删

5.在指定位置插入、删除数据

6.顺序表的查找

7.顺序表的打印

8.顺序表的销毁

四、代码展示及测试

五、顺序表和数组的区别


 一、定义

  • 数据结构在内存中的表示通常有两种形式,一种是顺序存储,另一种是链式存储。线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表的数据元素,我们把这种存储形式存储的线性表称为顺序表。
  • 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

二、特点和存储结构

  1. 顺序表的逻辑结构物理结构是一致的。
  2. 顺序表中任意一个数据元素都可以随机存取,所以顺序表是一种随机存取的存储结构。

1.数据库的存储结构

三、实现

1.顺序表的创建

#define int INIT_CAPACITY 4;
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType * a;//指向顺序表开辟的基准空间
	int size;      //顺序表的长度(下标)
	int capacity;  //顺序表的容量
}SL;

2.顺序表的初始化

void SLInit(SL *ps)//这里要用指针,不然会出现传值操作
{
	ps->a = NULL;
    ps->capacity = ps->size = 0;
}
//刚开始顺序表什么都没有,所以定义为空

3.顺序表的扩容、尾插和头插

  • 在对一个空的顺序表进行操作时,需要经常对空间进行扩容等操作 ,所以我们可以将扩容操作单独写成一个函数,以便其他函数调用。
//空间的开辟
void SLCheckCapacity(SL* ps)
{
    //先判断顺序表是否为空
	if (ps->size == ps->capacity)
	{
		int newspace = (ps->capacity == 0 ? 4 : ps->capacity * 1.5);
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newspace * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("relloc invaild!");
			exit(-1);
		}
		ps->a = tmp;//a指向新开辟的空间
		ps->capacity = newspace;//容量指向新开辟的容量
	}
}
//顺序表的尾插
void SLPushBack(SL* ps,SLDataType x)
{
	//先检查空间是否需要扩容
	SLCheckCapacity(ps);

	//尾插
	ps->a[ps->size] = x;
	ps->size++;//下标加一
}
//顺序表的头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查是否扩容

	SLCheckCapacity(ps);
    
    //把每个数向后移动一位,把第一个位置留出来
	for (int i = ps->size; i > 0; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[0] = x;
	ps->size += 1;
}

4.顺序表的尾删、头删

//顺序表的尾删
void SLPopBack(SL* ps)
{
	//先检查顺序表里面是否有数
	assert(ps);
	assert(ps->size);

	ps->size--;
}
//顺序表的头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);
    
    //从0开始,前一位的值等于后一位,把第一位的遮盖住
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size -= 1;//下标减一
}

5.在指定位置插入、删除数据

//在指定位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos > 0 && pos < ps->size);

	SLCheckCapacity(ps);

    //从后面开始,到指定位置的后一个位置,往后移动
	for (int i = ps->size; i > pos; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[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; i++)
	{
		ps->a[i] = ps->a[i+1];
	}
	ps->size -= 1;
}

6.顺序表的查找

//顺序表的查找
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
		return -1;
	}
}

7.顺序表的打印

//顺序表的打印
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

8.顺序表的销毁

//顺序表的销毁
void SLDestory(SL* ps)
{
	if (ps->a)
	{
		free(ps->a);
	}
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

四、代码展示及测试

  • SeList.h
#pragma once

#include <stdio.h>
#include<stdlib.h>
#include<assert.h>

#define int INIT_CAPACITY 4;
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType * a;
	int size;
	int capacity;
}SL;

//初始化和销毁
void SLInit(SL * ps);
void SLDestory(SL* ps);

//打印
void SLPrint(SL* ps);

//开辟新空间
void SLCheckCapacity(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);

//查找
int SLFind(SL* ps, SLDataType x);
  • SeList.c
#include"Selist.h"

void SLInit(SL *ps)
{
	ps->a = NULL;
    ps->capacity = ps->size = 0;
}

void SLDestory(SL* ps)
{
	if (ps->a)
	{
		free(ps->a);
	}
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

void SLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newspace = (ps->capacity == 0 ? 4 : ps->capacity * 1.5);
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newspace * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("relloc invaild!");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newspace;
	}
}

void SLPushBack(SL* ps,SLDataType x)
{
	//先检查空间是否需要扩容
	SLCheckCapacity(ps);

	//尾插
	ps->a[ps->size] = x;
	ps->size++;
}

void SLPopBack(SL* ps)
{
	//先检查顺序表里面是否有数
	assert(ps);
	assert(ps->size);

	ps->size--;
}

void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查是否扩容

	SLCheckCapacity(ps);

	for (int i = ps->size; i > 0; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[0] = x;
	ps->size += 1;
}

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);

	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size -= 1;
}

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos > 0 && pos < ps->size);

	SLCheckCapacity(ps);

	for (int i = ps->size; i > pos; i--)
	{
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[pos] = x;
	ps->size++;
}

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos > 0 && pos < ps->size);
	for (int i = pos; i <ps->size; i++)
	{
		ps->a[i] = ps->a[i+1];
	}
	ps->size -= 1;
}

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
		return -1;
	}
}
  • test.c
#include"SeList.h"

void text1()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1,1);
	SLPushBack(&s1,2);
	SLPushBack(&s1,3);
	SLPopBack(&s1);
	SLPrint(&s1);
	SLPushFront(&s1, 2);
	SLPrint(&s1);
	SLPopFront(&s1);
	SLPrint(&s1);

	SLInsert(&s1, 1, 9);
	SLPrint(&s1);

	SLErase(&s1, 1);
	SLPrint(&s1);

	int x=SLFind(&s1, 9);
	if (x>=0)
	{
		printf("找到了,位置在%d", x);
	}
	else
	{
		printf("未找到");
	}
	
}

int main()
{
	text1();
	return 0;
}

五、顺序表和数组的区别

  • 顺序表的底层结构是数组,对数组的封装,实现了常用的增删查该接口。