目录
顺序表
理解顺序表之前应该掌握线性表的含义:: 线性表:: 是n个具有相同特性的数据元素的有限序列 常见的线性表有:顺序表 链表 栈 队列 这四个本次都会以代码的形式逐一实现。线性表在逻辑上是连续的 但是在真实的物理存储机构上却不一定是连续的,通常以数组或者是链式结构的形式存储,数组存储就是接下来说的顺序表,而链式存储就是紧接着的链表。
(1)顺序表:: 顺序表就是之前用到的数组,只不过在数组的基础上,顺序表是要求数据从头开始存的顺序存储方式,不能跳跃间隔 在内存上表现为一段连续的物理地址。
顺序表的实现比较简单,之前的电话本代码也用到了相关的结构。。 因顺序表的代码比较简单这里就直接给出了
typedef int SLDataType;
// 动态顺序表
typedef struct SeqList
{
SLDataType* a;
int size; // 表示数组中存储了多少个数据
int capacity; // 数组实际能存数据的空间容量是多大
}SL;
// 接口函数
void SeqListPrint(SL* ps);
void SeqListInit(SL* ps);
void SeqListDestory(SL* ps);
void SeqListCheckCapacity(SL* ps);
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
// ...
// 找到了返回x位置下标,没有找打返回-1
int SeqListFind(SL* ps, SLDataType x);
// 指定pos下标位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);
// 删除pos位置的数据
void SeqListErase(SL* ps, int pos);
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; ++i)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SeqListDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
void SeqListCheckCapacity(SL* ps)
{
// 如果没有空间或者空间不足,那么我们就扩容
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
void SeqListPopBack(SL* ps)
{
if (ps->size > 0)
{
//ps->a[ps->size - 1] = 0;
ps->size--;
}
暴力处理方式
//assert(ps->size > 0);
//ps->size--;
}
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
// 挪动数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
// 挪动数据
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++ begin;
}
ps->size--;
}
链表
动态的顺序表是比静态的顺序表有扩容的优势的 但是动态的顺序表也会存在空间的浪费,为此我们引出了链表,链表先说单链表,当然了 链表相比顺序表来说也有它不可避免的缺点,下面先比较链表与顺序表的优缺点 :
顺序表优点: 支持随机访问,缓存利用率高 缺点是 : 浪费空间。
链表的优点,: 他的空间利用率比顺序表大,缺点是 不支持随机访问, 链表在逻辑上是连续的 但是在物理内存上是不连续的 你可以认为链表是由一个个的节点构成的 节点中即保存了该节点的内容 也有指向下一个节点的指针,当然了链表是不能进行随机访问的 这也是它的一大缺点。
单链表的代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include <assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SLTNode;
void SListPrint(SLTNode* phead);
void SListPushBack(SLTNode** pphead, SLTDateType x);
void SListPushFront(SLTNode** pphead, SLTDateType x);
void SListPopBack(SLTNode** pphead);
void SListPopFront(SLTNode** pphead);
SLTNode* SListFind(SLTNode* phead, SLTDateType x);
void SListInsertAfter(SLTNode* pos, SLTDateType x);
// 在pos位置之前去插入一个节点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);
void SListErase(SLTNode** pphead, SLTNode* pos);
void SListEraseAfter(SLTNode* pos);
void SListDestory(SLTNode** pphead);
SLTNode* BuyListNode(SLTDateType x)
{
// 1M = 1024KB = 1024 * 1024 Byte
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void SListPushBack(SLTNode** pphead, SLTDateType x)
{
assert(pphead);
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
// 找到尾节点
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SLTNode** pphead, SLTDateType x)
{
assert(pphead);
SLTNode* newnode = BuyListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SListPopBack(SLTNode** pphead)
{
assert(pphead);
// 温柔的一点
/*if (*pphead == NULL)
{
return;
}*/
// 粗暴一点
assert(*pphead != NULL);
// 1、一个节点
// 2、两个及以上节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListPopFront(SLTNode** pphead)
{
assert(pphead);
//if (*pphead == NULL)
// return;
assert(*pphead != NULL);
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
// 在pos的后面插入,这个更适合,也更简单
void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
assert(pos);
SLTNode* newnode = BuyListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
// 在pos位置之前去插入一个节点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
assert(pphead);
assert(pos);
SLTNode* newnode = BuyListNode(x);
if (*pphead == pos)
{
newnode->next = *pphead;
*pphead = newnode;
}
else
{
// 找到pos的前一个位置
SLTNode* posPrev = *pphead;
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}
posPrev->next = newnode;
newnode->next = pos;
}
}
void SListErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
/**pphead = pos->next;
free(pos);*/
SListPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
//next = NULL;
}
void SListDestory(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
int main() {
SLTNode* a = NULL;
SListPushBack(&a, 6);
SListPrint(a);
system("pause");
return 0;
}
附上几个简单的链表oj题目
(1)
这个反转链表的t题目可以认为是讲 1 2 3 4 5 一个拿下来 只不过这次 2指向1 1指向一个空
那么我们可以定义一个新的newhead 代码演示
sltnode*cur=head
sltnode*newhead=NULL;
while(cur)
{
//先保存cur 下一个节点的位置
sltnode*next=cur->next;
cur->next=newhead;
newhead=cur;
cur=cur->next;
}
(2)
链表的中间节点这里就记住一个点 定义一个fast 和一个slow fasf每次走两步 而slow每次走一步 那么fast 走到头,slow走一半
sltnode*fast=head;
sltnode*slow=head; //fast 要跟slow在同一位置出发
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
}
return slow;
(3)
第三个题目可以说更简单了 让输出该链表中倒数第k个节点。 那我们就定义两个指针,一个先走k步,另外一个再开始走,先走的那个走到最后,后走的那个正好到他的倒数第K个点
sltnode*fast=head;
sltnode*slow=head;
while(k)
{
fast=fast->next;//当然了在这里最好再加上判断k的值是否大于链表的长度。
k--;
}
while(fast&&fast->next)
{
fast=fast->next;
slow=slow->next;
}
return slow;
(4)
合并两个有序链表 是要理解归并的思想 可以先从两个链表如n1 n2中先选取头最小的那个当合并链表的头部 其他的依次比较链接就可以 比较简单 直接给代码
sltnode* n1=head1;
sltnode*n2=head2;
if(n1==NULL)
{
return n2;
}
if(n2==NULL)
{
return n1;
}
//接着比较选取新链表的头部第一个数据
sltnode*newhead;
if(n1->data < n2->data)
{
newhead = n1;
n1 = n1->nextl;
}
esle
{
newhead = n2;
n2 = n2->next;
}
sltnode*tail=newhead;
while( n1 && n2)
{
if(n1->data<n2->data)
{
tail->next=n1;
n1=n1->next;
tail=tail->next;
}
else
{
tail->next=n2;
n2=n2->next;
tail=tail->next;
}
if(n1)
{
tail->next=n1;
}
if(n2)
{
tail->next=n2;
}
return newhead;
}
(5)
聊便分割的主要思想是因为要求小于x的结点排在其余节点之前, 那么可以从建立两个链表指针出发 用到两个哨兵位 遍历原链表 将小于z值的节点放在一起 将大于z值的节点放在一起 然后拼接 这样就能保证既不改变原有的数据顺序 。又满足题目要求
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
ListNode*lessstart=(ListNode*)malloc(sizeof(ListNode*));
ListNode*lesshead=lessstart;
ListNode*greatstart=(ListNode*)malloc(sizeof(ListNode*));
ListNode*greathead=greatstart;
ListNode*cur=pHead;
if(pHead==NULL)
{
return NULL;
}
while(cur)
{
if(cur->val < x)
{
lesshead->next=cur;
lesshead=cur;
cur=cur->next;
}
else
{
greathead->next=cur;
greathead=cur;
cur=cur->next;
}
}
lesshead->next=greatstart->next;
greathead->next=NULL;
ListNode*HEAD=lessstart->next;
free(lessstart);
free(greatstart);
return HEAD;
}
};
(6)
本题解决的时候三个要点, 第一个要点是先找到该链表的中点坐标,这个在之前的fast走两步,slow走一步可以办得到, 其次在反转中点后的链表,这个之前的例题也可以办得到,最后遍历翻转后的链表实施一一对比比较。
ListNode* foundmidposition(ListNode* A)
{
ListNode*fast=A;
ListNode*slow=A;
while(fast->next&&fast)
{
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
ListNode* revesrelinklist(ListNode* A)
{
ListNode*newhead=NULL;
ListNode*cur=A;
while(cur)
{
ListNode*tmp=cur->next;
cur->next=newhead;
newhead=cur;
cur=tmp;;
}
return newhead;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A)
{
ListNode* cur= A;
// write code here
//整体思想 先找中点 ,其次将一个反转 然后再逐一比较::
ListNode* aa=foundmidposition(cur);
ListNode* bb=revesrelinklist(aa);
while(bb)
{
if((bb->val)==(cur->val))
{
bb=bb->next;
cur=cur->next;
}
else
{
return false;
}
}
return true;
(7)
链表相交找交点的方法就是 第一步先算出 两个链表的长度 然后长度长的减去长度短的,先让长度长的走长出的部分,再一起走,一起走的时候 边走边比较
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *l1=headA;
struct ListNode *l2=headB;
struct ListNode *tailA=l1;
struct ListNode *tailB=l2;
int count1=1;
while(l1->next)
{
l1=l1->next;
++count1;
}
int count2=1;
while(l2->next)
{
l2=l2->next;
++count2;
}
int k=abs(count1-count2);
if(count1<count2)
{
while(k--)
{
tailB=tailB->next;
}
}
else
{
while(k--)
{
tailA=tailA->next;
}
}
while(tailA)
{
if(tailA==tailB)
{
return tailA;
}
else
{
tailA=tailA->next;
tailB=tailB->next;
}
}
return NULL;
}
判断链表是否有环
(8) 两步走,第一步是先走fast fast走两步 slow走一步 如果有环会相遇
bool hasCycle(struct ListNode *head)
{
struct ListNode *fast=head;
struct ListNode *slow=head;
while(fast && fast->next)
{
fast=fast->next->next;
if(fast==slow)
{
return true;
}
slow=slow->next;
}
return false;
}
(9)
struct Node* copyRandomList(struct Node* head) {
//先再原始的每一个节点后方copy一个新的节点
struct Node*cur=head;
struct Node*tmp;
while(cur)
{
struct Node*copy=(struct Node*)malloc(sizeof(struct Node));
copy->val=cur->val;
tmp=cur->next;
cur->next=copy;
copy->next=tmp;
cur=copy->next;
}
//再将random的值链接起来
cur=head;
while(cur)
{
struct Node* copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
cur=copy->next;
}
//分离
struct Node*copyhead=NULL,*copytail=NULL;
cur=head;
while(cur)
{
struct Node*copy=cur->next;
struct Node*next=copy->next;
if(copyhead==NULL)
{
copyhead=copytail=copy;
}
else
{
copytail->next=copy;
copytail=copy;
}
cur->next=next;
cur=next;
}
return copyhead;
}
struct Node* copyRandomList(struct Node* head) {
struct Node* cur=head;
if(cur==NULL)
{
return NULL;
}
while(cur)
{
struct Node*newnode=(struct Node*)malloc(sizeof(struct Node));
newnode->val=cur->val;
struct Node*nextt=cur->next;
cur->next=newnode
newnode->next=nextt;
}
cur=head;
while(cur)
{
struct Node*copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
cur=cur->next->next;
}
//分离
cur=head;
struct Node* copyhead=NULL;
struct Node*copytail=NULL;
while(cur)
{
struct Node* copy=cur->next;
struct Node* nextt=copy->next;
if(copyhead==NULL)
{
copyhead=copytail=copy;
}
else
{
copytail->next=copy;
copytail=copytail->next;
}
cur->next=nextt;
cur=nextt;
}
return copyhead;
}
二 双向链表的实现
双向链表在单链表的基础上 在增加一个指针 它的目的就是 一个结点 跟它前后的节点相互联系
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int LTDateType;
typedef struct ListNode
{
LTDateType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
LTNode* ListInit();
void ListPrint(LTNode* phead);
LTNode* BuyListNode(LTDateType x);
void ListPushBack(LTNode* phead, LTDateType x);
void ListPopBack(LTNode* phead);
void ListPushFront(LTNode* phead, LTDateType x);
void ListPopFront(LTNode* phead);
LTNode* ListFind(LTNode* phead, LTDateType x);
void ListInsert(LTNode* pos, LTDateType x);
void ListErase(LTNode* pos);
#include "List.h"
LTNode* ListInit()
{
// 哨兵位头结点
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
LTNode* BuyListNode(LTDateType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
void ListPushBack(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = BuyListNode(x);
// phead tail newnode
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//ListInsert(phead, x);
}
//void ListPopBack(LTNode* phead)
//{
// assert(phead);
// assert(phead->next != phead);
//
// LTNode* tail = phead->prev;
// LTNode* tailPrev = tail->prev;
// free(tail);
//
// tailPrev->next = phead;
// phead->prev = tailPrev;
//}
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* tail = phead->prev;
phead->prev = tail->prev;
tail->prev->next = phead;
free(tail);
}
void ListPushFront(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* next = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = next;
next->prev = newnode;
//ListInsert(phead->next, x);
}
void ListPopFront(LTNode* phead)
{
assert(phead);
// 链表空
assert(phead->next != phead);
LTNode* next = phead->next;
LTNode* nextNext = next->next;
phead->next = nextNext;
nextNext->prev = phead;
free(next);
}
LTNode* ListFind(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// pos位置之前插入
void ListInsert(LTNode* pos, LTDateType x)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* newnode = BuyListNode(x);
// posPrev newnode pos
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
// 删除pos位置
void ListErase(LTNode* pos)
{
assert(pos);
// ...
}
栈
栈的定义可以理解为是串糖葫芦,最上面的那个糖葫芦就是栈顶,而最下面的那个糖葫芦就是栈底,吃糖葫芦的时候,你从第一个开始吃,所以栈的元素也就满足先进后出的原则。(当然这里说的是栈在数据结构层次的定义,栈在内存角度的定义:
用来存放函数的参数,局部变量 ,函数的返回地址,寄存器内容 并且有操作系统自动收回,无需手动控制。 函数中定义的局部变量是按照先后定义的顺序入栈的,栈的生长方向是自上向下生长,后定义的变量地址是低于先定义的变量的地址的 。 栈的空间大小比堆小 64bits下的windows默认1mb 在Linux下默认10mb
栈的分配方式既有动态分配也有静态分配的 动态分配可以用alloca函数, 使用alloca 函数也不需要手动去释放 (注意与堆的realloc malloc calloc 函数区别 alloca 函数不常用 缺点是可移植性查劲,并且是操作不当容易爆栈)
栈由操作系统进行分配的 所以它的分配效率高,并且可以在硬件层堆栈提供支持:分配专门的寄存器存放栈的地址 压栈和出栈都有专门的指令来执行
————————————————
版权声明:本文为CSDN博主「craaazy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_53217755/article/details/121854289
)
栈的代码实现主要是进行
typedef struct Stack
{
int *a;
int sz;
int capacity;
}Stack;
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0; // ps->top = -1;
ps->capacity = 0;
}
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = realloc(ps->a, sizeof(STDataType)*newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
bool StackEmpty(ST* ps)
{
assert(ps);
/*if (ps->top == 0)
{
return true;
}
else
{
return false;
}*/
return ps->top == 0;
}
队列
队列即是先进先出的原则,只不过栈是一个指针,指向了一段连续的空间(当然栈也是由链式存储的方式),队列是两个指针,一个指针指向队列的头部,另一个指针指向队列的尾部。
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
// size_t _size;
}Queue;
//void QueueInit(QueueNode** pphead, QueueNode** pptail);
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur != NULL)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//尾插
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePop(Queue* pq)//头部删除
{
assert(pq);
//if (pq->head == NULL)
// return;
assert(!QueueEmpty(pq));
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
if (pq->head == NULL)
{
pq->tail = NULL;
}
}
QDataType QueueFront(Queue pq)//返回头部数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq.head->data;
}
QDataType QueueBack(Queue* pq)//返回尾部数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq) //计算数量
{
assert(pq);
int n = 0;
QueueNode* cur = pq->head;
while (cur)
{
++n;
cur = cur->next;
}
return n;
}
bool QueueEmpty(Queue* pq) //判断是否为空
{
assert(pq);
return pq->head == NULL;
}
oj练习题:用两个栈实现一个队列,用两个队列实现一个栈
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
// size_t _size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur != NULL)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//尾插
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePop(Queue* pq)//头部删除
{
assert(pq);
//if (pq->head == NULL)
// return;
assert(!QueueEmpty(pq));
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
if (pq->head == NULL)
{
pq->tail = NULL;
}
}
QDataType QueueFront(Queue* pq)//返回头部数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)//返回尾部数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq) //计算数量
{
assert(pq);
int n = 0;
QueueNode* cur = pq->head;
while (cur)
{
++n;
cur = cur->next;
}
return n;
}
bool QueueEmpty(Queue* pq) //判断是否为空
{
assert(pq);
return pq->head == NULL;
}
//先写一个队列 (先进先出 )
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack*tmp=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&(tmp->q1));
QueueInit(&tmp->q2);
return tmp;
}
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj) {
Queue*empty=&obj->q1;
Queue*noempty=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
empty=&obj->q2;
noempty=&obj->q1;
}
while(QueueSize(noempty)>1)
{
QueuePush(empty,QueueFront(noempty));
QueuePop(noempty);
}
int top=QueueFront(noempty);
QueuePop(noempty);
return top;
}
int myStackTop(MyStack* obj) {
//返回尾部数据 返回栈顶元素;
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
设计循环队列::
typedef struct {
int *a;
int front;
int tail;
int k;
} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue *cq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
cq->a=(int*)malloc(sizeof(int)*(k+1));
cq->front=cq->tail=0;
cq->k=k;
return cq;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->a[obj->tail]= value;
++obj->tail;
obj->tail %= ((obj->k)+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
obj->front++;
obj->front %= (obj->k+1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
int i=(obj->tail+obj->k) % (obj->k+1);
return obj->a[i];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->tail+1) % (obj->k+1)==obj->front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
堆
堆 分为 小堆和大堆 也是一个完全的二叉树 后面还会用到堆进行排序:代码实现
void Swap(HPDataType* px, HPDataType* py)
{
HPDataType tmp = *px;
*px = *py;
*py = tmp;
}
void HeapInit(HP* hp)
{
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
void HeapDestroy(HP* hp)
{
assert(hp);
free(hp->a);
hp->capacity = hp->size = 0;
}
void AdjustUp(int* a, int child)
{
assert(a);
int parent = (child - 1) / 2;
//while (parent >= 0)
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPrint(HP* hp)
{
for (int i = 0; i < hp->size; ++i)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}
void HeapPush(HP* hp, HPDataType x)
{
assert(hp);
if (hp->size == hp->capacity)
{
size_t newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDataType* tmp = realloc(hp->a, sizeof(HPDataType)*newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
hp->a = tmp;
hp->capacity = newCapacity;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp->a, hp->size - 1);
}
bool HeapEmpty(HP* hp)
{
assert(hp);
return hp->size == 0;
}
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}
HPDataType HeapTop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
return hp->a[0];
}
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
// 选出左右孩子中小的那一个 完全二叉树 左孩子存在 右孩子可能不存在
if (child + 1 < n && a[child + 1] < a[child])
{
++child;
}
// 如果小的孩子小于父亲,则交换,并继续向下调整
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
// 删除堆顶的数据
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
Swap(&hp->a[0], &hp->a[hp->size - 1]);
hp->size--;
AdjustDown(hp->a, hp->size, 0);
}
堆:内存角度上堆是由程序员进行操作的 他的内存分配地址是自下向上,注意堆里后申请的内存空间不一定在先申请的内存空间的后面,如果先申请的内存空间被释放,则后申请的内存空间会利用先前释放的内存,从而导致后分配的内存空间在地址上不存在先后关系。堆中存取的数据如果没有手动去释放那么他的生命周期等同于函数的声明周期
操作系统中存放一个记录空闲内存地址的链表,当程序进行申请空间时,会遍历该链表,找到第一个比空间比程序申请的空间大的堆节点,然后分配给程序,并且将这个堆节点删除,堆的分配方式只由动态分配,常见的由malloc realoc calloc 函数 它的分配效率相比栈是低的 原因是堆的内存分配容易造成 碎片化的内存空间 ,一般来说堆在堆顶的第一个字节处会存放堆的大小,其他内存由程序员指定,
数据结构的角度堆的是实现是分为大堆跟小堆的
————————————————
版权声明:本文为CSDN博主「craaazy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_53217755/article/details/121854289
void Swap(HPDataType* px, HPDataType* py)
{
HPDataType tmp = *px;
*px = *py;
*py = tmp;
}
void HeapInit(HP* hp)
{
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
void HeapDestroy(HP* hp)
{
assert(hp);
free(hp->a);
hp->capacity = hp->size = 0;
}
void AdjustUp(int* a, int child)
{
assert(a);
int parent = (child - 1) / 2;
//while (parent >= 0)
while (child > 0)
{
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPrint(HP* hp)
{
for (int i = 0; i < hp->size; ++i)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}
void HeapPush(HP* hp, HPDataType x)
{
assert(hp);
if (hp->size == hp->capacity)
{
size_t newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDataType* tmp = realloc(hp->a, sizeof(HPDataType)*newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
hp->a = tmp;
hp->capacity = newCapacity;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp->a, hp->size - 1);
}
bool HeapEmpty(HP* hp)
{
assert(hp);
return hp->size == 0;
}
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}
HPDataType HeapTop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
return hp->a[0];
}
void AdjustDown(int* a, int n, int parent)
{
int child = parent * 2 + 1;
while (child < n)
{
// 选出左右孩子中小的那一个 完全二叉树 左孩子存在 右孩子可能不存在
if (child + 1 < n && a[child + 1] < a[child])
{
++child;
}
// 如果小的孩子小于父亲,则交换,并继续向下调整
if (a[child] < a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
// 删除堆顶的数据
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
Swap(&hp->a[0], &hp->a[hp->size - 1]);
hp->size--;
AdjustDown(hp->a, hp->size, 0);
}
二叉树:
二叉树的性质:
1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对 应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树