【异世界历险之数据结构世界(顺序表)】

前言

一天,大学生小Y被突来的大卡车撞飞,死亡后在C语言女神引导下来到了异世界——数据结构世界。
他复活在初始城——顺序表城,买好了装备和武器(C语言基础),随即开始了他的数据结构世界探索之旅。

顺序表介绍及实现

介绍

顺序表是线性表的一种,具有以下特点:
1.具有相同类型的数据的集合。
2.物理结构连续
3.逻辑结构连续
(一个不太恰当的例子:数组如同苍蝇馆子里的土豆丝,顺序表如米其林餐厅的土豆丝,二者作用差异不大,但实现过程和效果天差地别)
展示

实现

1.顺序表分类

//静态顺序表(定长数组)
struct SeqList
{
	int arr[100];
	int size;//有效数据个数
};
//动态顺序表(自由长度)
struct SeqList
{
	int* arr;
	int capacity;//空间大小
	int size;//有效个数
};

优点分析:

静态顺序表:使用定长数组实现,适用于数据量固定且已知的场景。
动态顺序表:使用动态数组实现,可以根据需要动态调整数组大小,适用于数据量不确定的场景。

2.顺序表初始化

1.前置条件:

SeqList.h
#pragma once
# include<stdio.h>
# include<stdlib.h>
# include<assert.h>
typedef int SLDataType;//改变命名(有利于后期改变类型)

struct SeqList
{
	int* arr;
	int capacity;
	int size;
};
typedef struct SeqList SL;//简化

//初始化
void SLInit(SL* plist);//头文件声明,方便调用(后续不再显示头文件)
 test.c
# include"SeqList.h"
//测试文件
 void test01()
{
	 SL plist ;//初始化一个变量
	 SLInit(&plist);//传址能改变实参,传值不能

}
int main()
{
	test01;
}//后续不再展示测试文件

逻辑分析:

初始化顺序表时,将动态数组的指针设置为 NULL,表示当前没有分配内存。
将 size 设置为 0,表示顺序表中没有元素。
将 capacity 设置为 0,表示当前分配的内存大小为 0。

2.顺序表初始化

SeqList.c
# include"SeqList.h"
//初始化
void SLInit(SL* phead)
{
	phead->arr = NULL;
	phead->capacity = phead->size = 0;
}

3.顺序表销毁/打印

逻辑分析:

销毁:
检查 arr 是否为 NULL,如果不为 NULL,则释放分配的内存。
将 arr 重置为 NULL,capacity 和 size 重置为 0,表示顺序表已销毁。
打印:
遍历顺序表中的每个元素,并将其打印到控制台。

//销毁
void SLDesTory(SL* ps)
{
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}
//打印
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

4.顺序表头插/尾插

逻辑分析:

头插:
调用 SLCheackcapacity 函数检查是否需要扩展容量。
从后往前遍历数组,将每个元素向后移动一位,为新元素腾出空间。
在数组开头插入新元素,并将 size 增加 1。
尾插:
调用 SLCheackcapacity 函数检查是否需要扩展容量。
在数组末尾添加新元素,并将 size 增加 1。

//空间检查
void SLCheackcapacity(SL* ps)
{
	if (ps->size == ps->capacity)//如果数据个数和内存相同
	{
		SLDataType newcapacity = ps->capacity = 0 ? 8:2 * ps->capacity;
		SL* str = (SL*)malloc(sizeof(SL) * newcapacity);
		if (str == NULL)
		{
			perror("malloc fail!!!");
			exit(1);
		}//模板化处理
		ps->capacity = newcapacity;
		ps->arr = str;
	}
}

//头插/尾插
void SLPushFront(SL* ps, SLDataType x)
{   assert(ps);//不能解引用空指针
	SLCheackcapacity(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i-1];
	}//往后移一位
	ps->arr[0] = x;
	ps->size++;//size加一
}

void SLPushBack(SL* ps,SLDataType x)
{   assert(ps);
	SLCheackcapacity(ps);
	ps->arr[ps->size++] = x;//这段代码等价于  ps->arr[size] = x;
	                        //                ps->size++;(先赋值。后加加)
}

头插/尾插

5.顺序表头删/尾删

//头删/尾删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);//顺序表不能为空否则无法删除
	for (int i = 0; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//前移一位
	}
	ps->size--;//size减一
}

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

效果展示:
头删/尾删

6.顺序表自由插入/自由删除

逻辑分析:

头删:
从数组开头开始,将每个元素向前移动一位,覆盖掉第一个元素。
将 size 减少 1。
尾删:
直接将 size 减少 1,表示删除了最后一个元素。

//自由插入(之前)/自由删除

void SLInsert(SL* ps,SLDataType pos, SLDataType x)
{
	assert(ps);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

void SLErase(SL* ps, SLDataType pos)
{
	assert(ps);
	assert(ps->size &&pos>=0);
	for (int i = pos; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i+1];//删除pos点处数
	}
	ps->size--;
}

效果展示:
自由插入/自由删除

7.顺序表查找

逻辑分析:

遍历顺序表中的每个元素,检查是否等于目标值 x。
如果找到,返回该元素的索引。
如果遍历完整个数组都没有找到,返回 -1。

