数据结构-队列,循环队列,哈希表

一. 队列(queue)

1.1 概念

**队列:**允许从一端进行数据的插入,另一端进行数据删除的线性存储结构

**特点:**先进先出(FIFO)

**用处:**一般用于缓存数据

1.2 队列的实现

1.2.1 队列的声明

cpp 复制代码
typedef int DataType_t;

//队列节点
typedef struct queuenode{
    DataType_t data;
    struct queuenode *pnext;
}QNode_t;

//队列对象
typedef struct queue{
    QNode_t *phead;
    QNode_t *ptail;
    int clen;
}Queue_t;

1.2.2 创建队列对象与节点、队列判空

cpp 复制代码
//创建队列对象
Queue_t *creat_queue()
{
    Queue_t *pqueue = malloc(sizeof(Queue_t));
    if(pqueue == NULL){
        printf("malloc error\n");
        return NULL;
    }
    pqueue->phead = NULL;
    pqueue->ptail = NULL;
    pqueue->clen = 0;
    return pqueue;
}

//创建队列节点
QNode_t *creat_queuenode(DataType_t data)
{
    QNode_t *newnode = malloc(sizeof(QNode_t));
    if(newnode == NULL){
        printf("malloc error\n");
        return NULL;
    }
    newnode->pnext = NULL;
    newnode->data = data;
    return newnode;
}
//判断队列为空
bool is_empty_queue(Queue_t *pqueue)
{
    return pqueue->clen == 0;
}

1.2.3 队尾入队列

cpp 复制代码
//队尾入队列
int queue_push(Queue_t *pqueue, DataType_t data)
{
    QNode_t *newnode = creat_queuenode(data);
    if(newnode == NULL){
        return -1;
    }
    if(is_empty_queue(pqueue)){
        pqueue->phead = newnode;
        pqueue->ptail = newnode;
    }
    else{
        pqueue->ptail->pnext = newnode;
        pqueue->ptail = newnode;
    }
    pqueue->clen++;
    return 0;
}

1.2.4 队头出队列

cpp 复制代码
//队头出队列
int queue_pop(Queue_t *pqueue, DataType_t *pdata)
{
    if(is_empty_queue(pqueue)){
        return -1;
    }
    QNode_t *pfree = pqueue->phead;
    pqueue->phead = pfree->pnext;
    if(pfree->pnext == NULL){
        pqueue->ptail = NULL;
    }
    if(pdata){
        *pdata = pfree->data;
    }
    free(pfree);
    pfree = NULL;
    pqueue->clen--;
    return 0;
}

1.2.5 获取队头元素

cpp 复制代码
//获取队头元素
int get_queue_top_element(Queue_t *pqueue, DataType_t *pdata)
{
    if(is_empty_queue(pqueue)){
        return -1;
    }
    *pdata = pqueue->phead->data;
    return 0;
}

1.2.6 打印队列

cpp 复制代码
//打印队列
void print_queue(Queue_t *pqueue)
{
    if(is_empty_queue(pqueue)){
        return;
    }
    QNode_t *cur = pqueue->phead;
    while(cur){
        printf("%d->",cur->data);
        cur = cur->pnext;
    }
    printf("NULL\n");
}

1.2.7 销毁队列

cpp 复制代码
//销毁队列
int destroy_queue(Queue_t **ppqueue)
{
    while(!is_empty_queue(*ppqueue)){
        queue_pop(*ppqueue, NULL);
    }
    free(*ppqueue);
    *ppqueue = NULL;
    return 0;
}

二. 循环队列

2.1 普通顺序队列

普通顺序队列存在的问题:假溢出

**假溢出:**队列数组没满(队头出过队列),但是此时继续入队列,会导致rear越界,导致不能入队

列。

**原因:**普通数组队列是直线结构,不能复用前面的空间

2.2 循环队列

队头与队尾相连

注意:循环队列中有一个存储单元不存储数据,用于队列判满

2.2.1 循环队列的实现

1、循环队列的声明
cpp 复制代码
#define MAX_QUEUE_LEN 6

typedef int DataType_t;

