入栈、出栈与队列

1.二级指针

1.在被调函数中需要修改主调函数的指针变量时,需传递其指针地址;

2.指针数组的数组名作为参数传递时;

2.内核链表

(1)定义

内核链表:本质为循环双向链表,其没有数据域

存储形式:将节点嵌于结构体内(数据中),而不是将数据存于节点内;

(2)内核链表的相关宏

offset_of

作用:获取节点到结构体首地址的偏移量;

原理:假设结构体的首地址为0,则节点的首地址即为其到结构体首地址的偏移量;

container_of

作用:通过偏移量,获取结构体的首地址;

原理:用节点首地址减去偏移量;

一般定义内核链表时,将节点置于结构体首部,此时节点首地址即为结构体首地址;

3.栈

先进后出的存储方式;

满栈、空栈:栈顶是否存储数据;

增栈:入栈时,栈顶由内存地地址-->内存高地址,出栈反之;

减栈:栈顶的运动方式与增栈相反;

对象:包含头地址与元素个数;

节点:包含元素与指向下一个节点的指针;

栈相关的代码练习:


typedef int Data_type_t;

typedef struct stnode 
{
    Data_type_t data;
    struct stnode *pnext;
}Stnode_t;

typedef struct stack
{
    Stnode_t *ptop;
    int clen;
}Stack_t;
Stack_t *c_stack()
{
    Stack_t *stack = malloc(sizeof(Stack_t));
    if(NULL == stack)
    {
        printf("error\n");
        return NULL;
    }

    stack->ptop = NULL;
    stack->clen = 0;

    return stack;
}

int is_empty_stack(Stack_t *stack)
{
    return NULL == stack->ptop ;
}

void stack_for_each(Stack_t *stack)
{
    Stnode_t *ptmp = stack->ptop;

    while (ptmp)
    {
        printf("%d ", ptmp->data);
        ptmp = ptmp->pnext;
    }
    puts("");
    
}

//入栈
int push_stack(Stack_t *stack, Data_type_t data)
{
    Stnode_t *pinst = malloc(sizeof(Stnode_t));
    if(NULL == pinst)
    {
        printf("error\n");
        return -1;
    }

    pinst->pnext =NULL;
    pinst->data = data;

    if(is_empty_stack(stack))
    {
        stack->ptop = pinst;
    }
    else
    {
        pinst->pnext = stack->ptop;
        stack->ptop = pinst;
    }

    stack->clen++;
    return 0;
}

//出栈
int pop_stack(Stack_t *stack, Data_type_t *pdata)
{
    if(is_empty_stack(stack))
    {
        return -1;
    }

    Stnode_t *ptmp = stack->ptop;
    stack->ptop = ptmp->pnext;
    if(pdata)
    {
        *pdata = ptmp->data;
        printf("pop : %d\n", *pdata);
    }

    free(ptmp);
    stack->clen--;
    
    return 0;
}

//获取栈元素
int get_top_stack(Stack_t *stack, Data_type_t *pdata)
{
    if(is_empty_stack(stack))
    {
        return -1;
    }
    if(pdata && stack->ptop->data)
    {
        *pdata = stack->ptop->data;
        printf("top : %d\n", *pdata);
        return *pdata;
    }
}

//销毁
void destroy_stack(Stack_t **stack, Data_type_t *pdata)
{
    if(is_empty_stack(*stack))
    {
        return ;
    }
    while ((*stack)->ptop)
    {
        pop_stack(*stack, pdata);
    }

    free(*stack);
    stack =NULL;
    
    return ;
}

4.队列

(1)链式队列

定义:允许从一段进行数据的插入,从另一端进行数据的删除的线性存储结构;

插入:称为入队,插入的一端即为队尾;

删除:称为出队,删除的一端即为队头;

特点:先进先出;

应用:应用于缓存数据;

对象:包含头地址、尾地址与元素个数;

节点:包含元素个数与指向下一个节点的指针;

代码练习

#ifndef __LQUEUE_H__
#define __LQUEUE_H__

typedef int Data_type_t;

