一. 队列(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;
}