数据结构
1.数据结构概述
定义:如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器中,以及在此基础上实现某个功能,而执行的相应操作,该操作也叫算法。
数据结构 = 个体 + 个体的关系
程序 = 数据的存储 + 数据的操作 + 可被计算机所执行的语言
数据存储分成两部分:
1.个体的存储
2.个体关系的存储
从某种角度而言,数据存储最核心的是个体关系的存储,个体存储可忽略不计。
2.算法
算法 = 对存储数据的操作
泛型:同一种逻辑结构,无论该逻辑结构的物理存储是怎样,都可对它执行相同的操作。
衡量算法的标准:
-
时间复杂度:程序要执行的次数,非执行的时间
-
空间复杂度:算法执行过程中大概所占最大内存
-
难易程度
-
健壮性
3.线性结构
- 连续存储[数组]
数组:一组相同类型元素的集合。
优点:存储速度快
缺点:插入删除元素速度慢,空间通常有限制,需事先知道数组长度,需要大块连续的内存块
连续存储数组的算法:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
struct Array
{
int* pBase; //存放第一个元素的地址
int length; //数组长度
int count; //数组有效元素个数
};
void init_arr(struct Array* pArr, int length); //数组初始化函数
bool is_empty(struct Array* pArr); //判断数组内容是否为空
void show_arr(struct Array* pArr); //显示数组内容函数
bool append_arr(struct Array* pArr, int value); //数组追加数据函数,value:追加的元素
bool is_full(struct Array* pArr); //判断数组内容是否满了
bool insert_arr(struct Array* pArr, int position, int value); //数组插入数据函数,position:插入位置, value:插入的元素
bool delete_arr(struct Array* pArr, int position, int* pVal); //数组删除数据函数,position:删除位置, *pVal: 被删除的元素
void sort_arr(struct Array* pArr); //数组排序函数
int main()
{
struct Array arr;
int value;
init_arr(&arr, 6);
append_arr(&arr, 1);
append_arr(&arr, 2);
append_arr(&arr, 3);
append_arr(&arr, 4);
append_arr(&arr, 5);
insert_arr(&arr, 1, 6);
delete_arr(&arr, 2, &value);
sort_arr(&arr);
show_arr(&arr);
return 0;
}
void init_arr(struct Array* pArr, int length)
{
pArr->pBase = (int*)malloc(sizeof(int) * length); //开辟内存空间
if (pArr->pBase == NULL) //如果为空,则开辟失败
{
printf("内存开辟失败\n");
exit(-1); //终止程序
}
else
{
pArr->length = length; //数组长度初始化
pArr->count = 0; //数组有效元素个数初始化
}
}
bool is_empty(struct Array* pArr)
{
if (pArr->count == 0) //如果为空,返回false,否则返回true
{
return false;
}
else
{
return true;
}
}
void show_arr(struct Array* pArr)
{
if (is_empty(pArr)) //判断数组是否为空,不为空则输出数组的内容
{
for (int i = 0; i < pArr->count; i++)
{
printf("%d ", pArr->pBase[i]); //输出打印数组内容
}
printf("\n"); //输出换行
}
else
{
printf("数组为空\n");
}
}
bool is_full(struct Array* pArr)
{
if (pArr->count == pArr->length) //如果数组长度与数组有效元素数量相同,则满了,返回false
{
return false;
}
else
{
return true;
}
}
bool append_arr(struct Array* pArr, int value)
{
if (is_full(pArr)) //判断数组是否满了,没满的话返回true,将会追加数据
{
pArr->pBase[pArr->count] = value;
(pArr->count)++; //数组有效个数加一
return true;
}
else
{
printf("数组已满\n");
return false;
}
}
bool insert_arr(struct Array* pArr, int position, int value)
{
int i;
if (!(is_full(pArr))) //判断数组是否满了
return false;
if (position < 1 || position > pArr->count + 1) //判断是否超出范围了
return false;
for ( i = pArr->count - 1; i >= position - 1; i--)
{
pArr->pBase[i + 1] = pArr->pBase[i]; //数据元素依次向后挪一位
}
pArr->pBase[position - 1] = value; //插入数据
pArr->count++;
return true;
}
bool delete_arr(struct Array* pArr, int position, int* pVal)
{
int i;
if (position < 1 || position > pArr->count) //判断是否超出范围了
return false;
if (!(is_empty(pArr))) //判断数组是否为空
return false;
*pVal = pArr->pBase[position - 1]; //将被删除的元素保存到pVal变量中
for (i = position; i < pArr->count; i++)
{
pArr->pBase[i - 1] = pArr->pBase[i]; //将数组中的元素向前挪一位
}
pArr->count--; //数组有效个数减一
}
void reverse_arr(struct Array* pArr)
{
int i = 0; //数组起始位置
int j = pArr->count - 1; //数组末尾
int temp; //交换变量
while (i < j)
{
temp = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = temp;
i++;
j--;
}
return;
}
void sort_arr(struct Array* pArr) //这边写的是升序排序
{
int i, j, temp;
for (i = 0; i < pArr->count; i++)
{
for (j = i + 1; j < pArr->count; j++)
{
if (pArr->pBase[i] > pArr->pBase[j])
{
temp = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = temp;
}
}
}
}
- 离散存储[链表]
定义:由一系列结点组成,结点可以在运行时动态生成,每个结点包括两部分:存储数据元素的数据域和存储下一个结点地址的指针域。
优点:存储容量无限,插入删除元素速度快
缺点:存取元素速度慢
首节点:第一个有效节点
尾节点:最后一个有效节点
头结点:第一个首节点之前的节点,头结点不存放有效数据,主要为了方便对链表进行操作
头指针:指向头结点的指针变量
尾指针:指向尾结点的指针变量
想要确定一个链表只需要头指针一个参数即可,可通过头指针推算出链表的其他所有信息。
struct Node
{
int data; //数据域
struct Node* pNext; //指针域
};
求链表长度:
p = pHead -> pNext;
while(p != NULL)
{
count++;
p = p->pNext;
}
分类:
-
单链表:每一个结点只有一个指针域
-
双链表:每一个结点有两个指针域
-
循环链表:可通过任意节点找到其他所有的节点
-
非循环链表:不可通过任意节点找到其他所有的节点
</