typedef struct lqnode
{
    Data_type_t data;
    struct lqnode *pnext;
}Lqnode_t;

typedef struct lqueue
{
    Lqnode_t *phead;
    Lqnode_t *ptail;
    int clen;
}Lqueue_t;

extern Lqueue_t *c_lqueue();
extern int is_lqueue_empty(Lqueue_t *lqueue);
extern int insert_lqueue_tail(Lqueue_t *lqueue, Data_type_t data);
extern void lqueue_for_each(Lqueue_t *lqueue);
extern int delet_lqueue_head(Lqueue_t *lqueue, Data_type_t *pdata);
extern int get_top_lqueue(Lqueue_t *lqueue, Data_type_t *hdata);
extern void destroy_lqueue(Lqueue_t **lqueue);


#endif

#include <stdio.h>
#include <stdlib.h>
#include "lqueue.h"

//创建
Lqueue_t *c_lqueue()
{
    Lqueue_t *lqueue = malloc(sizeof(Lqueue_t));
    if(NULL == lqueue)
    {
        printf("malloc error\n");
        return NULL;
    }

    lqueue->phead = NULL;
    lqueue->ptail = NULL;
    lqueue->clen = 0;  

    return lqueue;
}

//判断队列是否为空
int is_lqueue_empty(Lqueue_t *lqueue)
{
    return NULL == lqueue->phead;
}

//遍历
void lqueue_for_each(Lqueue_t *lqueue)
{
    if(is_lqueue_empty(lqueue))
    {
        return ;
    }

    Lqnode_t *ptmp = lqueue->phead;
    while(ptmp)
    {
        printf("%d ", ptmp->data);
        ptmp = ptmp->pnext;
    }
    puts("");
}

//入队(尾插)
int insert_lqueue_tail(Lqueue_t *lqueue, Data_type_t data)
{
    Lqnode_t *pinset = malloc(sizeof(Lqnode_t));
    if(NULL == pinset)
    {
        printf("malloc error\n");
        return -1;
    }

    pinset->pnext  = NULL;
    pinset->data = data;

    if(is_lqueue_empty(lqueue))
    {
        lqueue->phead = pinset;
        lqueue->ptail = pinset;
    }
    else
    {
        lqueue->ptail->pnext = pinset;
        lqueue->ptail = pinset;
    }
    lqueue->clen++;

    return 0;
}

//出队(头删)
int delet_lqueue_head(Lqueue_t *lqueue, Data_type_t *pdata)
{
    if(is_lqueue_empty(lqueue))
    {
        return -1;
    }
    
    Lqnode_t *pdet = lqueue->phead;
    if(NULL != pdata)
    {
        *pdata = lqueue->phead->data;
    }

    lqueue->phead = pdet->pnext;
    if(NULL == pdet->pnext)
    {
        lqueue->ptail = NULL;
    }
    
    free(pdet);
    lqueue->clen--;
    
    return 0;
}

//获取队头元素
int get_top_lqueue(Lqueue_t *lqueue, Data_type_t *hdata)
{
    if(is_lqueue_empty(lqueue))
    {
        return -1;
    }

    if(hdata && lqueue->phead->data)
    {
        *hdata = lqueue->phead->data;
        printf("top : %d\n", *hdata);
    }
    return 0;
}

//销毁
void destroy_lqueue(Lqueue_t **lqueue)
{
    while((*lqueue)->phead)
    {
        delet_lqueue_head(*lqueue, NULL);
    }

    free(*lqueue);
    *lqueue =  NULL;
}


int main(void)
{
    Lqueue_t *lqueue = c_lqueue();
    if(NULL == lqueue)
    {
        printf("malloc error\n");
        return -1;
    }
    Data_type_t hdata;
    insert_lqueue_tail(lqueue, 1);
    insert_lqueue_tail(lqueue, 2);
    insert_lqueue_tail(lqueue, 3);
    insert_lqueue_tail(lqueue, 4);
    insert_lqueue_tail(lqueue, 5);
    
    lqueue_for_each(lqueue);

    delet_lqueue_head(lqueue, &hdata);
    lqueue_for_each(lqueue);
   
   
    get_top_lqueue(lqueue, &hdata);

    destroy_lqueue(&lqueue);

    return 0;
}

