详解动态顺序表

2024-01-02 13:03:01

𝙉𝙞𝙘𝙚!!👏🏻???????👏🏻??????? 👏🏻?????:Solitary-walk

? ? ? ?? ? ━━━┓
? ? ?- 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━ ?? ?

本人座右铭 : ? 欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑?
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑 ? ?希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑 ? 此外,希望各位大佬们在看完后,可以互赞互关一下,看到必回
👑👑👑💎👑👑👑 ? ??

目录?

一:顺序表的介绍

? 1:顺序表定义

? ? 顺序表是线性表的一种,首先顺序表是是用一段物理地址连续的存储单元依次存储数据元素的线性结构,多数情况下借助数组来存储

二:动态顺序表和静态顺序表区别

? 1:静态顺序表:

? ?静态就体现了静态顺序表的特征,数组的空间大小是固定的

//静态顺序表的结构体的构造
#define N 10
typedef int SLDataTYpe;//便于实现各种数据类型,一改全改

typedef struct SeqList
{
	SLDataTYpe a[N];
	int size;//记录数据有效的个数
}SL;
?2:动态顺序表

?动态体现了是一个不断变化的过程,可以频繁进行扩容

//动态顺序表
typedef int SLDataType;//便于实现各种数据类型,一改全改
typedef struct SeqList
{
	SLDataType* a;
	int size;//记录数据有效的个数
	int capacity;//空间空间的容量
}SL;
3:二者区别

静态顺序表:以定长数组的形式存储

动态顺序表:以动态开辟的数组进行存储

本质上二者没有优劣之分,只不过动态顺序表多了一个成员:空间容量

三:动态顺序表的接口实现

1. 初始化

为了避免不必要的麻烦,我们在初始化的时候直接就对数组进行动态开辟

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);//先为数组开4 个空间
	if (psl->a == NULL)  //空间检查
	{
		printf("perror malloc\n");
		return;
	}
    // 开辟成功
	psl->size = 0;
	psl->capacity = 4;
}
2.销毁
void SLDestroy(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->capacity = 0;
	psl->size = 0;
}
3.尾插

分析:首先我们在尾插之前需要先进行空间容量检查

?

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);
	//先判断空间是否足够
	CheckCapacity(psl);
	//之后直接尾插
	psl->a[psl->size] = x;
	psl->size++;
}
4.头插

分析:

1)空间容量的检查

2) 数据的挪动:从后往前挪动(避免数据覆盖)

3)下标边界值的确定

?

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	//先判断空间是否足够
	CheckCapacity(psl);
	 挪动数据:从后往前挪动数据 注意边界的问题
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;

	
	 psl->size++;
}
?5.尾删

先进行判空,其次直接size--

void SLPopBack(SL* psl)
{
	assert(psl);
	//判断是否为空   温柔  / 暴力判断
	 if (psl->size == 0)
	{
		printf("为空 \n");
		return;
	}
	
	psl->size--;

}
6.头删

先进行判空

其次挪动数据:从前往后挪动

下标边界的确定

?

void SLPopFront(SL* psl)
{
	assert(psl);
	assert(psl->size);//判空
	// 挪动数据:从前往后挪数据
	int start = 0;
	while (start < psl->size - 1)
	{
		psl->a[start] = psl->a[start + 1];
		start++;
	}
	//SLEarse(psl, 0);
	//注意:别忘了size--
	psl->size--;


}
7.任意位置的插入

1)空间容量检查

2)pos这个位置是否合法

3)数据挪动:和头插一样

?

void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	// 先进行空间判断,以及位置的合法

	CheckCapacity(psl);
	
	assert(pos >= 0 && pos <= psl->size);  //这里可以取等,因为数组下标是连续的,可以在psl->size 这个位置插入
	
	int i = psl->size - 1;
	while (i >= pos)
	{
		psl->a[i + 1] = psl->a[i];
		i--;
	}
	psl->a[pos] = x;
	psl->size++;
	// 灵活运用: 头插,尾插就是此函数的一个特列

}
8.任意位置的删除

1)判空检查

2)pos是否合法

3)数据挪动:同头删一样

?

void SLEarse(SL* psl, int pos)
{
	assert(psl);
	//删除之前,先对pos这个位置判断是否合法,是否为空
	assert(pos >= 0 && pos < psl->size);  //注意这里没有必要取等  ,同时这个语句暗含着对判空的操作了
	int i = pos;
	//挪动数据:从前往后
	while (i < psl->size - 1)
	{
		psl->a[i] = psl->a[i + 1];
		i++;
	}
	//别忘了 size--

	psl->size--;

}

9.空间容量的检查

? ? 当我们在进行头插,尾插,任意位置插入时都需要进行空间容量的检查,这里为了避免不必要的麻烦,写了一个函数

void CheckCapacity(SL* psl)
{
	assert(psl);
	if (psl->capacity == psl->size)
	{
		//realloc来扩容   void* realloc (void* ptr, size_t size);  第二个参数:扩容之后新的空间字节数,而不是要增加的字节数
		// 注意这样写是错误的,在free 的时候有问题  SLDataType* tmp = (SLDataType*)realloc(psl->a, (sizeof(SLDataType) *  4) * 2);//以原来2倍空间扩容
		SLDataType* tmp = (SLDataType*)realloc(psl->a, (sizeof(SLDataType) * psl->capacity) * 2);//以原来2倍空间扩容
		if (tmp == NULL)
		{
			printf("perror realloc\n");
			return;
		}
		// 扩容成功,进行更新
		psl->a = tmp;
		psl->capacity *= 2;

	}
}
10. 查找

1)前期的判断:位置是否合法?? 顺序表是否为空表