//循环队列对象
typedef struct sysqueue{
    DataType_t *pbase;
    int head;
    int tail;
}SQueue_t;
2、循环队列的创建
cpp 复制代码
//创建循环队列
SQueue_t *creat_sysqueue()
{
    SQueue_t *psqueue = malloc(sizeof(SQueue_t));
    if(psqueue == NULL){
        printf("malloc error\n");
        return NULL;
    }
    psqueue->pbase = malloc(MAX_QUEUE_LEN * sizeof(DataType_t));
    if(psqueue->pbase == NULL){
        printf("malloc error\n");
        return NULL;
    }
    psqueue->head = psqueue->tail = 0;
    return psqueue;
}
3、循环队列的判空与判满

**判空:**当队头下标等于队尾下标时,则队列为空

**判满:**当队尾下标加一等于队头下标时,则队列为满

cpp 复制代码
//循环队列判空
int is_empty_sysqueue(SQueue_t *psqueue)
{
    return psqueue->tail == psqueue->head;
}

//循环队列满
int is_full_sysqueue(SQueue_t *psqueue)
{
    return (((psqueue->tail + 1) % MAX_QUEUE_LEN) == psqueue->head);
}
4、循环队列的遍历
cpp 复制代码
//打印循环队列
void print_sysqueue(SQueue_t *psqueue)
{
    if(is_empty_sysqueue(psqueue)){
        return;
    }
    int cur = psqueue->head;
    printf("head->");
    //while(cur < psqueue->tail){
    while(cur != psqueue->tail){
        printf("%d->",psqueue->pbase[cur]);
        cur = (cur + 1) % MAX_QUEUE_LEN; 
    }
    printf("tail\n");
}
5、循环队列的队尾入队列
cpp 复制代码
//循环队列队尾入队
int sysqueue_push(SQueue_t *psqueue, DataType_t data)
{
    if(is_full_sysqueue(psqueue)){
        return -1;
    }
    psqueue->pbase[psqueue->tail] = data;
    psqueue->tail = (psqueue->tail + 1) % MAX_QUEUE_LEN;
    return 0;
}
6、循环队列的队头出队列
cpp 复制代码
//循环队列队头出队
int sysqueue_pop(SQueue_t *psqueue, DataType_t *pdata)
{
    if(is_empty_sysqueue(psqueue)){
        return -1;
    }
    if(pdata != NULL){
        *pdata = psqueue->pbase[psqueue->head];
    }
    psqueue->head = (psqueue->head + 1) % MAX_QUEUE_LEN;
    return 0;
}
7、获取队头元素
cpp 复制代码
//获取循环队列队头元素
int get_top_sysqueue(SQueue_t *psqueue, DataType_t *pdata)
{
    if(is_empty_sysqueue(psqueue)){
        return -1;
    }
    *pdata = psqueue->pbase[psqueue->head];
    return 0;
}
8、销毁队列
cpp 复制代码
//销毁循环队列
void destroy_sysqueue(SQueue_t **ppsqueue)
{
    free((*ppsqueue)->pbase);
    (*ppsqueue)->pbase = NULL;
    free(*ppsqueue);
    *ppsqueue = NULL;
}

三. 哈希表与哈希存储

3.1 哈希存储

哈希存储(散列存储) :将要存储数据的关键字 和数据存储位置值之间建立起对应的函数关系(哈希函数),当数据存储时,根据该关系映射数据的存储位置;查找数据时,利用该函数关系运算出数据的存储位置。

**目的:**为了快速检索数据

3.2 哈希冲突

**哈希冲突/哈希矛盾:**哈希冲突是指两个不同的关键字通过哈希函数映射到同一个哈希地址

解决方法:

1、开放定址法

**开放定址法:**当发生冲突时,通过探测找到下一个空闲槽位存储数据

2、链地址法

链地址法:将所有哈希地址相同 的元素存储在一个链表中。哈希表的每个槽位存储链表的头指针,

冲突元素追加到链表末尾。

3.3 哈希的实现

1、哈希的声明

cpp 复制代码
typedef struct contact{
    char name[32];
    char tel[15];
}DataType_t;

