C++学习笔记day18-----数据结构与算法

本文介绍了队列和链表两种基本数据结构。详细解释了单向队列、循环队列的概念及其实现方法,并提供了核心的操作函数。此外,还介绍了链表的基本原理,包括双向链表的特点和优势,以及链表的各种操作方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

队列类型的数据结构:
队列类型是一种先进先出的数据结构,只能在队列的头部取出数据,在队尾存入数据。下面介绍两种基于数组实现的队列。给出了队列的操作方法(函数)。
单向队列:

#include"queue.h"
typedef struct{
    int buf[SIZE];
    int head;//表示最前面的数字所在存储区的下标,如果队列里没有数据的时候,head>应该等于tail
    int tail;//下一个数字应该放置的存储区的下标
}queue;
//队列的初始化函数
void queue_init(queue* p_queue){
    p_queue->head = 0;
    p_queue->tail = 0;
}
//队列的清理函数
void queue_deinit(queue* p_queue){
    p_queue->head = 0;
    p_queue->tail = 0;
}
//获得队列个数的函数
int queue_size(const queue* p_queue){
    return p_queue->tail - p_queue->head;
}
//判断队列是否满的函数
int queue_full(const queue* p_queue){
    if (p_queue->tail >= SIZE)
        return 1;
    else
        return 0;
}
//判断队列是否空的函数
int queue_empty(const queue* p_queue){
    if(p_queue->head == p_queue->tail)
        return 1;
    else
        return 0;
}
//向队列里加入数字的函数
int queue_push(queue* p_queue,int value){
    if(queue_full(p_queue))
        return 0;
    p_queue->buf[p_queue->tail] = value;
    p_queue->tail += 1;
    return 1;
}
//从队列里获得数字的函数
int queue_pop(queue* p_queue,int* p_value){
    if(queue_empty(p_queue))
        return 0;
    *p_value = p_queue->buf[p_queue->head];
    p_queue->head += 1;
    return 1;
}
//从队列获得一个数字,但不删除
int queue_front(const queue* p_queue,int* p_value){
    if(queue_empty(p_queue))
        return 0;
    *p_value = p_queue->buf[p_queue->head];
    return 1;
}

循环队列:
循环队列的要点在于怎么处理队列头和尾的循环,在以下代码中,通过在每次对队列头下标值累加的时候判断其是否超出了队列长度,如果超出就将队列的头和尾的下标减去队列长度,确保队列头和尾的下标值不会超过int类型的上限(头和尾是一直累加的,如果不管,多次循环之后必定超出int类型上限)。

#include"queuep.h"
typedef struct{
    int num[SIZE];
    int head;
    int tail;
}queuep;

//初始化队列
void queuep_init(queuep* p_queue){
    p_queue->head = 0;
    p_queue->tail = 0;
}
//清除队列
void queuep_deinit(queuep* p_queue){
    p_queue->head = 0;
    p_queue->tail = 0;
}
//计算队列当前的长度
int queuep_size(const queuep* p_queue){
    return p_queue->tail - p_queue->head;
}
//判断当前队列是否为满
int queuep_full(const queuep* p_queue){
    if((p_queue->tail - p_queue->head) == SIZE)
        return 1;
    else
        return 0;
}
//判断当前队列是否为空
int queuep_empty(const queuep* p_queue){
    if(p_queue->head == p_queue->tail)
        return 1;
    else
        return 0;
}
//向队尾填入数据
int queuep_push(queuep* p_queue,int value){
    if(queuep_full(p_queue))
        return 0;
    //这里对队尾的下标取余是为了保证,当队尾下标超出SIZE之后还是可以正常存入数据
    p_queue->num[p_queue->tail % SIZE] = value;
    p_queue->tail++;
    return 1;
}
//从队列头部弹出数据
int queuep_pop(queuep* p_queue,int* p_value){
    if(queuep_empty(p_queue))
        return 0;
    //这里对队列头部的取余,其实是没有必要的,一旦上一次的取值使得头部下标超出数组长度,那么就会被下面的代码重置,但是为了保证代码的一致性,还是写上好了。
    *p_value = p_queue->num[p_queue->head % SIZE];
    p_queue->head++;
    if(p_queue->head == SIZE){
        p_queue->tail -= SIZE;
        p_queue->head -= SIZE;
    }
    return 1;
}
//从队列头部获得数据,但是不弹出
int queuep_front(const queuep* p_queue,int* p_value){
    if(queuep_empty(p_queue))
        return 0;
    *p_value = p_queue->num[p_queue->head%SIZE];
    return 1;
}