2) 其实就是暴力求解,依次遍历顺序表即可,若是找到返回下标找不到返回-1

int  SLFind(SL* psl, SLDataType x)//查找
{
	assert(psl);
	assert(psl->size);//避免为空
	int i = 0;
	while (i < psl->size)
	{
		if (psl->a[i] == x)
		{
			return i;// 返回下标
		}
		i++;
	}
	return -1;//没有找到
}

11.任意位置修改?

1)依然是老生常谈,对位置以及顺序表进行判断

2)直接进行修改

void SLModify(SL* psl, int pos, SLDataType x) //指定位置修改
{
	assert(psl);
	assert(psl->size);//避免为空
	assert(pos >= 0 && pos <= psl->size - 1);
	psl->a[pos] = x;

}

完整代码:

SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);//先为数组开4 个空间
	if (psl->a == NULL)
	{
		printf("perror malloc\n");
		return;
	}
	psl->size = 0;
	psl->capacity = 4;
}
void SLDestroy(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->capacity = 0;
	psl->size = 0;
}
void CheckCapacity(SL* psl)
{
	assert(psl);
	if (psl->capacity == psl->size)
	{
		//realloc来扩容   void* realloc (void* ptr, size_t size);  第二个参数:扩容之后新的空间字节数,而不是要增加的字节数
		// 注意这样写是错误的,在free 的时候有问题  SLDataType* tmp = (SLDataType*)realloc(psl->a, (sizeof(SLDataType) *  4) * 2);//以原来2被空间扩容
		SLDataType* tmp = (SLDataType*)realloc(psl->a, (sizeof(SLDataType) * psl->capacity) * 2);//以原来2被空间扩容
		if (tmp == NULL)
		{
			printf("perror realloc\n");
			return;
		}
		// 扩容成功,进行更新
		psl->a = tmp;
		psl->capacity *= 2;

	}
}
void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);
	//先判断空间是否足够
	CheckCapacity(psl);
	//之后直接尾插
	/*psl->a[psl->size] = x;
	psl->size++;*/
	if (psl->size == 0)
	{
		psl->a[psl->size] = x;
		psl->size++;
	}
	else
	{
		SLInsert(psl, psl->size, x);
		//注意这里不用size++,因为SLInsert这个函数以及涉及到了
	}
}
void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	//先判断空间是否足够
	CheckCapacity(psl);
	 挪动数据:从后往前挪动数据 注意边界的问题
	//int end = psl->size - 1;
	//while (end >= 0)
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	end--;
	//}
	//psl->a[0] = x;

	SLInsert(psl, 0, x);
	// psl->size++;
}
void SLPopBack(SL* psl)
{
	assert(psl);
	//判断是否为空   温柔  / 暴力判断
	 /*if (psl->size == 0)
	{
		printf("为空 \n");
		return;
	}*/
	assert(psl->size);
	psl->size--;

}
void SLPopFront(SL* psl)
{
	assert(psl);
	assert(psl->size);//判空
	// 挪动数据:从前往后挪数据
	int start = 0;
	while (start < psl->size - 1)
	{
		psl->a[start] = psl->a[start + 1];
		start++;
	}
	//SLEarse(psl, 0);
	//注意:别忘了size--
	psl->size--;


}
void SLPrint(SL* psl)
{
	assert(psl);
	int i = 0;
	while (i < psl->size)
	{
		printf("%d ", psl->a[i]);
		i++;
	}
	printf("\n");
}
void SLEarse(SL* psl, int pos)
{
	assert(psl);
	//删除之前,先对pos这个位置判断是否合法,是否为空
	assert(pos >= 0 && pos < psl->size);  //注意这里没有必要取等  ,同时这个语句暗含着对判空的操作了
	int i = pos;
	//挪动数据:从前往后
	while (i < psl->size - 1)
	{
		psl->a[i] = psl->a[i + 1];
		i++;
	}
	//别忘了 size--

	psl->size--;

}
void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	// 先进行空间判断,以及位置的合法

	CheckCapacity(psl);
	
	assert(pos >= 0 && pos <= psl->size);  //这里可以取等,因为数组下标是连续的,可以在psl->size 这个位置插入
	
	int i = psl->size - 1;
	while (i >= pos)
	{
		psl->a[i + 1] = psl->a[i];
		i--;
	}
	psl->a[pos] = x;
	psl->size++;
	// 灵活运用: 头插,尾插就是此函数的一个特列

}
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//顺序表分类:静态顺序表  动态顺序表
 
//静态的:空间开大了浪费,空间开小了不够用


/*静态顺序表的结构体的构造
#define N 10
typedef int SLDataTYpe;//便于实现各种数据类型,一改全改

typedef struct SeqList
{
	SLDataTYpe a[N];
	int size;//记录数据有效的个数
}SL;
*/

//动态顺序表
typedef int SLDataType;//便于实现各种数据类型,一改全改
typedef struct SeqList
{
	SLDataType* a;
	int size;//记录数据有效的个数
	int capacity;//空间空间的容量
}SL;
//接口函数的实现

void SLInit(SL* psl);
void SLDestroy(SL* psl);
void SLPushBack(SL* psl, SLDataType x);
void SLPushFront(SL* psl, SLDataType x);
void SLPopBack(SL* psl);
void SLPopFront(SL* psl);
void SLPrint(SL* psl);
void SLEarse(SL* psl,int pos);
void SLInsert(SL* psl, int pos, SLDataType x);

结束语:

以上就是我今日要为大家分享的,希望各位大佬随时指正,咱一波关注走起,看到必回

文章来源:https://blog.csdn.net/X_do_myself/article/details/135329170
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。