前言
继复杂度之后,我们就应该正式叩开数据结构的大门了。那么,数据结构是什么呢?数据结构是计算机存储、组织数据的方式,指相互存在一种或多种特定关系的数据元素的集合。常⽤的数据结构有:线性结构、树结构、图结构等。
学习数据结构,我们先从最为简单的线性结构入手,它是n个具有相同特性的数据元素的有限序列。它是一种在实际中广泛使用的数据结构,常见的数据结构:顺序表、链表、栈、队列、字符串......
线性表在逻辑上是线性结构,也就说是连续的一条直线,但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
一、顺序表
1.概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。
顺序表一般可以分为:静态顺序表(使用定长数组存储元素)、动态顺序表(使用动态开辟的数组存储)。
2.代码实现
静态顺序表只适用于知道需要存储多少数据的场景,空间设大了会浪费,设小了会储存不下。故而接下来我们实现更为频繁使用的动态顺序表。
(1)顺序表结构体
typedef int SLDatatype; //重命名int,方便更改存储数据类型
typedef struct SeqList
{
SLDatatype* a; // 指向动态开辟的数组
int size; //有效数据个数
int capacity; //容量空间的大小
}SL;
(2)功能实现
//所有功能如下
//初始化
void SeqListInit(SL* psl);
//摧毁
void SeqListDestroy(SL* psl);
//尾插
void SeqListPushBack(SL* psl, SLDatatype x);
//头插
void SeqListPushFront(SL* psl, SLDatatype x);
//尾删
void SeqListPopBack(SL* psl);
//头删
void SeqListPopFront(SL* psl);
//打印
void SeqListPrint(SL* psl);
//中间插入
void SeqListInsert(SL* psl, int pos, SLDatatype x);
//中间删除
void SeqListErase(SL* psl, int pos);
//查找 --- 找到返回下标,没有找到返回-1
int SeqListFind(SL* psl, SLDatatype x);
//修改
void SeqListModify(SL* psl, int pos, SLDatatype x);
void SeqListInit(SL* psl)
{
psl->a = (SLDatatype*)malloc(sizeof(SLDatatype) * 4);
if (psl->a == NULL)
{
perror("malloc fail");
return;
}
psl->capacity = 4;
psl->size = 0;
}
void SeqListDestroy(SL* psl)
{
free(psl->a);
psl->a = NULL;
psl->size = 0;
psl->capacity = 0;
}
void SeqListPrint(SL* psl)
{
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->a[i]);
}
printf("\n");
}
// 检查数组空间是否足够,是否需要开辟空间
void SeqListCheckCapacity(SL* psl)
{
if (psl->size == psl->capacity)
{
SLDatatype* tmp = (SLDatatype*)realloc(psl->a, sizeof(SLDatatype) * psl->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
psl->a = tmp;
psl->capacity *= 2;
}
}
void SeqListPushBack(SL* psl, SLDatatype x)
{
//psl->a[psl->size] = x;
//psl->size++;
//SeqListCheckCapacity(psl);
//psl->a[psl->size++] = x;
SeqListInsert(psl, psl->size, x); // 函数复用
}
void SeqListPushFront(SL* psl, SLDatatype x)
{
//SeqListCheckCapacity(psl);
//
//int end = psl->size - 1;
//while (end >= 0)
//{
// psl->a[end + 1] = psl->a[end];
// --end;
//}
//psl->a[0] = x;
SeqListInsert(psl, 0, x); // 函数复用
}
void SeqListPopBack(SL* psl)
{
//暴力检查
//assert(psl->size = 0);
//温柔的检查
//if (psl->size == 0)
// return;
//psl->a[psl->size - 1] = 0;
//psl->size--;
SeqListErase(psl, psl->size); // 函数复用
}
void SeqListPopFront(SL* psl)
{
//暴力检查
//assert(psl->size = 0);
//int start = 0;
//while (start < psl->size)
//{
// psl->a[start] = psl->a[start + 1];
// start++;
//}
//psl->size--;
SeqListErase(psl, 0); // 函数复用
}
void SeqListInsert(SL* psl, int pos, SLDatatype x)
{
assert(0 <= pos && pos <= psl->size);
SeqListCheckCapacity(psl);
int end = psl->size - 1;
while (end >= pos)
{
psl->a[end + 1] = psl->a[end];
end--;
}
psl->a[pos] = x;
psl->size++;
}
void SeqListErase(SL* psl, int pos)
{
assert(0 <= pos && pos < psl->size);
//asser(psl->size = 0);
int start = pos + 1;
while (start < psl->size)
{
psl->a[start - 1] = psl->a[start];
start++;
}
psl->size--;
}
int SeqListFind(SL* psl, SLDatatype x)
{
for (int i = 0; i < psl->size; i++)
{
if (psl->a[i] == x)
{
return i;
}
}
return -1;
}
void SeqListModify(SL* psl, int pos, SLDatatype x)
{
assert(0 <= pos && pos < psl->size);
psl->a[pos] = x;
}
二.链表
1.链表的概念及结构
链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表在逻辑上是连续的,但是物理上不一定是连续的。它的一个个结点一般是从堆上申请出来的。从堆上申请的空间,是按照一定的策略来分配的,两次申请空间可能连续,也可能不连续。
2.链表的分类
(1)单向或者双向
(2)带头或者不带头
(3)循环或者非循环
以上情况组合下来就有8种链表结构,但是实际中最常见的还是两种结构:无头单项非循环链表和带头双向循环链表。
1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。
3.代码实现
(1)无头单项非循环链表
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
//打印
void SLTPrint(SLTNode* phead);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SLTPopFront(SLTNode** pphead);
//尾删
void SLTPopBack(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在pos位置之后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//单链表删除pos位置之后的值
pos的next
void SLTEraseAfter(SLTNode* pos);
//单链表的销毁
void SLTDestroy(SLTNode** pphead);
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
while (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//改变 链表内容,传入二级指针才能改变phead
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
// assert(*pphead); // 不能断言,链表为空,也需要插入
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLTNode(x);
SLTNode* tail = *pphead;
if (tail == NULL)
{
*pphead = newnode;
}
else
{
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SLTPopFront(SLTNode** pphead)
{
assert(pphead);//链表为空,pphead也不为空,因为他是头指针plist的地址
assert(*pphead);// 链表为空,不能头删
SLTNode* del = (*pphead);
*pphead = (*pphead)->next;
free(del);
}
void SLTPopBack(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
while ((tail)->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
while (phead != NULL)
{
if (phead->data == x)
{
printf("找到了\n");
return phead;
}
else
{
phead = phead->next;
}
}
printf("链表中不存在\n");
}
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
SLTNode* newnode = BuySLTNode(x);
if (pos->next == NULL)
{
pos->next = newnode;
}
else
{
newnode->next = pos->next;
pos->next = newnode;
}
}
void SLTEraseAfter(SLTNode* pos)
{
if (pos->next == NULL)
{
return;
}
else
{
SLTNode* tmp = pos->next;
pos->next = pos->next->next;
free(tmp);
tmp = NULL;
}
}
void SLTDestroy(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
(2)带头双向循环链表
typedef int LTDataType;
typedef struct ListNode
{
LTNode* next;
LTNode* prev;
LTDataType data;
}LTNode;
// 链表初始化
void LTNodeInit(LTNode** pphead);
// 链表打印
void LTNodePrint(LTNode* phead);
// 链表尾插
void LTNodePushBack(LTNode *phead, LTDataType x);
//链表头插
void LTNodePushFront(LTNode* phead, LTDataType x);
//链表头删
void LTNodePopFront(LTNode* phead);
//链表尾删
void LTNodePopBack(LTNode* phead);
//链表查找
LTNode* ListFind(LTNode* phead, LTDataType x);
//链表在pos的前面进行插入
void LTNodeInsert(LTNode* pos, LTDataType x);
//链表删除pos位置的节点
void LTNodeErase(LTNode* pos);
//链表销毁
void LTNodeDestory(LTNode* phead);
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
//传入二级指针
void LTNodeInit(LTNode** pphead)
{
*pphead = BuyLTNode(-1);
(*pphead)->next = *pphead;
(*pphead)->prev = *pphead;
}
// 传入一级指针
//LTNode* LTNodeInit(LTNode* phead)
//{
// phead = BuyLTNode(-1);
//
// phead->next = phead;
// phead->prev = phead;
//
// return phead;
//}
void LTNodePrint(LTNode* phead)
{
assert(phead);
printf("guard<==>");
LTNode* cur = phead->next;
while (cur != phead->prev)
{
printf("%d<==>", cur->data);
cur = cur->next;
}
if (cur != phead)
{
printf("%d\n", cur->data);
}
}
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
void LTNodePushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNodeInsert(phead, x);
//LTNode* tail = phead->prev;
//LTNode* newnode = BuyLTNode(x);
//
//tail->next = newnode;
//newnode->prev = tail;
//newnode->next = phead;
//phead->prev = newnode;
}
void LTNodePushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNodeInsert(phead->next, x);
//LTNode* newnode = BuyLTNode(x);
//newnode->next = phead->next;
//phead->next->prev = newnode;
//phead->next = newnode;
//newnode->prev = phead;
}
void LTNodePopFront(LTNode* phead)
{
assert(phead);
//assert((phead->next) != phead);
assert(!LTEmpty(phead));
LTNodeErase(phead->next);
//LTNode* tmp = phead->next;
//phead->next->next->prev = phead;
//phead->next = phead->next->next;
//free(tmp);
}
void LTNodePopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNodeErase(phead->prev);
//phead->prev = phead->prev->prev;
//free(phead->prev->next);
//phead->prev->next = phead;
}
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead;
while (cur)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
void LTNodeInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyLTNode(x);
newnode->prev = pos->prev;
newnode->next = pos;
pos->prev->next = newnode;
pos->prev = newnode;
}
void LTNodeErase(LTNode* pos)
{
assert(pos);
//pos->next->prev = pos->prev;
//pos->prev->next = pos->next;
LTNode* posPrev = pos->prev;
LTNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
void LTNodeDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* temp = cur->next;
free(cur);
cur = temp;
}
free(phead);
}