1.数据结构基本概念
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。它是计算机存储、组织数据的方式,直接影响程序的效率和性能。
2.数据结构分类
1.逻辑结构
- 集合结构:所有数据都在一个集合中,元素间关系平等。
- 线性结构:数据之间是一对一的关系,如数组、链表。
- 树状结构:数据之间是一对多的关系,如二叉树、B树。
- 图状结构:数据之间是多对多的关系,如社交网络图。
2.物理结构(存储结构)
- 顺序存储:数据存储在连续的存储单元中,如数组。
- 链式存储:数据存储单元可以是任意的,通过指针连接,如链表。
2.抽象数据类型 (ADT)
抽象数据类型是数学模型与操作的结合,即数据与算法的结合。它定义了数据的逻辑结构及其操作,而不涉及具体的实现细节。
3.算法基础
1.算法定义
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列。
1.算法特征
- 输入输出:算法必须有输出。
- 有穷性:执行步骤会自动结束,且每一步在可接受时间内完成。
- 确定性:相同输入产生相同输出。
- 可行性:每一步都可实现。
算法设计原则
- 正确性:语法正确,合法输入产生合理输出,对非法输入能给出适当响应,通过各种测试用例。
- 可读性:便于交流、阅读和理解。
- 健壮性:能处理非法输入而不产生异常。
- 高效性:低存储需求,高执行效率。
4.时间复杂度分析
1.推导规则
- 用常数1取代运行时间中所有加法常数。
- 只保留最高阶项。
- 如果最高阶项系数不为1,去除该系数。
2.常见时间复杂度排序
O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(n!) < O(nⁿ)
5.线性表概述
线性表是零个或多个数据元素的有限序列,具有以下特性:
- 元素之间是有顺序的。
- 如果存在多个元素:
- 第一个元素无前驱。
- 最后一个元素没有后继。
- 每个元素只有一个前驱和一个后继。
- 线性表的元素个数n(n≥0)称为线性表的长度。
- n=0时为空表。
- 非空表中每个元素都有一个确定的位置。
6.顺序表实现
顺序表是在计算机内存中以数组形式保存的线性表,用一组地址连续的存储单元依次存储数据元素。
顺序表ADT(抽象数据类型)
typedef struct
{
DATATYPE *data; // 存储数据的数组
int length; // 当前长度
int capacity; // 总容量
} SeqList;
7.基本操作实现
1.创建顺序表
SeqList *CreateSeqList(int len)
{
SeqList *list = (SeqList *)malloc(sizeof(SeqList));
if (list == NULL)
{
return NULL;
}
list->data = (DATATYPE *)malloc(sizeof(DATATYPE) * len);
if (list->data == NULL)
{
free(list);
return NULL;
}
list->length = 0;
list->capacity = len;
return list;
}
2.显示顺序表
int ShowSeqList(SeqList *list)
{
if (list == NULL || list->data == NULL)
{
return -1;
}
printf("SeqList: [");
for (int i = 0; i < list->length; i++)
{
// 假设DATATYPE有打印方法
printf("%s", list->data[i].name);
if (i < list->length - 1)
{
printf(", ");
}
}
printf("]\n");
return 0;
}
3.尾插元素
int InsertTailSeqList(SeqList *list, DATATYPE data)
{
if (list == NULL || IsFullSeqList(list))
{
return -1;
}
list->data[list->length] = data;
list->length++;
return 0;
}
4.判断顺序表是否已满
int IsFullSeqList(SeqList *list)
{
if (list == NULL)
{
return -1;
}
return list->length >= list->capacity;
}
5.判断顺序表是否为空
int IsEmptySeqList(SeqList *list)
{
if (list == NULL)
{
return -1;
}
return list->length == 0;
}
6.按位置插入元素
int InsertPosSeqList(SeqList *list, DATATYPE data, int pos)
{
if (list == NULL || pos < 0 || pos > list->length || IsFullSeqList(list))
{
return -1;
}
// 将pos及之后的元素后移
for (int i = list->length; i > pos; i--)
{
list->data[i] = list->data[i-1];
}
list->data[pos] = data;
list->length++;
return 0;
}
7.查找元素
int FindSeqList(SeqList *list, char *name)
{
if (list == NULL || name == NULL)
{
return -1;
}
for (int i = 0; i < list->length; i++)
{
if (strcmp(list->data[i].name, name) == 0)
{
return i; // 返回找到的位置
}
}
return -1; // 未找到
}
8.修改元素
int ModifySeqList(SeqList *list, char *old, DATATYPE new)
{
int pos = FindSeqList(list, old);
if (pos == -1)
{
return -1;
}
list->data[pos] = new;
return 0;
}
9.删除元素
int DeleteSeqList(SeqList *list, char *name)
{
int pos = FindSeqList(list, name);
if (pos == -1)
{
return -1;
}
// 将pos之后的元素前移
for (int i = pos; i < list->length - 1; i++)
{
list->data[i] = list->data[i+1];
}
list->length--;
return 0;
}
10.获取顺序表长度
int GetSeqListLen(SeqList *list) {
if (list == NULL) {
return -1;
}
return list->length;
}
11.获取指定位置元素
DATATYPE *GetItemSeqList(SeqList *list, int ind)
{
if (list == NULL || ind < 0 || ind >= list->length)
{
return NULL;
}
return &(list->data[ind]);
}
12.主程序示例
int main() {
// 创建容量为10的顺序表
SeqList *list = CreateSeqList(10);
if (list == NULL) {
printf("Create list failed!\n");
return -1;
}
// 插入数据
DATATYPE data1 = {"Alice"};
DATATYPE data2 = {"Bob"};
DATATYPE data3 = {"Charlie"};
InsertTailSeqList(list, data1);
InsertTailSeqList(list, data2);
InsertPosSeqList(list, data3, 1);
// 显示顺序表
ShowSeqList(list);
// 查找元素
int pos = FindSeqList(list, "Bob");
if (pos != -1) {
printf("Found Bob at position %d\n", pos);
}
// 修改元素
DATATYPE newData = {"Bobby"};
ModifySeqList(list, "Bob", newData);
// 删除元素
DeleteSeqList(list, "Alice");
// 再次显示
ShowSeqList(list);
// 销毁顺序表
DestroySeqList(list);
return 0;
}
总结
顺序表是线性表的一种实现方式,具有以下特点:
- 随机访问效率高,通过下标可直接访问元素。
- 插入和删除操作需要移动大量元素,效率较低。
- 需要预先分配固定大小的存储空间。
在实际应用中,应根据具体需求选择合适的线性表实现方式(顺序表或链表)。顺序表适合元素数量固定或需要频繁随机访问的场景,而链表则更适合频繁插入删除操作的场景。