数据结构,无非是对数据进行包装和管理。即实现增删查改等基本功能,因此我们只需了解其中内在逻辑,如何设计具体的代码,可依据个人的喜好使逻辑自洽。
1.顺序表
1.1关键点
顺序表一般说来是针对物理空间上连续的数据进行管理,比如数组。
因此通过数组下标操作可基本实现增删查改。
值得注意
①由于空间的弹性,空间不足我们需要开辟新的空间
②前插前删,需要整体挪动数据
typedef int Seqdate;
typedef struct s
{
Seqdate* pr;//储存数据的地址
int size;//数据的个数
int capacity;//申请的空间
}sl;
void Seqinit(sl* ptr);//初始化
void SeqDestory(sl* ptr);//顺序表销毁
void Seqinsertfront(sl* ptr, Seqdate m);//头插
void Seqinsertback(sl* ptr, Seqdate m);//尾插
void Seqpopfront(sl* ptr);//头删
void Seqpopback(sl* ptr);//尾删
void Seqcheckcapacity(sl* ptr);//每次插入时都要检查空间大小
void Print(sl* ptr);//打印
int SeqListFind(sl* ptr, Seqdate x);//查找数据,返回下标
void SeqListInsert(sl* ptr, int pos, Seqdate x);//特定位置前插//后插
void SeqListErase(sl* ptr, int pos);//删除特定位置
1.2代码实现
void SeqDestory(sl* ptr)
{
if (ptr->pr != NULL)
{
free(ptr->pr);
ptr->pr = NULL;
ptr->capacity = 0;
ptr->size = 0;
}
return;
}
void Seqinit(sl* ptr)
{
ptr->pr = NULL;
ptr->size = 0;
ptr->capacity = 0;
return;
}
void Seqcheckcapacity(sl* ptr)
{
if (ptr->capacity == ptr->size)
{
int newcapacity = ptr->capacity == 0 ? 4 : 2 * ptr->capacity;
Seqdate* tem = (Seqdate*)realloc(ptr->pr, newcapacity * sizeof(Seqdate));
assert(tem);
ptr->pr = tem;
tem = NULL;
ptr->capacity = newcapacity;
}
assert(ptr->pr);
}
void Seqinsertfront(sl* ptr, Seqdate m)
{
Seqcheckcapacity(ptr);
int last = ptr->size - 1;
while (last >= 0)
{
*(ptr->pr + last + 1) = *(ptr->pr + last);
last--;
}
ptr->pr[0] = m;
ptr->size++;
return;
}
void Seqinsertback(sl* ptr, Seqdate m)
{
Seqcheckcapacity(ptr);
ptr->pr[ptr->size] = m;
ptr->size++;
return;
}
void Seqpopback(sl* ptr)
{
Seqcheckcapacity(ptr);
if (ptr->size > 0)
{
ptr->size--;
}
return;
}
void Seqpopfront(sl* ptr)
{
Seqcheckcapacity(ptr);
int begin = 0;
while (begin < ptr->size - 1)
{
ptr->pr[begin] = ptr->pr[begin + 1];
begin++;
}
ptr->size--;
return;
}
void Print(sl* ptr)
{
for (int i = 0;i < ptr->size;i++)
{
printf("%d ", ptr->pr[i]);
}
printf("\n");
return;
}
int SeqListFind(sl* ptr, Seqdate x)
{
int pos = 0;
while (pos < ptr->size)
{
if (ptr->pr[pos] == x)
return pos;
pos++;
}
return -1;
}
void SeqListInsert(sl* ptr, int pos, Seqdate x)
{
assert(ptr);
assert(pos >= 0);
Seqcheckcapacity(ptr);
int right = ptr->size - 1;
while (right >= pos)
{
ptr->pr[right + 1] = ptr->pr[right];
right--;
}
ptr->pr[pos] = x;
ptr->size++;
}
void SeqListErase(sl* ptr, int pos)
{
assert(ptr);
assert(pos >= 0);
int left = pos + 1;
while (left <= ptr->size - 1)
{
ptr->pr[left-1] = ptr->pr[left];
left++;
}
ptr->size--;
}
2.链表
2.1无头不循环单链表
2.1.1关键点
顺序表物理空间上的连续性决定了它只需一个结构体、一个指针来维护
而链表物理空间上的不连续决定它需要多个结构体、多个指针来维护。
因此链表维护的时间复杂度较高,但空间不受限制。
typedef int SLNodeDate;
typedef struct SLS
{
SLNodeDate val;
struct SLS* next;//指向下一个节点
} SLN;
SLN* SLNcteatnode(SLNodeDate x);//创建节点并初始化
void SLNpushfront(SLN** pphead, SLNodeDate x);//头插
void SLNpushback(SLN** pphead, SLNodeDate x);//尾插
void SLNpopfront(SLN** pphead);//头删
void SLNpopback(SLN** pphead);//尾删
SLN* SLNfind(SLN* phead, SLNodeDate x);//查找
void SLNInsert(SLN** phead, SLN* pos, SLNodeDate x);//目标位置前插或后插
void SLNerase(SLN** pphead, SLN* pos);//删除目标位置
void SLNPrint(SLN* phead);//打印链表
void SLNDestory(SLN** phead);//链表销毁
值得注意
①修改变量,函数传变量的地址,即一级指针;
修改指针,传指针的地址,即二级指针
②增删都有可能涉及头指针的修改,故传二级指针
③链表销毁,避免出现野指针,也需要传二级指针
④循环找尾节点
2.1.2代码实现
SLN* SLNcteatnode(SLNodeDate x)
{
SLN* str = (SLN*)malloc(sizeof(SLN));
if (str == NULL)
{
perror("malloc fail");
exit(-1);
}
str->val = x;
str->next = NULL;
return str;
}
void SLNpushfront(SLN** pphead, SLNodeDate x)
{
assert(pphead);
SLN* newnode=SLNcteatnode(x);
assert(newnode);
newnode->next = *pphead;
*pphead = newnode;
}
void SLNpushback(SLN** pphead, SLNodeDate x)
{
assert(pphead);
SLN* newnode=SLNcteatnode(x);
SLN* cur = *(pphead);
if (cur == NULL)
*pphead = newnode;
else
{
while (cur->next!=NULL)
{
cur = cur->next;
}
cur->next = newnode;
}
}
void SLNpopfront(SLN** pphead)
{
assert(*pphead);
SLN* cur = *pphead;
*pphead = (*pphead)->next;
free(cur);
}
void SLNpopback(SLN** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLN* cur = *pphead;
SLN* prev = NULL;
while (cur->next!=NULL)
{
prev = cur;
cur = cur->next;
}
free(cur);
prev->next = NULL;
}
}
SLN* SLNfind(SLN* phead, SLNodeDate x)
{
assert(phead);
SLN* pos = phead;
while (pos != NULL)
{
if (pos->val == x)
{
return pos;
}
pos = pos->next;
}
return NULL;
}
void SLNInsert(SLN** phead, SLN* pos, SLNodeDate x)
{
assert(phead);
assert(*phead);
assert(pos);
SLN* cur = *phead;
SLN* newnode = SLNcteatnode(x);
if (cur == pos)
{
newnode->next = cur;
*phead = newnode;
}
else
{
while (cur->next != pos)
{
cur = cur->next;
}
newnode->next = pos;
cur->next = newnode;
}
}
void SLNerase(SLN** pphead, SLN* pos)
{
assert(pphead);
assert(*pphead);
if (*pphead == pos)
{
free(pos);
*pphead = NULL;
}
else
{
SLN* cur = *pphead;
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
pos = NULL;
}
}
void SLNPrint(SLN* phead)
{
SLN* cur = phead;
while (cur)
{
printf("%d->", cur->val);
cur = cur->next;
}
printf("NULL\n");
}
void SLNDestory(SLN** phead)
{
SLN* next = (*phead)->next;
SLN* cur = *phead;
while (cur != NULL)
{
next = cur->next;
free(cur);
cur = next;
}
*phead = NULL;
}
2.2有头循环双向链表
2.2.1关键点
双向链表每个节点需要两个指针维护
用空间换取时间使得数据访问十分方便
// 带头+双向+循环链表增删查改实现
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);
2.2.2代码实现
ListNode* ListCreate()
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
assert(newnode);
newnode->next = newnode;
newnode->prev = newnode;
return newnode;
}
void ListDestory(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
ListNode* next = cur->next;
while (cur != pHead)
{
free(cur);
cur = next;
next = cur->next;
}
free(cur);
}
void ListPrint(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
printf("ڱλ<->");
while (cur != pHead)
{
printf("%d<->", cur->_data);
cur = cur->next;
}
printf("\n");
}
void ListPushBack(ListNode* pHead, LTDataType x)
{
ListNode* cur = ListCreate();
cur->_data = x;
pHead->prev->next = cur;
cur->prev = pHead->prev;
pHead->prev = cur;
cur->next = pHead;
}
void ListPopBack(ListNode* pHead)
{
assert(pHead);
if (pHead->next == pHead)
return;
ListNode* last = pHead->prev;
pHead->prev->prev->next = pHead;
pHead->prev = pHead->prev->prev;
free(last);
}
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur=ListCreate();
assert(cur);
cur->_data = x;
cur->next = pHead->next;
pHead->next->prev = cur;
pHead->next = cur;
cur->prev = pHead;
}
void ListPopFront(ListNode* pHead)
{
assert(pHead);
if (pHead->next == pHead)
return;
ListNode* cur = pHead->next;
pHead->next->next->prev = pHead;
pHead->next = pHead->next->next;
free(cur);
}
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;
}
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* cur = ListCreate();
cur->_data = x;
pos->prev->next = cur;
cur->prev = pos->prev;
cur->next = pos;
pos->prev = cur;
}
void ListErase(ListNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
}
3.栈和队列
栈和队列,是基于上述两种数据结构的,既有数组栈也有链表栈,队列也一样
但栈必须符合先入后出,队列符合先进先出
3.1栈
3.1.1关键点
既然是缩减版,那只需符合先进后出的逻辑即可
数组栈:
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶,也可以理解为size
int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
3.1.2代码实现
void StackInit(Stack* ps)
{
assert(ps);
ps->_a = NULL;
ps->_capacity = 0;
//栈顶指向元素下一个
ps->_top = 0;
//栈顶指向栈顶元素
//ps->top=-1;
}
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->_capacity == ps->_top)
{
//判断空间大小
int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
STDataType* ptr = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
assert(ptr);
ps->_a = ptr;
ps->_capacity = newcapacity;
}
//栈顶指向下一位
ps->_a[ps->_top] =x;
//栈顶指向顶元素
//ps->_a[ps->_top+1]=x;
ps->_top++;
}
void StackPop(Stack* ps)
{
assert(ps);
//判断栈是否为空
if (ps->_top == 0)
return;
ps->_top--;
}
STDataType StackTop(Stack* ps)
{
assert(ps);
//断言不为空
assert(ps->_top > 0);
return ps->_a[ps->_top - 1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->_top;
}
int StackEmpty(Stack* ps)
{
assert(ps);
return ps->_top == 0;
}
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->_a);
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
3.2队列
3.2.1关键点
链表队列:
为了时间上的方便,我们可以把头尾指针以及数据个数放在一个结构体里维护
typedef int Qdate;
typedef struct STL
{
struct STL* next;
Qdate val;
}ST;
typedef struct Queue
{
ST* phead;
ST* ptail;
int size;
}QT;
void QueueInit(QT* ptr);//初始化
void QueuePush(QT* ptr, Qdate x);//尾插
bool QueueEmpty(QT* ptr);//判断是否空
void QueuePop(QT* ptr);//头删
int QueueSize(QT* ptr);//队列大小
void QueueDestroy(QT* ptr);//队列销毁
Qdate QueueFront(QT* ptr);//获取头部数据
Qdate QueueBack(QT* ptr);//获取尾部数据
3.2.2代码实现
void QueueInit(QT* ptr)
{
assert(ptr);
ptr->phead = ptr->ptail = NULL;
ptr->size = 0;
}
void QueuePush(QT* ptr, Qdate x)
{
assert(ptr);
ST* newnode = (ST*)malloc(sizeof(ST));
assert(newnode);
newnode->val = x;
newnode->next = NULL;
if (QueueEmpty(ptr))
{
ptr->phead = ptr->ptail = newnode;
}
else
{
ptr->ptail->next = newnode;
ptr->ptail = newnode;
}
ptr->size++;
}
bool QueueEmpty(QT* ptr)
{
assert(ptr);
return ptr->size == 0;
}
void QueuePop(QT* ptr)
{
assert(ptr);
assert(!QueueEmpty(ptr));
ST* cur = ptr->phead;
ptr->phead = ptr->phead->next;
free(cur);
if (ptr->phead == NULL)
ptr->ptail = NULL;
ptr->size--;
}
int QueueSize(QT* ptr)
{
assert(ptr);
return ptr->size;
}
void QueueDestroy(QT* ptr)
{
assert(ptr);
ST* cur = ptr->phead;
while (cur)
{
ST* next = cur->next;
free(cur);
cur = next;
}
ptr->phead = ptr->ptail = NULL;
ptr->size = 0;
}
Qdate QueueFront(QT* ptr)
{
assert(ptr);
assert(ptr->size > 0);
return ptr->phead->val;
}
Qdate QueueBack(QT* ptr)
{
assert(ptr);
assert(ptr->size > 0);
return ptr->ptail->val;
}
3.3模拟实现
有意思的是二者的相互实现
用栈模拟队列
增加数据,栈和队列同样尾插;
删除数据,栈尾删,队列头删。
设置两个栈,一个入,一个出,出的不够用,将入的反方向叠罗汉给出,出的尾删,即入的头删
用队列模拟栈
一样,两个队列,非空队列用来导数据,空队列用来出数据
3.4循环队列
循环队列就是限制大小,链接首尾,将空间循环利用
数组队列,虽然空间上不循环,但可以通过%运算,使其限制在指定容量中
循环双向链表,虽然空间上循环,但实现过程复杂。
数组队列
①%运算决定了开辟k+1空间,余数在0-k之间
②front==back,则队列为空
③(back+1) % (k+1) ==front ,则队列满
typedef struct {
int* a;
int front;//指向头
int back;//指向尾的下一位
int k;//容量
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* ptr=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
ptr->a=(int*)malloc(sizeof(int)*(k+1));
ptr->front=0;
ptr->back=0;
ptr->k=k;
return ptr;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
if(obj->front==obj->back)
return true;
return false;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
if((obj->back+1)%(obj->k+1)==obj->front)
return true;
return false;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//插入数据
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->back]=value;
obj->back=(obj->back+1)%(obj->k+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//删除数据
if(myCircularQueueIsEmpty(obj))
return false;
obj->front=(obj->front+1)%(obj->k+1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {//取front
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {//取back
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[(obj->back+obj->k)%(obj->k+1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
链表队列
①size== 0,则队列为空
②size== k,则队列满
③怎么判断头插还是尾插比较关键,故直接采用循环链表,创建k个节点,用phead、ptail指向,控制头尾指针实现数据管理
typedef struct Node
{
struct Node* next;
int val;
}node;
typedef struct {
struct Node* phead;//指向头有效节点
struct Node* ptail;//指向尾有效节点
int k;//队列最大值
int size;//记录数据个数
} MyCircularQueue;
node* Mycreatenode(int x)
{
node* ptr=(node*)malloc(sizeof(node));
ptr->val=x;
ptr->next=NULL;
return ptr;
}
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* ptr=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
ptr->phead=Mycreatenode(0);
int i=0;
node* cur=ptr->phead;
while(i<k-1)//再创建k-1个节点
{
cur->next=Mycreatenode(0);
cur=cur->next;
i++;
}
cur->next=ptr->phead;//形成循环
ptr->ptail=ptr->phead;
ptr->k=k;
ptr->size=0;
return ptr;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
if(obj->size==0)
return true;
return false;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
if(obj->size==obj->k)
return true;
return false;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
//空的情况特殊,删完后,phead后移了,但ptail没动
if(myCircularQueueIsEmpty(obj))
{
obj->ptail=obj->phead;
obj->phead->val=value;
}
else
{
obj->ptail->next->val=value;
obj->ptail=obj->ptail->next;
}
obj->size++;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
obj->phead=obj->phead->next;
obj->size--;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->phead->val;
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->ptail->val;
}
void myCircularQueueFree(MyCircularQueue* obj) {
int i=0;
while(i<obj->k)
{
node* next=obj->phead->next;
free(obj->phead);
obj->phead=next;
i++;
}
obj->phead=NULL;
obj->ptail=NULL;
obj->size=0;
obj->k=0;
}