//查找
void SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;//找到了
		}
	}
	return -1;//没找到
}

效果展示:
在这里插入图片描述

8.顺序表排序

PS:作者采用较为高效率的快速排序,读者也可以尝试冒泡排序等方式。
逻辑分析:
cmp_int 是一个比较函数,用于比较两个整数的大小。
SLSort 使用 qsort 函数对顺序表中的元素进行快速排序。
qsort 需要传入数组、元素个数、元素大小和比较函数。

//排序
int cmp_int(const void* str1, const void* str2)
{
	return *(int*)str1 - *(int*)str2;
}

void SLSort(SL* ps)
{
	assert(ps);
	qsort(ps->arr, ps->size, sizeof(int), cmp_int);
}

效果展示:在这里插入图片描述

总结

SeqList.h
#pragma once
# include<stdio.h>;
# include<stdlib.h>
# include<assert.h>
typedef int SLDataType;//改变命名(有利于后期改变类型)

struct SeqList
{
	int* arr;
	int capacity;
	int size;
};

typedef struct SeqList SL;//简化

//初始化
void SLInit(SL* ps);//头文件声明,方便调用

//销毁
 
void SLDesTory(SL* ps);

//打印

void SLPrint(SL* ps);

//头插/尾插

void SLPushFront(SL* ps, SLDataType x);

void SLPushBack(SL* ps, SLDataType x);

//头删/尾删

void SLPopFront(SL* ps);

void SLPopBack(SL* ps);

//自由插入/自由删除

void SLErase(SL* ps, SLDataType x);

void SLInsert(SL* ps, SLDataType pos, SLDataType x);
//查找
int SLFind(SL* ps, SLDataType x);

//排序

void SLSort(SL*PS);

SeqList.c
# include"SeqList.h"
//初始化
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}


//销毁

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

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


//空间检查
void SLCheackcapacity(SL* ps)
{
	if (ps->size == ps->capacity)//如果数据个数和内存相同
	{
		SLDataType newcapacity = ps->capacity = 0 ? 8:2 * ps->capacity;
		SL* str = (SL*)malloc(sizeof(SL) * newcapacity);
		if (str == NULL)
		{
			perror("malloc fail!!!");
			exit(1);
		}//模板化处理
		ps->capacity = newcapacity;
		ps->arr = str;
	}
}


//头插/尾插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheackcapacity(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i-1];
	}//后移一位
	ps->arr[0] = x;
	ps->size++;//size加一
}

void SLPushBack(SL* ps,SLDataType x)
{
	assert(ps);
	SLCheackcapacity(ps);
	ps->arr[ps->size++] = x;
}


//头删/尾删

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);
	for (int i = 0; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//前移一位
	}
	ps->size--;//减少一个
}

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

//自由插入(之前)/自由删除

void SLInsert(SL* ps,SLDataType pos, SLDataType x)
{
	assert(ps);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

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

//查找

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;//找到了
		}
	}
	return -1;//没找到
}

//排序
int cmp_int(const void* str1, const void* str2)
{
	return *(int*)str1 - *(int*)str2;
}

void SLSort(SL* ps)
{
	assert(ps);
	qsort(ps->arr, ps->size, sizeof(int), cmp_int);
}
test.c
# include"SeqList.h"
//测试文件
 void test01()
{
	 SL plist ;
	 SLInit(&plist);

	 SLPushBack(&plist, 1);
	 SLPushBack(&plist, 2);
	 SLPushBack(&plist, 3);
	 SLPushBack(&plist, 4);
	 SLPushBack(&plist, 5);
	 SLPushBack(&plist, 6);
	 SLPrint(&plist);

	 SLPushFront(&plist, 7);
	 SLPushFront(&plist, 8);
	 SLPushFront(&plist, 9);
	 SLPrint(&plist);

	 SLPopBack(&plist);
	 SLPopBack(&plist);
	 SLPrint(&plist);

	 SLPopFront(&plist);
	 SLPopFront(&plist);
	 SLPrint(&plist);

	 SLErase(&plist, 2);
	 SLErase(&plist, 3);
	 SLPrint(&plist);

	 SLInsert(&plist, 2, 4);
	 SLInsert(&plist, 1, 3);
	 SLPrint(&plist);

	 int x = 0;
	 scanf("%d", &x);
	 int ret = SLFind(&plist, x);
	 if (ret >= 0)
	 {
		 printf("找到了.pos点为:%d\n",ret);
	 }
	 else
	 {
		 printf("没找到\n");
	 }
	 
    SLSort(&plist);
    SLPrint(&plist);

}

int main()
{
	test01();
}

顺序表是数据结构中的基础内容,包括静态和动态两种类型。本文详细介绍了顺序表的初始化、销毁、打印、插入、删除、查找和排序等操作,并通过代码逻辑分析帮助读者深入理解每段代码的实现细节。掌握顺序表的实现和操作,为后续学习更复杂的数据结构打下坚实基础。

实战项目:通讯录
单链表

尾语

小Y成功通过了顺序表城的考验,获得了神器之一——顺序表之剑,他的异世界之旅还未结束,我们拭目以待!!!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值