(2)循环队列

代码练习


#define SEQQUE_MAX_LEN 10

typedef int Data_type_t;

typedef struct seqque
{
    Data_type_t *pbase;
    int head;
    int tail;
}Seqque_t;



Seqque_t *create_seqque()
{
    Seqque_t *psq = malloc(sizeof(Seqque_t));
    if (NULL == psq)
    {
        printf("malloc error\n");
        return NULL;
    }
    psq->head = 0;
    psq->tail = 0;
    psq->pbase = malloc(sizeof(Data_type_t) * SEQQUE_MAX_LEN);
    if (NULL == psq->pbase)
    {
        printf("malloc error\n");
        return NULL;
    }

    return psq;
}

int is_full_seqque(Seqque_t *psq)
{
    if ((psq->tail+1)%SEQQUE_MAX_LEN == psq->head)
    {
        return 1;
    }
    return 0;
}

int is_empty_seqque(Seqque_t *psq)
{
    if (psq->head == psq->tail)
    {
        return 1;
    }
    return 0;
}

int push_seqque(Seqque_t *psq, Data_type_t data)
{
    if (is_full_seqque(psq))
    {
        printf("seqque is full\n");
        return -1;
    }

    psq->pbase[psq->tail] = data;
    psq->tail = (psq->tail+1) % SEQQUE_MAX_LEN;

    return 0;
}

void seqque_for_each(Seqque_t *psq)
{
    for (int i = psq->head; i != psq->tail; i = (i+1)%SEQQUE_MAX_LEN)
    {
        printf("%d ", psq->pbase[i]);
    }
    printf("\n");
}

//获取队头元素
int get_top_seqque(Seqque_t *psq, Data_type_t *data)
{
    if(is_empty_seqque(psq))
    {
        return -1;
    }
    
    if(NULL != data)
    {
        *data = psq->pbase[psq->head];
        printf("top : %d\n", *data);
    }

    return 0;
}

//出队
int pop_seqque(Seqque_t *psq, Data_type_t *data)
{
    if(is_empty_seqque(psq))
    {
        return -1;
    }

    if(NULL != data)
    {
        *data = psq->pbase[psq->head];
    }
    
    psq->head = (psq->head + 1) % SEQQUE_MAX_LEN;

    return 0;
}

//销毁
void destroy_seqque(Seqque_t **psq)
{
    free((*psq)->pbase);
    free(*psq);
    *psq = NULL;
}

int main(void)
{
    Seqque_t *psq = create_seqque();
    if (NULL == psq)
    {
        return -1;
    }

    for (int i = 0; i < 10; i++)
    {
        push_seqque(psq, i);
    }

    seqque_for_each(psq);
    
    Data_type_t data;
    get_top_seqque(psq, &data);

    pop_seqque(psq, &data);
    pop_seqque(psq, &data);
    seqque_for_each(psq);
    push_seqque(psq, 9);
    seqque_for_each(psq);
    
    destroy_seqque(&psq);
    
    return 0;
}

(3)队列与栈的区别

核心操作原则

  • 栈 (Stack):
    后进先出 (LIFO - Last In First Out)

    • 像一摞盘子:最后放上去的盘子(栈顶),总是最先被取走。

    • 操作只在一端(栈顶)进行:push(压栈/入栈)、pop(弹栈/出栈)、peek/top(查看栈顶)。

  • 队列 (Queue):
    先进先出 (FIFO - First In First Out)

    • 像排队:最早排队的人(队头),最先被服务离开。

    • 操作在两端进行:enqueue(入队)在队尾dequeue(出队)在队头peek/front(查看队头)。

操作位置

  • 栈: 所有有效操作(增、删、查)都集中在同一端(栈顶)

  • 队列: 插入(enqueue)发生在队尾 (Rear),删除(dequeue)发生在队头 (Front)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值