//哈希节点
typedef struct hashnode{
    DataType_t data;
    struct hashnode *pnext;
}HSNode_t;

#define HASH_TABLE_SIZE 27

2、哈希函数

cpp 复制代码
//哈希函数
int hash(char key)
{
    if(key >= 'a' && key <= 'z'){
        return key - 'a';
    }
    else if(key >= 'A' && key <= 'Z'){
        return key - 'A';
    }
    else{
        return HASH_TABLE_SIZE - 1;
    }
}

3、哈希表的遍历

cpp 复制代码
//哈希表遍历
void print_hash(HSNode_t **hash_table)
{
    int i = 0;
    for(i = 0; i < HASH_TABLE_SIZE; i++){
        HSNode_t *cur = hash_table[i];
        while(cur){
            print(cur);
            cur = cur->pnext;
        }
    }
}

4、哈希表销毁

cpp 复制代码
//哈希表销毁
void destroy_hash(HSNode_t **hash_table)
{
    int i = 0;
    for(i = 0; i < HASH_TABLE_SIZE; i++){
        HSNode_t *cur = hash_table[i];
        HSNode_t *next = NULL;
        while(cur){
            next = cur->pnext;
            free(cur);
            cur = next;
        }
        hash_table[i] = NULL;
    }
}

5、哈希表查找

cpp 复制代码
//哈希表查找
HSNode_t *find_hash(HSNode_t **hash_table, char *target)
{
    int addr = hash(target[0]);
    HSNode_t *cur = hash_table[addr];
    while(cur){
        if(strcmp(target, cur->data.name) == 0){
            return cur;
        }
        cur = cur->pnext;
    }
    return NULL;
}

6、哈希表的插入(有序插入)

cpp 复制代码
//哈希表插入排序
void sort_insert_hash(HSNode_t **hash_table, HSNode_t *pnewnode, int addr)
{
    HSNode_t *cur = hash_table[addr];
    HSNode_t *prev = NULL;
    //找一个合适的位置,插入的位置的下一个的name比newnode的name大
    while(cur && strcmp(cur->data.name, pnewnode->data.name) < 0){
        prev = cur;
        cur = cur->pnext;
    }
    //跳出情况一:链表为空
    //情况二:第一个节点就比newnode的name大,这里使用字符串比较
    if(prev == NULL){
        pnewnode->pnext = cur;
        hash_table[addr] = pnewnode;
    }
    //跳出情况:找到了合适的位置
    else{
        pnewnode->pnext = cur;
        prev->pnext = pnewnode;
    }
}

//哈希表插入
int insert_hash_table(HSNode_t **hash_table, DataType_t data)
{
    HSNode_t *pnewnode = malloc(sizeof(HSNode_t));
    if(pnewnode == NULL){
        printf("malloc error\n");
        return -1;
    }
    pnewnode->data = data;
    pnewnode->pnext = NULL;
    int addr = hash(data.name[0]);
    //有序插入
    sort_insert_hash(hash_table,pnewnode,addr);
    return 0;
}
相关推荐
tyung2 小时前
Go 手写 Wait-Free SPSC 无界队列:无 CAS、无锁、泛型节点池
数据结构·后端·go
Chen_harmony2 小时前
一、数据结构概念和复杂度计算
数据结构
小欣加油2 小时前
leetcode287寻找重复数
数据结构·c++·算法·leetcode
fie88894 小时前
LBP + HOG 特征检测与识别 MATLAB 实现
数据结构·算法·matlab
退休倒计时5 小时前
【每日一题】LeetCode 15. 三数之和 TypeScript
数据结构·算法·leetcode·typescript
AbandonForce6 小时前
滑动窗口:定长滑动窗口与不定长滑动窗口
数据结构·c++·算法
炸薯条!6 小时前
二叉树的链式表示(2)
java·数据结构·算法
YHHLAI7 小时前
JavaScript 数据结构精讲:数组底层与实战避坑
开发语言·javascript·数据结构
Coder-magician7 小时前
《代码随想录》刷题打卡day12:二叉树part02
数据结构·c++·算法
IT策士7 小时前
Redis 从入门到精通:数据结构Set 与 Sorted
数据结构·数据库·redis