目录
2.双向循环带头链表
线性表
线性表是n个具有形同特性的数据元素的有限序列。常见的线性表:顺序表、链表、栈,队列、字符串……本文重点讲解顺序表和链表,其他结构可见数据结构专栏的其他内容
注意:线性表在逻辑上是线性结构,也就是说是连续的但在物理结构上。但是在物理结构上并不一定是连续的,线性表在物 理上存储时,通常以数组和链式结构的形式存储。
顺序表
概念及结构
顺序表是用一段
物理地址连续
的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可分为静态顺序表和动态顺序表
1.静态顺序表:使用定长数组存储元素
具体实现见动态顺序表,静态算是动态的简略版本,通常动态顺序表的使用场景更多。
2. 动态顺序表:使用动态开辟的数组存储。
动态顺序表实现
头文件:SeqList.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <iostream>
using namespace std;
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* date;
int size;
int capacity;
}SL,SeqList;
//打印顺序表
void SeqListPrint(SeqList* psl);
//初始化顺序表
void SeqListInit(SeqList* psl);
//销毁顺序表
void SeqListDestroy(SeqList* psl);
//检查容量
void SeqListChackCapacity(SeqList* psl);
//尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
//尾删
void SeqListPopBack(SeqList* psl);
//头插
void SeqListPushFront(SeqList* psl, SLDataType x);
//头删
void SeqListPopFront(SeqList* psl);
//在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
//删除pos位置的数据
void SeqListErase(SeqList* psl, size_t pos);
//顺序查找
int SeqListFind(SeqList* psl, SLDataType x);
源文件:SeqList.cpp
#include"SeqList.h"
void SeqListPrint(SeqList* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
cout << psl->date[i] << " ";
}
cout << endl;
}
void SeqListInit(SeqList* psl)
{
assert(psl);
psl->date = nullptr;
psl->size = 0;
psl->capacity = 0;
}
void SeqListDestroy(SeqList* psl)
{
assert(psl);
free(psl->date);
psl->size = 0;
psl->capacity = 0;
}
void SeqListChackCapacity(SeqList* psl)
{
assert(psl);
if (psl->size == psl->capacity)
{
size_t newCapaicty = psl->capacity == 0 ? 4 : psl->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(psl->date, newCapaicty * sizeof(SLDataType));
if (tmp == nullptr)
{
printf("realloc fail\n");
exit(-1);
}
else
{
psl->date = tmp;
psl->capacity = newCapaicty;
}
}
}
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListChackCapacity(psl);
psl->date[psl->size] = x;
psl->size++;
}
void SeqListPopBack(SeqList* psl)
{
assert(psl);
if (psl->size > 0)
{
psl->date[psl->size - 1] = 0;
psl->size--;
}
}
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListChackCapacity(psl);
for (int i = psl->size; i > 0; i--)
{
psl->date[i] = psl->date[i - 1];
}
psl->date[0] = x;
psl->size++;
}
void SeqListPopFront(SeqList* psl)
{
assert(psl);
if (psl->size > 0)
{
for (int i = 0; i < psl->size-1; i++)
{
psl->date[i] = psl->date[i + 1];
}
--psl->size;
}
}
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
if (pos > psl->size)
{
cout << "pos 越界:" << pos << endl;
return;
}
SeqListChackCapacity(psl);
for (int i = psl->size; i > pos; i--)
{
psl->date[i] = psl->date[i - 1];
}
psl->date[pos] = x;
psl->size++;
}
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
if (pos >= psl->size)
{
cout << "pos 越界:" << pos << endl;
return;
}
for (int i = pos; i < psl->size-1; i++)
{
psl->date[i] = psl->date[i + 1];
}
psl->size--;
}
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->date[i] == x)
{
return i;
}
}
return -1;
}
链表
链表的概念及结构
概念:链表是一种
物理存储结构上非连续
、非顺序的存储结构,数据元素的
逻辑顺序
是通过链表中的
指针链
接
次序实现的 。
![](https://img-blog.csdnimg.cn/337fc435739e43aa807f0b3a0d19dc26.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWFzc2FjaHVzZXR0c18xMQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
注意:
1.从上图看,链式结构在逻辑上是连续的,但在物理上不一定是连续的
2.现实中节点一般都是从堆上申请的
3.从堆上申请的空间是按照一定策略来分配的,两次申请的空间可能连续也可能不连续。
链表的分类
实际上链表是非常多样的,通过不同组合可形成8钟组合:
1.单向或者双向
2.带头或者不带头
带头:即把代表链表开头的头指针换成一个空的节点,这个节点一般不进行存储数据,只用来方便后续指针的尾接。
3.循环或者非循环
虽然能组合出很多链表,但我们实际中最常用的还是两种结构,下面我们也只对这两种量表进行实现,通过结合这两种结构的实现完全可实现其他所有链表
链表的实现
无头单向非循环链表
头文件:SList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <iostream>
using namespace std;
typedef int SLDataType;
typedef struct SListNode
{
SLDataType date;
struct SListNode* next;
}SLN;
//打印
void SListPrint(SLN* phead);
//尾插
void SListPushBack(SLN** pphead, SLDataType x);
//头插
void SListPushFront(SLN** pphead, SLDataType x);
//尾删
void SListPopBack(SListNode** pphead);
//头删
void SListPopFront(SListNode** pphead);
//查找并返回地址
SListNode* SListFind(SListNode* phead, SLDataType x);
// 在pos位置之前插入
void SListInsert(SListNode** pphead, SListNode* pos, SLDataType x);
// 删除pos 位置
void SListErase(SListNode** pphead, SListNode* pos);
// 在pos之后插入
void SListInsertAfter(SListNode* pos, SLDataType x);
// 删除pos位置后面的值
void SListEraseAfter(SListNode* pos);
//释放数组
void SListDestroy(SListNode** pphead);
源文件:SList.cpp
#include "SList.h"
void SListPrint(SLN* phead)
{
SLN* cur = phead;
while (cur != NULL)
{
cout << cur->date<<"->";
cur = cur->next;
}
cout << "NULL"<<endl;
}
void SListPushBack(SLN** pphead, SLDataType x)
{
SLN* tmp = (SLN*)malloc(sizeof(SLN));
tmp->date = x;
tmp->next = NULL;
if (*pphead == NULL)
{
if (tmp == NULL)
{
exit(-1);
}
*pphead = tmp;
}
else
{
SLN* cur = *pphead;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = tmp;
}
}
void SListPopFront(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL)
{
return;
}
else
{
SListNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
}
}
void SListPopBack(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL)
{
return;
}
SLN* cur = *pphead;
while (cur->next->next)
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
void SListPushFront(SLN** pphead, SLDataType x)
{
assert(pphead);
if (*pphead == NULL)
{
return ;
}
SLN* newnode = (SLN*)malloc(sizeof(SLN));
newnode->date = x;
newnode->next = *pphead;
*pphead = newnode;
}
SListNode* SListFind(SListNode* phead, SLDataType x)
{
if (phead == NULL)
{
return NULL;
}
SListNode* cur = phead;
while (cur)
{
if (cur->date == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListInsert(SListNode** pphead, SListNode* pos, SLDataType x)
{
assert(pphead);
if (*pphead == NULL || pos == NULL)
{
return;
}
SListNode* cur = *pphead;
while (cur->next)
{
if (cur->next == pos)
{
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
newNode->date = x;
cur->next = newNode;
newNode->next = pos;
return;
}
cur = cur->next;
}
return;
}
void SListErase(SListNode** pphead, SListNode* pos)
{
assert(pphead);
if (*pphead == NULL || pos == NULL)
{
return;
}
SListNode* cur = *pphead;
while (cur->next)
{
if (cur->next == pos)
{
cur->next = pos->next;
free(pos);
return;
}
cur = cur->next;
}
return;
}
void SListInsertAfter(SListNode* pos, SLDataType x)
{
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
newNode->date = x;
if (pos == NULL)
{
return;
}
else
{
SListNode* next = pos->next;
pos->next = newNode;
newNode->next = next;
}
}
void SListEraseAfter(SListNode* pos)
{
if (pos == NULL)
{
return;
}
else
{
SListNode* next = pos->next->next;
free(pos->next);
pos->next = next;
}
}
void SListDestroy(SListNode** pphead)
{
assert(pphead);
SListNode* cur = *pphead;
SListNode* next = NULL;
while (cur)
{
next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
双向循环带头链表
头文件:
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <iostream>
using namespace std;
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
源文件:List.cpp
#include"List.h"
ListNode* ListCreate()
{
ListNode* newlist = (ListNode*)malloc(sizeof(ListNode));
assert(newlist);
newlist->_data = 0;
newlist->_next = newlist;
newlist->_prev = newlist;
return newlist;
}
void ListDestory(ListNode* pHead)
{
assert(pHead);
ListNode* next = NULL;
ListNode* cur = pHead;
while (cur!=pHead)
{
next = cur->_next;
free(cur);
cur = NULL;
}
}
void ListPrint(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
cout << "NULL"<<endl;
}
void ListPushBack(ListNode* pHead, LTDataType x)
{
ListInsert(pHead, x);
}
void ListPushFront(ListNode* pHead, LTDataType x)
{
ListInsert(pHead->_next,x);
}
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newlist = ListCreate();
newlist->_data = x;
newlist->_next = pos;
newlist->_prev = pos->_prev;
pos->_prev->_next = newlist;
pos->_prev = newlist;
}
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->_prev;
ListNode* next = pos->_next;
free(pos);
prev->_next = next;
next->_prev = prev;
}
void ListPopBack(ListNode* pHead)
{
ListErase(pHead->_prev);
}
void ListPopFront(ListNode* pHead)
{
ListErase(pHead->_next);
}
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
if (cur->_data == x)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}