顺序表
文章目录
一:前言(初步认识)
- 1.1 顺序表概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用 数组 存储。在数组上完成数据的增删查改。- 1.2 顺序表一般可以分为:
a. 静态顺序表:使用定长数组存储。
b. 动态顺序表:使用动态开辟的数组存储。
逻辑上线性的,物理上顺序的
二:代码实现
A:顺序表的两种定义方式
首先定义一个全局变量
typedef int SDataType;
- a.定义静态存储结构
typedef struct SeqList{
//静态顺序表定义
int array[100]; // 能存 100 个数的静态顺序表
int size; //当前顺序表中存放的数,顺序表为空size=0
//顺便表示了即将插入的下标
} Seqlist;
- b.定义动态存储结构
typedef struct SeqList{
//动态顺序表定义
SDataType *array;
int size; //当前顺序表中存放的数,顺序表为空size=0,当顺序表不为空是表示数据中元素的下标
int capacity; //顺序表的容量
} SeqList;
B:顺序表的初始化与销毁
a:初始化
void SeqListInit(SeqList *seqlist, int capacity){
assert(seqlist != NULL);
seqlist->array = (SDataType*)malloc(sizeof(SDataType)*capacity);
//动态申请存储空间
seqlist->size = 0;//表示的是顺序表(数组中)可以读到的元素个数
seqlist->capacity = capacity;
}
- 首先需要申请空间,而参数中的 capacity 指的是你在初始化时所需要的空间大小。
b:销毁建立的顺序表
- 代码
void SeqListDestroy(SeqList *seqlist){
assert(seqlist != NULL);
assert(seqlist->array!=NULL);
seqlist->array = NULL;
seqlist->size = 0;
seqlist->capacity = 0;
free(seqlist->array);
//注意将申请的空间释放掉,避免造成内存泄漏
}
- 辅助理解
- 在销毁时只需要将指向定义数组的指针设置为空(而非将顺序表中的元素一个一个的往掉删除),并且将记录顺序表中元素数量的 size 置为 0
- 将申请的空间释放。(必须释放,不然会造成内存泄漏)
C:增(往顺序表中添加元素)
- 1.头插(注意将数组中的元素往后移动的过程中要避免发生元素的覆盖现象)
- 2.中间插(注意数组中有效数组的位置,避免覆盖)
- 3.尾插(直接插入到数组中最后一个有效元素的后面)
a:头插
- 代码
//头插
//1.从后往前搬,避免覆盖
//2.写循环
// 先确定循环的边界
// i 空间下标[size,0) //数组中空格位置的下标
// i 数据下标[size-1,0]
//3. 搬移
// i对应空间下标: array[i]=array[i-1];
// i对应数据下标: array[i+1]=array[i];
void SeqListPushFront(SeqList *seqlist, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
for (int i = seqlist->size ; i > 0; i--){
//做数据的搬移,i代表空间下标
seqlist->array[i] = seqlist->array[i - 1];
}
seqlist->array[0] = value;
seqlist->size++;
}
- 辅助理解
- 在进行头插运算时,需要考虑将原来顺序表中的元素从最后一位开始往后挪动一位,防止数据的丢失。
- (若将数组中的元素从第一位开始移动,那么往后的元素全都会被第一位所覆盖)
- 如下图中将value进行头插的时侯,应按顺序将数组中的元素往后移,然后再将value的值插入到数组的首元素位置。
b:尾插
- 代码
void SeqListPushBack(SeqList *seqlist, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
seqlist->array[seqlist->size] = value;
seqlist->size++;
}
//尾插时直接将 value 的值插入到顺序表的最后一个元素后面即可。
- 时间复杂度
操作 | 时间复杂度 |
---|---|
尾插 | O(1) |
头插 | O(N) |
c: 从指定的位置 (position)处插入value
- 动图演示
- 代码示例
void SeqListInsert(SeqList *seqlist, int pos, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
assert(pos >= 0 && pos <= seqlist->size); //检查要插入的位置是否合法
for (int i = seqlist->size - 1; i >= pos; i--){
seqlist->array[i+1] = seqlist->array[i];
}
//做数组搬移,将pos位置往后的元素按顺序依次从往后移动一位
seqlist->array[pos] = value;
seqlist->size++;
}
D: 删(从顺序表中删除指定的元素)
a:头删
- 删除的要点:从空间下标的第二个元素开始依次的往前移动一位,将自己前面的元素覆盖
- 动图演示
- 代码示例
//头删
//1.从后往前搬,避免覆盖
//2.写循环
// 先确定循环的边界
// i 空间下标 [0,size-2]
// i 数据下标 [1,size-1]
//3. 搬移
// i对应空间下标: array[i-1]=array[i];
// i对应数据下标: array[i]=array[i+1];
void SeqListPopFront(SeqList* seqlist){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
assert(seqlist->size > 0);
//i 代表的是空间下标
for (int i = 1; i <seqlist->size ; i++){
seqlist->array[i - 1] = seqlist->array[i];
}
seqlist->size--;
}
b:中间删除(指定位置删除某位置元素)
- 动图演示
- 代码示例
//删除 pos 所在的下标数据
void SeqListErase(SeqList *seqlist, int pos){
assert(seqlist!=NULL);
assert(seqlist->array!=NULL);
assert(seqlist ->size>0);
assert(pos >= 0 && pos < seqlist->size);
//做数据的搬移,因为是将指定位置的元素删除既可直接将其覆盖
for (int i = pos; i <seqlist->size; i++){
seqlist->array[i] = seqlist->array[i+1];
}
seqlist->size--;
}
c:尾删
- 操作说明:顺序表的尾删,直接将记录顺序表中有效个数的值size-1(既访问顺序表元素的时候能访问到的元素个数减少了)
- 代码示例
void SeqListPopBack(SeqList*seqlist){gai
assert(seqlist!=NULL);
assert(seqlist->array!=NULL);
assert(seqlist->size > 0);
//检查删除的数组中是否还有元素存在
seqlist->size--;
}
E:改(修改pos 所在下标的所在数据)
- 操作说明:将顺序表中指定位置的元素替换成指定的值即可
- 代码示例
//修改pos 所在下标的所在数据为 value
void SeqListModify(SeqList *seqlist, int pos, SDataType value){
assert(pos>=0&&pos<seqlist->size);
seqlist->array[pos] = value;
}
F:查(查找指定元素)
a:查找
- 代码示例
//查找
//如果找到,返回第一个找到的下标
//如果没找到返回-1
int SeqListFind(const SeqList *seqlist, SDataType value){
assert(seqlist!=NULL);
for (int i - 0; i < seqlist->size;i++){
if (seqlist->array[i] == value)
return i;
}
return -1;
}
b:找到并删除第一个遇到的 value
- 代码示例
void SeqListRemove(SeqList *seqlist, SDataType value){
int pos = SeqListFind(sqlist,value);
if (pos != -1){
SeqListErase(seqlist,pos);
}
}
- 以上即为数据结构顺序表中最基本的操作增 、删、改、查四种操作的代码示例。
尽管理解起来比较抽象,但是只用自己敲一遍,并且理解透彻的话。你会发现也没有想象中的那么难以理解。
删除顺序表中遇到的所有value
- 整体思想
- 1.先申请一个与原数组相同类型和大小的空间array
- 2.将原数组中元素和指定的值进行比较,将不相等的元素放到空间array中
- 3.之后将新申请数组中的元素依次赋值给原先的数组并将新申请的空间释放掉
-
动图演示
-
代码
void SeqListRemoveALl(SeqList* seqlist,SDataType value){
SDataType *array=(SDataType *)malloc(sizeof(SDataType)*seqlist->_size);
int index=0;
for(int i=0;i<seqlist->_size;i++){
if(seqlist->array[i]!=value){
array[index]=seqlist->array[i];
index++;
}
}
for(ing j=0;j<index;j++){
seqlist->array[j]=array[j];
}
free(array);
seqlist->size=index;
}
冒泡排序
void Swap(int *a,int *b){
int t=*a;
*a=*b;
*b=t;
}
void BubbleSort(int array[],int size){
for(int i=0;i<size-1;i++){
//一次冒泡
for(int j=1;j<size-1-i;j++){
if(array[i]>array[i+1]){
Swap(array+j+1.array+j);
}
}
}
}
冒泡排序的优化
- 优化思想:在经过依次排序后若指定的标志量的值没有改变说明该数组已经是有序的了,就不需要再进行排序了
void Swap(int *a,int *b){
int t=*a;
*a=*b;
*b=t;
}
void BubbleSort(int array[],int size){
int isSorted;
for(int i=0;i<size-1;i++){
isSort=1;
//一次冒泡
for(int j=1;j<size-1-i;j++){
if(array[i]>array[i+1]){
Swap(array+j+1.array+j);
isSort=0;
//如果整个序列是有序的,那么就不进行交换,则
//isSort=1;
}
}
if(isSort==1)
break;
}
}
二分查找
int BinarySearch(int array[],int size,int value){
int left=0;
int right=size-1;
int mid= (left+right)/2+left;
while(left<right){
if(array[mid]==value;
return mid;
if(array[mid]<value)
left=mid+1;
if(array[mid]>value)
right=mid;
}
return -1;
}
三:整体实现
main.c
#include"SeqList.h"
void TestSeqList(){
SeqList seqlist;
SeqListInit(&seqlist,100);
SeqListPushBack(&seqlist,1);
SeqListPushFront(&seqlist,11);
SeqListPushBack(&seqlist,2);
SeqListPushFront(&seqlist,12);
SeqListPushBack(&seqlist,3);
SeqListPushFront(&seqlist,13);
SeqListInsert(&seqlist,4,100);
SeqListPopBack(&seqlist);
SeqListPopFront(&seqlist);
SeqListErase(&seqlist, 3);
SeqListPrint(&seqlist);
SeqListDestroy(&seqlist);
}
int main(){
TestSeqList();
system("pause");
return 0;
}
SeqList.c
#include"SeqList.h"
//扩容
//条件:当size=capacity时说明顺序表已满
//函数前面加个static是只能在这个编译单元中访问(自己内部使用)
static void CheckCapccity(SeqList *seqlist){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
assert(seqlist->size <= seqlist->capacity);
if (seqlist->size < seqlist->capacity){
return;
}
//走到这里,需要扩容
int capacity = 2 * seqlist->capacity;
SDataType *array = (SDataType *)malloc(sizeof(SDataType)*capacity);
assert(array);
//把老数据搬过来
for (int i = 0; i < seqlist->capacity; i++){
array[i] = seqlist->array[i];
}
//释放老空间
free(seqlist->array);
seqlist->array = array; //数组代表数组的首地址
}
//顺序表的初始化
void SeqListInit(SeqList *seqlist, int capacity){
assert(seqlist != NULL);
seqlist->array = (SDataType*)malloc(sizeof(SDataType)*capacity);
assert(seqlist->array);
seqlist->size = 0;
seqlist->capacity = capacity;
}
//顺序表的销毁
//只管将数组的首地址置为空,将表示数组的数目size置为0即可
//并且将原来初始化顺序表时申请的空间释放即可
void SeqListDestroy(SeqList *seqlist){
assert(seqlist != NULL);
assert(seqlist->array!=NULL);
seqlist->array = NULL;
seqlist->size = 0;
seqlist->capacity = 0;
free(seqlist->array); //重点
}
//尾插,不需要搬移数组中的元素
void SeqListPushBack(SeqList *seqlist, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
CheckCapccity(seqlist);
seqlist->array[seqlist->size] = value;
seqlist->size++;
}
//头插
void SeqListPushFront(SeqList *seqlist, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
CheckCapccity(seqlist);
//做数据的搬移,i代表空间下标
for (int i = seqlist->size ; i > 0; i--){
seqlist->array[i] = seqlist->array[i - 1];
}
//注意循环的条件
seqlist->array[0] = value;
seqlist->size++;
}
//中间插入
void SeqListInsert(SeqList *seqlist, int pos, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
assert(pos >= 0 && pos <= seqlist->size);
CheckCapccity(seqlist);
for (int i = seqlist->size - 1; i >= pos; i--){
seqlist->array[i+1] = seqlist->array[i];
}
seqlist->array[pos] = value;
seqlist->size++;
}
//尾删
void SeqListPopBack(SeqList*seqlist){
assert(seqlist!=NULL);
assert(seqlist->array!=NULL);
assert(seqlist->size > 0);
seqlist->size--;
}
//头删
//1.从后往前搬,避免覆盖
//2.写循环
// 先确定循环的边界
// i 空间下标 [0,size-2]
// i 数据下标 [1,size-1]
//3. 搬移
// i对应空间下标: array[i-1]=array[i];
// i对应数据下标: array[i]=array[i+1];
void SeqListPopFront(SeqList* seqlist){
assert(seqlist != NULL);
assert(seqlist->array != NULL);
assert(seqlist->size > 0);
for (int i = 1; i <seqlist->size ; i++){
seqlist->array[i - 1] = seqlist->array[i];
}
seqlist->size--;
}
//删除 pos 所在的下标数据
void SeqListErase(SeqList *seqlist, int pos){
assert(seqlist!=NULL);
assert(seqlist->array!=NULL);
assert(seqlist ->size>0);
assert(pos >= 0 && pos < seqlist->size);
for (int i = pos; i <seqlist->size; i++){
seqlist->array[i] = seqlist->array[i+1];
}
seqlist->size--;
}
//打印
void SeqListPrint(const SeqList *seqlist){
for (int i = 0; i < seqlist->size; i++){
printf("%d ", seqlist->array[i]);
}
printf("\n");
}
//修改pos 所在下标的所在数据为 value
void SeqListModify(SeqList *seqlist, int pos, SDataType value){
assert(pos>=0&&pos<seqlist->size);
seqlist->array[pos] = value;
}
//查找
//如果找到,返回第一个找到的下标
//如果没找到返回-1
int SeqListFind(const SeqList *seqlist, SDataType value){
assert(seqlist!=NULL);
for (int i = 0; i < seqlist->size;i++){
if (seqlist->array[i] == value)
return i;
}
return -1;
}
//找到并删除第一个遇到的 value
void SeqListRemove(SeqList *seqlist, SDataType value){
int pos = SeqListFind(seqlist,value);
if (pos != -1){
SeqListErase(seqlist,pos);
}
}
//判断顺序表是否为空,
bool SeqListEmpty(const SeqList *seqlist){
return seqlist->size == 0;
}
//返回数据个数
int SeqListSize(const SeqList *seqlist){
return seqlist->size;
}
void SeqListRemoveALl(SeqList* seqlist, SDataType value){
assert(seqlist != NULL);
assert(seqlist->array!=NULL);
//方法一:
申请一个与原数组等长的数组
//SDataType *array = (SDataType*)malloc(sizeof(SDataType)*seqlist->size);
//assert(array);
//int index = 0;
//for (int i = 0; i < seqlist->size; i++){
// if (seqlist->array[i] != value){
// array[index] = seqlist->array[i];
// index++;
// }
//}
//for (int j = 0; j < index; j++){
// seqlist->array[j] = array[j];
//}
//seqlist->size = index;
//法二
int index = 0;
for (int i = 0; i < seqlist->size; i++){
if (seqlist->array[i] != value){
seqlist->array[index] = seqlist->array[i];
index++;
}
}
seqlist->size++;
}
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<stdbool.h>
#include<assert.h>
#include<windows.h>
//typedef struct SeqList{
// //静态顺序表定义
// int array[100]; // 能存 100 个数的静态顺序表
// int size; //当前顺序表中存放的数,
// //顺序表为空size=0顺便表示了即将插入的下标
//} Seqlist;
typedef int SDataType;
typedef struct SeqList{
//动态顺序表定义
SDataType *array //指向堆上的空间
int size; //记录数组中的有效元素个数
int capacity; //容量
} SeqList;
//顺序表的初始化/销毁
//seqlist 是一个变量的地址
//capacity是顺序表的初始容量
void SeqListInit(SeqList *seqlist, int capacity);
void SeqListDestroy(SeqList *seqlist);
//增删改查
//1.【增】
//尾插
void SeqListPushBack(SeqList *seqlist, SDataType value);
//头插
//1.从后往前搬,避免覆盖
//2.写循环
// 先确定循环的边界
// i 空间下标[size,0)
// i 数据下标[size-1,0]
//3. 搬移
// i对应空间下标: array[i]=array[i-1];
// i对应数据下标: array[i+1]=array[i];
void SeqListPushFront(SeqList *seqlist,SDataType value);
//中间插入,往pos所在的下标插入value
//1.从后往前
// [size-1,pos] i 数据
// array[i+1]=array[i];
void SeqListInsert(SeqList *seqlist,int pos,SDataType value);
//删
//1.尾删
void SeqListPopBack(SeqList* seqlist);
//2.头删
//1.从后往前搬,避免覆盖
//2.写循环
// 先确定循环的边界
// i 空间下标 [0,size-2]
// i 数据下标 [1,size-1]
//3. 搬移
// i对应空间下标: array[i]=array[i+1];
// i对应数据下标: array[i-1]=array[i];
void SeqListPopFront(SeqList* seqlist);
//删除 pos 所在的下标数据
void SeqListErase(SeqList *seqlist,int pos);
//打印
void SeqListPrint(const SeqList *seqlist);
//修改pos 所在下标的所在数据为 value
void SeqListModify(SeqList *seqlist,int pos, SDataType value);
//查找
int SeqListFind(const SeqList *seqlist,SDataType value);
//找到并删除第一个遇到的 value
void SeqListRemove(SeqList *seqlist,SDataType value);
//判断顺序表是否为空,
bool SeqListEmpty(const SeqList *seqlist);
//返回数据个数
int SeqListSize(const SeqList *seqlist);
//删除所有遇到的value值
void SeqListRemoveALl(SeqList* seqlist,SDataType value);