链表类型的数据结构
链表的类型,其实是由一个个节点组成的,链表自身的结构体只记录链表的头尾节点。每个节点中,记录了当前节点内存放的数据和指向下一个节点的节点指针。
当然,也可以在节点的结构体中加入记录前一个节点的指针。如果这样做,那么一个节点里记录前一个节点的地址和后一个节点的地址,就变成了双向链表。
双向链表的优势在于,对链表尾部处理的时候,不需要从链表的头节点遍历到链表尾部,再处理,而是可以通过节点的前向指针,直接从尾部开始处理。
下面提供操作双向链表的方法(函数)

//链表初始化
void link_init(link* p_link){
    p_link->head.p_next = &(p_link->tail);
    p_link->head.p_prev = NULL;
    p_link->tail.p_prev = &(p_link->head);
    p_link->tail.p_next = NULL;
    p_link->p_cur = NULL;
}
//清理链表
void link_deinit(link* p_link){
    p_link->p_cur = NULL;
    while(p_link->head.p_next != &(p_link->tail)){
        node* p_first = &(p_link->head);
        node* p_mid = p_first->p_next;
        node* p_last = p_mid->p_next;
        p_first->p_next = p_last;
        p_last->p_prev = p_first;
        free(p_mid);
        p_mid = NULL;
    }
}
//获得链表里数据个数
int link_size(const link* p_link){
    int count = 0;
    const node* temp = {0};
    temp = &(p_link->head);
    while(temp->p_next != &(p_link->tail)){
        count++;
        temp = temp->p_next;
    }
    return count;
}
//判断链表是否空的函数
int link_empty(const link* p_link){
    if(p_link->head.p_next == &(p_link->tail))
        return 1;
    return 0;
}

