一、数据结构前言
1. 数据结构:数据和数据间的组织方式
2. 程序(数据和函数组成) = 数据结构 + 算法
3. 时间复杂度
概念:一个算法程序的某一语句的执行次数(而不是该算法的运行时间);
时间复杂度是一个函数 T (n) ,为方便一般使用大O表示法标准:渐进时间复杂度 O (f (n));
常见算法时间复杂度:
- 递归算法时间复杂度 = 单次调用时间复杂地+总递归次数
- 冒泡排序时间复杂度 = O(n^2)
- 二分查找时间复杂度 = O(log(n))
- 递归求斐波那契数时间复杂素 = O(2^n)
4. 空间复杂度
概念:一个算法运行时临时占用的空间大小(即运行后新开辟的空间);
空间复杂度是一个函数 S (n) ,为方便一般使用大O表示法标准:渐进空间复杂度 O (f (n));
常见复杂度性能
二、顺序表
- 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组
上完成数据的增删查改。
1.顺序表的两种开辟方式(顺序表的数据结构)
静态顺序表
直接将数据放在定长数组
#define N 7
struct SeqList
{
int array[N];
size_t size;
}
动态顺序表
在堆上动态开辟数组并返回起始位置指针
struct SeqList
{
int *array;
size_t size;
size_t capicticy;
}
2.顺序表的常用函数
1)初始化动态顺序表
在堆上开辟自定义长度的顺序表,默认最小开辟三个元素
void SeqListInit(SeqList* ps, int initCapacity)
{
assert(ps);
initCapacity = initCapacity <= 0 ? 3 : initCapacity;
ps->array = (DataType*)malloc(initCapacity * sizeof(DataType));
if (NULL == ps->array)
{
assert(0);
return;
}
ps->capacity = initCapacity;
ps->size = 0;
}
2)释放顺序表
动态顺序表由于是在堆上开辟,调用结束后需要释放,避免内存溢出
void SeqListDestroy(SeqList* ps)
{
assert(ps);
if (ps->array)
{
free(ps->array);
ps->array = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
3)顺序表的尾插与头插
a. 顺序表插入前必须先检查空间是否足够,自定义空间检测函数static void ChecKCapacity(SeqList* ps) b. 头插需将后面所有元素向后依次移动 c. 故尾插时间复杂度为O(1),头插时间复杂度为O(N) d. 插入操作后记得对数组有效大小加1
void SeqListPushBack(SeqList* ps, DataType data)
{
assert(ps);
ChecKCapacity(ps);
ps->array[ps->size] = data;
ps->size++;
}
void SeqListPushFront(SeqList* ps, DataType data)
{
assert(ps);
ChecKCapacity(ps);
for (int i = ps->size - 1; i >= 0; i--)
{
ps->array[i + 1] = ps->array[i];
}
ps->array[0] = data;
ps->size++;
}
4)顺序表空间检测函数
a. 定义为static静态函数,使其只在本文件程序有效 b. 若空间不足,每次开辟的新空间为原来两倍 (这里使用左移<<操作符扩大2倍) c. 注意新空间开辟后,要释放原来堆空间
static void ChecKCapacity(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newCapacity = (ps->capacity << 1);
DataType* temp = (DataType*)malloc(newCapacity * sizeof(DataType));
if (NULL == temp)
{
assert(0);
return;
}
for (int i = 0; i < ps->size; i++)
{
temp[i] = ps->array[i];
}
free(ps->array);
ps->array = temp;
ps->capacity = newCapacity;
}
}
5)顺序表的尾删与头删
a. 顺序表删除前必须先判断是否对空链表进行操作,若为空表直接跳出 b. 头删需将后面所有元素向前依次移动 c. 故头插时间复杂度为O(1),尾插时间复杂度为O(N) d. 删除操作后记得对数组有效大小减1
void SeqListPopBack(SeqList* ps)
{
assert(ps);
if (SeqListEmpty(ps))
{
printf("ERROR:顺序表无内容\n");
return;
}
ps->size--;
}
void SeqListPopFront(SeqList* ps)
{
assert(ps);
if (SeqListEmpty(ps))
{
printf("ERROR:顺序表无内容\n");
return;
}
for (int i = 0; i < ps->size; i++)
{
ps->array[i] = ps->array[i + 1];
}
ps->size--;
}
6)顺序表的指定位置插入与删除
插入与删除前先判断pos位置是否在数组有效长度内
void SeqListInsert(SeqList* ps, int pos, DataType data)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("ERROR:插入位置超出范围\n");
return;
}
ChecKCapacity(ps);
for (int i = ps->size; i >= pos; i--)
{
ps->array[i + 1] = ps->array[i];
}
ps->array[pos] = data;
ps->size++;
}
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("ERROR:插入位置超出范围\n");
return;
}
for (int i = pos; i < ps->size; i++)
{
ps->array[i] = ps->array[i + 1];
}
ps->size--;
}
7)顺序表的其他函数
int SeqListSize(SeqList* ps)
{
assert(ps);
return ps->size;
}
int SeqListCapacity(SeqList* ps)
{
assert(ps);
return ps->capacity;
}
int SeqListFind(SeqList* ps, DataType data)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (data == ps->array[i])
{
return i;
}
}
return -1;
}
int SeqListEmpty(SeqList* ps)
{
assert(ps);
return 0 == ps->size;
}
DataType SeqListFront(SeqList* ps)
{
assert(ps);
return ps->array[0];
}
DataType SeqListBack(SeqList* ps)
{
assert(ps);
return ps->array[ps->size];
}
DataType SeqListGet(SeqList* ps, int pos)
{
assert(ps);
return ps->array[pos];
}
3.顺序表总结
1)顺序表由于在内存连续开辟,故找任意位置数据时可直接找到
2)顺序表的头插与头删需循环移动数据,时间复杂度高
3)顺序表扩容需将原数据遍历拷贝值新空间,消耗大
4)顺序表会造成空间浪费,因为空间是提前开辟好的
源代码
头文件
#pragma once
#include<stdio.h>
#include<Windows.h>
#include<malloc.h>
#include<assert.h>
#pragma warning(disable:4996)
typedef int DataType;
typedef struct SeqList
{
DataType* array;
int capacity;
int size;
}SeqList;
void SeqListInit(SeqList* ps, int initCapacity);
void SeqListDestroy(SeqList* ps);
void SeqListPushBack(SeqList* ps, DataType data);
void SeqListPopBack(SeqList* ps);
void SeqListPushFront(SeqList* ps, DataType data);
void SeqListPopFront(SeqList* ps);
void SeqListInsert(SeqList* ps, int pos, DataType data);
void SeqListErase(SeqList* ps, int pos);
int SeqListSize(SeqList* ps);
int SeqListCapacity(SeqList* ps);
int SeqListFind(SeqList* ps, DataType data);
int SeqListEmpty(SeqList* ps);
DataType SeqListFront(SeqList* ps);
DataType SeqListBack(SeqList* ps);
DataType SeqListGet(SeqList* ps, int pos);
void TestSeqList();
函数文件
#include "seqlist.h"
static void ChecKCapacity(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newCapacity = (ps->capacity << 1);
DataType* temp = (DataType*)malloc(newCapacity * sizeof(DataType));
if (NULL == temp)
{
assert(0);
return;
}
for (int i = 0; i < ps->size; i++)
{
temp[i] = ps->array[i];
}
free(ps->array);
ps->array = temp;
ps->capacity = newCapacity;
}
}
static void SeqListPrint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->array[i]);
}
printf("\n");
}
void SeqListInit(SeqList* ps, int initCapacity)
{
assert(ps);
initCapacity = initCapacity <= 0 ? 3 : initCapacity;
ps->array = (DataType*)malloc(initCapacity * sizeof(DataType));
if (NULL == ps->array)
{
assert(0);
return;
}
ps->capacity = initCapacity;
ps->size = 0;
}
void SeqListDestroy(SeqList* ps)
{
assert(ps);
if (ps->array)
{
free(ps->array);
ps->array = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
void SeqListPushBack(SeqList* ps, DataType data)
{
assert(ps);
ChecKCapacity(ps);
ps->array[ps->size] = data;
ps->size++;
}
void SeqListPopBack(SeqList* ps)
{
assert(ps);
if (SeqListEmpty(ps))
{
printf("ERROR:顺序表无内容\n");
return;
}
ps->size--;
}
void SeqListPushFront(SeqList* ps, DataType data)
{
assert(ps);
ChecKCapacity(ps);
for (int i = ps->size - 1; i >= 0; i--)
{
ps->array[i + 1] = ps->array[i];
}
ps->array[0] = data;
ps->size++;
}
void SeqListPopFront(SeqList* ps)
{
assert(ps);
if (SeqListEmpty(ps))
{
printf("ERROR:顺序表无内容\n");
return;
}
for (int i = 0; i < ps->size; i++)
{
ps->array[i] = ps->array[i + 1];
}
ps->size--;
}
void SeqListInsert(SeqList* ps, int pos, DataType data)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("ERROR:插入位置超出范围\n");
return;
}
ChecKCapacity(ps);
for (int i = ps->size; i >= pos; i--)
{
ps->array[i + 1] = ps->array[i];
}
ps->array[pos] = data;
ps->size++;
}
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
if (pos<0 || pos>ps->size)
{
printf("ERROR:插入位置超出范围\n");
return;
}
for (int i = pos; i < ps->size; i++)
{
ps->array[i] = ps->array[i + 1];
}
ps->size--;
}
int SeqListSize(SeqList* ps)
{
assert(ps);
return ps->size;
}
int SeqListCapacity(SeqList* ps)
{
assert(ps);
return ps->capacity;
}
int SeqListFind(SeqList* ps, DataType data)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (data == ps->array[i])
{
return i;
}
}
return -1;
}
int SeqListEmpty(SeqList* ps)
{
assert(ps);
return 0 == ps->size;
}
DataType SeqListFront(SeqList* ps)
{
assert(ps);
return ps->array[0];
}
DataType SeqListBack(SeqList* ps)
{
assert(ps);
return ps->array[ps->size];
}
DataType SeqListGet(SeqList* ps, int pos)
{
assert(ps);
return ps->array[pos];
}
static void Test1()
{
SeqList s;
SeqListInit(&s, 3);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPushBack(&s, 6);
SeqListPrint(&s);
SeqListErase(&s, 4);
SeqListPushFront(&s, 0);
SeqListPrint(&s);
printf("顺序表有效数字:%d\n", SeqListSize(&s));
printf("顺序表开辟空间:%d\n", SeqListCapacity(&s));
printf("顺序表中数字5的下标:%d\n", SeqListFind(&s, 5));
SeqListDestroy(&s);
}
void TestSeqList()
{
Test1();
}
主函数文件
#include "seqlist.h"
int main()
{
TestSeqList();
system("pause");
return 0;
}
|