//判断链表是否满的函数
int link_full(const link* p_link){
    return 0;
}
//插入在最前面擦入新数据的函数
int link_add_head(link* p_link,int value){
    node* p_first = NULL,*p_mid = NULL,*p_last = NULL;
    node* p_node = NULL;
    p_link->p_cur = NULL;
    p_node = (node*)malloc(sizeof(node));
    if(!p_node) return 0;
    p_node->num = value;
    p_node->p_next = NULL;
    p_node->p_prev = NULL;
    p_first = &(p_link->head);
    p_mid = p_first->p_next;
    p_last = p_mid->p_next;
    p_node->p_next = p_mid;
    p_mid->p_prev = p_node;
    p_node->p_prev = p_first;
    p_first->p_next = p_node;

    return 1;
}
//在最后面加入新数字的函数
int link_add_tail(link* p_link,int value){
    node* p_first = NULL,*p_mid = NULL,*p_last = NULL,*p_temp = NULL;
    node* p_node = NULL;
    p_link->p_cur = NULL;
    p_node = (node*)malloc(sizeof(node));
    if(!p_node) return 0;
    p_node->num = value;
    p_node->p_next = NULL;
    p_node->p_prev = NULL;

    p_first = p_link->tail.p_prev;
    p_mid = p_first->p_next;
    p_last = p_mid->p_next;

    p_first->p_next = p_node;
    p_node->p_prev = p_first;
    p_node->p_next = p_mid;
    p_mid->p_prev = p_node;
    return 1;
}
//在链表里按顺序插入新数字的函数
int link_insert(link* p_link,int value){
    node* p_first = NULL,*p_mid = NULL,*p_last = NULL,*p_temp = NULL;
    node* p_node = NULL;
    p_link->p_cur = NULL;
    p_node = (node*)malloc(sizeof(node));
    if(!p_link) return 0;
    p_node->num = value;
    p_node->p_next = NULL;
    p_node->p_prev = NULL;
    for(p_temp = &(p_link->head);p_temp != &(p_link->tail);p_temp = p_temp->p_next){
        p_first = p_temp;
        p_mid = p_first->p_next;
        p_last = p_mid->p_next;
        if(((p_first->num < p_node->num) && (p_mid->num > p_node->num)) || p_mid == &(p_link->tail)){
            p_first->p_next = p_node;
            p_node->p_prev = p_first;
            p_node->p_next = p_mid;
            p_mid->p_prev = p_node;
            break;
        }
    }
    return 1;
}
//删除最前面有效节点的函数
int link_remove_head(link* p_link){
    p_link->p_cur = NULL;
    if(link_empty(p_link)) return 0;
    node* p_first = &(p_link->head);
    node* p_mid = p_first->p_next;
    node* p_last = p_mid->p_next;
    p_first->p_next = p_last;
    p_last->p_prev = p_first;
    free(p_mid);
    p_mid = NULL;
    return 1;
}
//删除最后面有效节点的函数
int link_remove_tail(link* p_link){
    p_link->p_cur = NULL;
    if(link_empty(p_link)) return 0;
    node* p_last = &(p_link->tail);
    node* p_mid = p_last->p_prev;
    node* p_first = p_mid->p_prev;

    p_first->p_next = p_last;
    p_last->p_prev = p_first;

    free(p_mid);
    p_mid = NULL;
    return 1;
}
//删除顺序链表中的某个值的节点
int link_remove(link* p_link,int value){
    p_link->p_cur = NULL;
    if(link_empty(p_link)) return 0;
    node* p_temp = {0};
    for(p_temp = &(p_link->head);p_temp != &(p_link->tail);p_temp = p_temp->p_next){
        node* p_first = p_temp;
        node* p_mid = p_first->p_next;
        node* p_last = p_mid->p_next;
        if(p_mid->num == value && p_mid != &(p_link->tail)){
            p_first->p_next = p_last;
            p_last->p_prev = p_first;
            free(p_mid);
            p_mid = NULL;
            return 1;
        }
    }
    return 0;
}
//从链表里获得第一个数字的函数
int link_get_head(const link* p_link,int* value){
    if(link_empty(p_link)) return 0;
    const node* p_first = &(p_link->head);
    const node* p_mid = p_first->p_next;
    const node* p_last = p_mid->p_next;
    *value = p_mid->num;
    return 1;
}
//从链表末尾获得一个数字
int link_get_tail(const link* p_link,int* value){
    if(link_empty(p_link)) return 0;
    const node* p_last = &(p_link->tail);
    const node* p_mid = p_last->p_prev;
    const node* p_first = p_mid->p_prev;
    *value = p_mid->num;
    return 1;
}
//根据下标获得一个数字
int link_get(const link* p_link,int num,int* value){
    int count = 0;
    const node* p_temp = {0};
    if(link_empty(p_link)) return 0;
    for(p_temp = &(p_link->head);p_temp != &(p_link->tail);p_temp = p_temp->p_next){
        const node* p_first = p_temp;
        const node* p_mid = p_first->p_next;
        const node* p_last = p_mid->p_next;
        if(num == count && p_mid != &(p_link->tail)){
            *value = p_mid->num;
            return 1;
        }
        count++;
    }
    return 0;
}
//开始从前向后遍历所有节点
void link_begin(link* p_link){
    p_link->p_cur = &(p_link->head);
}
//在遍历过程中,获得下一个节点里的数字
int link_next(link* p_link,int* p_value){
    if(p_link->p_cur == NULL) return 0;
    p_link->p_cur = p_link->p_cur->p_next;
    if(p_link->p_cur == &(p_link->tail)){
        p_link->p_cur = NULL;
        return 0;
    }
    else{
        *p_value = p_link->p_cur->num;
        return 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值