【数据结构算法】⑥、队结构的顺序与链式存储实现、做算法题的⽅法、栈的思想应⽤、Letcode - 括号匹配检验的实现、每日气温的结构图

数据结构与算法 总共分为19个系列
①、数据结构与算法[基础]+线性结构部分内容篇
②、单向循环链表的创建插入删除实现篇
③、双向链表和双向循环链表的实现篇
④、数据结构-线性表-算法练习篇
⑤、栈(代码实现)和队列(原理概念)篇
⑥、队结构的顺序与链式存储实现篇

请添加图片描述

⭐️本文章知识点大纲

  1. 顺序队列表示与操作实现
  2. 队列链式存储实现
  3. 做算法题的方法
  4. 栈的思想应用
  5. leetcode - 括号匹配(实现)
  6. 思考 leetcode - 每日温度如何实现

⭐️队列

⭐️①、队结构的顺序与链式存储实现

队列的队列原则是:先进先出

1.1 结构图 - 队列

请添加图片描述

1.2. 表示与操作图 - 队列

请添加图片描述

1.3 ⌨️队列的代码实现 顺序队列表示与操作实现

内容包含:

  1. 创建一个空队列
  2. 清空一个队列
  3. 判断队列是否为空
  4. 返回队列的长度
  5. 对头(获取,不要改变原始队列)
  6. 入队列
  7. 出队列
  8. 队列的遍历
#include "stdio.h"
#include "stdlib.h"

#include "math.h"
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */

typedef int Status;
typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */

/* 循环队列的顺序存储结构 */
typedef struct
{
    
    
    QElemType data[MAXSIZE];
    int front;        /* 头指针 */
    int rear;        /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}SqQueue;


//6.1 初始化一个空队列Q
Status InitQueue(SqQueue *Q){
    
    
    Q->front = 0;
    Q->rear = 0;
    return OK;
}

//6.2 将队列清空
Status ClearQueue(SqQueue *Q){
    
    
    
    Q->front = Q->rear = 0;
    return OK;
}

//6.3 若队列Q为空队列,则返回TRUR,否则返回FALSE;
Status QueueEmpty(SqQueue Q){
    
    
    //队空标记
    if (Q.front == Q.rear)
        return TRUE;
    else
        return FALSE;
}


//6.4 返回Q的元素个数,也就是队列的当前长度
int QueueLength(SqQueue Q){
    
    
    return (Q.rear - Q.front + MAXSIZE)%MAXSIZE;
}

//6.5 若队列不空,则用e返回Q的队头元素,并返回OK,否则返回ERROR;
Status GetHead(SqQueue Q,QElemType *e){
    
    
    //队列已空
    if (Q.front == Q.rear)
        return ERROR;
    
    *e = Q.data[Q.front];
    return OK;
    
}

//6.6 入队 - 若队列未满,则插入元素e为新队尾元素
Status EnQueue(SqQueue *Q,QElemType e){
    
    
    
    //队列已满
    if((Q->rear+1)%MAXSIZE == Q->front)
        return ERROR;
    
    //将元素e赋值给队尾
    Q->data[Q->rear] = e;
    
    //rear指针向后移动一位,若到最后则转到数组头部;
    Q->rear = (Q->rear+1)%MAXSIZE;
    
    return OK;
}

//6.7 出队 - 若队列不空,则删除Q中队头的元素,用e返回值
Status DeQueue(SqQueue *Q,QElemType *e){
    
    
   
    //判断队列是否为空
    if (Q->front == Q->rear) {
    
    
        return ERROR;
    }
    
    //将队头元素赋值给e
    *e = Q->data[Q->front];
    
    //front 指针向后移动一位,若到最后则转到数组头部
    Q->front = (Q->front+1)%MAXSIZE;
    
    return OK;
}


//6.8 从队头到队尾依次对队列的每个元素数组
Status QueueTraverse(SqQueue Q){
    
    
    int i;
    i = Q.front;
    while ((i+Q.front) != Q.rear) {
    
    
        printf("%d   ",Q.data[i]);
        i = (i+1)%MAXSIZE;
    }
    printf("\n");
    return OK;
}


int main(int argc, const char * argv[]) {
    
    
    // insert code here...
    printf("001--顺序队列表示与操作实现\n");
    
    Status j;
    int i=0,l;
    QElemType d;
    SqQueue Q;
    InitQueue(&Q);
    printf("初始化队列后,队列空否?%u(1:空 0:否)\n",QueueEmpty(Q));
    
    printf("入队:\n");
    while (i < 10) {
    
    
        EnQueue(&Q, i);
        i++;
    }
    QueueTraverse(Q);
    printf("队列长度为: %d\n",QueueLength(Q));
    printf("现在队列空否?%u(1:空 0:否)\n",QueueEmpty(Q));
    printf("出队:\n");
   
   //出队
    DeQueue(&Q, &d);
    printf("出队的元素:%d\n",d);
    QueueTraverse(Q);

    //获取队头
    j=GetHead(Q,&d);
    if(j)
        printf("现在队头元素为: %d\n",d);
    ClearQueue(&Q);
    printf("清空队列后, 队列空否?%u(1:空 0:否)\n",QueueEmpty(Q));
    return 0;
}


⭐️②、链式队列的实现

添加了队头、队尾 结点
内容包含:

  1. 队列的初始化
  2. 销毁队列
  3. 清空队列
  4. 判断队列是否为空
  5. 返回队列的长度
  6. 插入元素到队列 (从队尾插入)
  7. 出队列
  8. 获取对头的元素
  9. 队列的遍历
#include <stdio.h>
#include "stdio.h"
#include "stdlib.h"

#include "math.h"
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */

typedef int Status;

typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */

typedef struct QNode    /* 结点结构 */
{
    
    
    QElemType data;
    struct QNode *next;
}QNode,*QueuePtr;

typedef struct            /* 队列的链表结构 */
{
    
    
    QueuePtr front,rear; /* 队头、队尾指针 */
}LinkQueue;


/*6.1 初始化队列*/
Status InitQueue(LinkQueue *Q){
    
    
    

    //1. 头/尾指针都指向新生成的结点
    Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));

    //2.判断是否创建新结点成功与否
    if (!Q->front) {
    
    
        return ERROR;
    }

    //3.头结点的指针域置空
    Q->front->next = NULL;
    
    return OK;
}


/*6.2 销毁队列Q*/
Status DestoryQueue(LinkQueue *Q){
    
    
    
    //遍历整个队列,销毁队列的每个结点
    while (Q->front) {
    
    
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    return OK;
    
}

/*6.3 将队列Q置空*/
Status ClearQueue(LinkQueue *Q){
    
    
    
    QueuePtr p,q;
    Q->rear = Q->front;
    p = Q->front->next;
    Q->front->next = NULL;
    
    while (p) {
    
    
        
        q = p;
        p = p->next;
        free(q);
        
    }
    
    return OK;
}

/*6.4 判断队列Q是否为空*/
Status QueueEmpty(LinkQueue Q){
    
    
    if (Q.front == Q.rear)
        return TRUE;
    else
        return FALSE;
}

/*6.5 获取队列长度*/
int QueueLength(LinkQueue Q){
    
    
    int i= 0;
    QueuePtr p;
    p = Q.front;
    while (Q.rear != p) {
    
    
        i++;
        p = p->next;
    }
    return i;
}

/*6.6 插入元素e为队列Q的新元素*/
Status EnQueue(LinkQueue *Q,QElemType e){
    
    
    
    //为入队元素分配结点空间,用指针s指向;
    QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
    
    //判断是否分配成功
    if (!s) {
    
    
         return ERROR;
    }
    
    //将新结点s指定数据域.
    s->data = e;
    s->next = NULL;
    
    //将新结点插入到队尾
    Q->rear->next = s;
    
    //修改队尾指针
    Q->rear = s;
    
    return OK;
}


/*6.7 出队列*/
Status DeQueue(LinkQueue *Q,QElemType *e){
    
    
    
    QueuePtr p;
    
    //判断队列是否为空;
    if (Q->front == Q->rear) {
    
    
        return ERROR;
    }
    
    //将要删除的队头结点暂时存储在p
    p = Q->front->next;
    
    //将要删除的队头结点的值赋值给e
    *e = p->data;
    
    //将原队列头结点的后继p->next 赋值给头结点后继
    Q->front->next = p ->next;
    
    //若队头就是队尾,则删除后将rear指向头结点.
    if(Q->rear == p) Q->rear = Q->front;
    
    free(p);
    
    return OK;
}

/*6.8 获取队头元素*/
Status GetHead(LinkQueue Q,QElemType *e){
    
    
   
    //队列非空
    if (Q.front != Q.rear) {
    
    
        //返回队头元素的值,队头指针不变
        *e =  Q.front->next->data;
        return TRUE;
    }
    
    return  FALSE;
    
}

/*6.9 遍历队列*/
Status QueueTraverse(LinkQueue Q){
    
    
    
    QueuePtr p;
    p = Q.front->next;
    while (p) {
    
    
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}


int main(int argc, const char * argv[]) {
    
    
    // insert code here...
    printf("链队列的表示与操作!\n");
    
    Status iStatus;
    QElemType d;
    LinkQueue q;
    
    //1.初始化队列q
    iStatus = InitQueue(&q);
    
    //2. 判断是否创建成
    if (iStatus) {
    
    
        printf("成功地构造了一个空队列\n");
    }
    
    //3.判断队列是否为空
    printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(q));
    
    //4.获取队列的长度
    printf("队列的长度为%d\n",QueueLength(q));
    
    //5.插入元素到队列中
    EnQueue(&q, -3);
    EnQueue(&q, 6);
    EnQueue(&q, 12);
    
    printf("队列的长度为%d\n",QueueLength(q));
    printf("是否为空队列?%d (1:是 0:否)\n",QueueEmpty(q));
    
    //6.遍历队列
    printf("队列中的元素如下:\n");
    QueueTraverse(q);

    //7.获取队列头元素
    iStatus = GetHead(q, &d);
    if (iStatus == OK) {
    
    
        printf("队头元素是:%d\n",d);
    }
    
    //8.删除队头元素
    iStatus =DeQueue(&q, &d);
    if (iStatus == OK) {
    
    
        printf("删除了的队头元素为:%d\n",d);
    }
    
    //9.获取队头元素
    iStatus = GetHead(q, &d);
    if (iStatus == OK) {
    
    
        printf("新的队头元素为:%d\n",d);
    }
    
    //10.清空队列
    ClearQueue(&q);
    
    //11.销毁队列
    DestoryQueue(&q);
    
    
    
    return 0;
}

⭐️③、 做算法题的⽅法: (必看)

  1. 充分阅读题⽬.了解题⽬背后的关键意思;
  2. 分析题⽬,涉及到哪些数据结构,对问题进⾏分类. 到底属于链表问题, 栈思想问题, 字符串问题,⼆叉树问 题,图相关问题,排序问题; 与你之前所接触过的算法题有没有类似,找到问题的解题思路
  3. 实现算法. 在算法的实现的过程,并不是⼀蹴⽽就, 肯定是需要不断的调试,修改的;
  4. 验证算法正确性
  5. 找到题源, 看其他的开发者提供的解决思路.
  6. 找到题解建议之后, 对于其他优秀思路,分析它的优势,并且学习它的思路.并且写成其他解法的代码(借⼒, 不要单纯的闭⻔造⻋)
  7. 算法题的解题能⼒来⾃于2点: 对于数据结构与算法核⼼问题是否夯实 + 是否有⾜够多且⾜够耐⼼的积 累; 例如如果你连基本的链表操作都不知道, 如果去解决链表翻转问题; 例如你连图的存储.遍历等都不清楚,如 何去解决图的最⼩路径问题?,如果解决图的问题; 例如你都⽆法⾃⼰不了解⼆叉树,如果去计算⼆叉树的深 度问题?

栈的思想应⽤: 指的是利⽤栈的特性(先进后出)去解决问题,那么什么问题适合⽤栈思想解决了?

  1. 数据是线性的
  2. 问题中常常涉及到数据的来回⽐较,匹配问题;例如,每⽇温度,括号匹配,字符串解码,去掉重复字⺟等问题.
  3. 问题中涉及到数据的转置,例如进制问题.链表倒序打印问题等
  4. 注意并不是说栈思想只是⼀个解决的的参考思想.并不是万能的.它适⽤于以上这样的情况下去解决问题; 利⽤栈思想解决问题时,⾸先需要透彻的解析问题之后,找到问题解决的规律.才能使⽤它解决; 思想只有指导作⽤,遇到不同的题⽬,需要个例分析.在基本思想上去找到具体问题具体的解决问题之道;

⭐️④、Leetcode - 括号匹配检验 (字节出现过的算法面试题)

1. 题目 括号匹配检验:

假设表达式中允许包含两种括号:圆括号与方括号,其嵌套顺序随意,即() 或者[([][])]都是正确
的.而这[(]或者(()])或者([()) 都是不正确的格式. 检验括号是否匹配的方法可用”期待的急迫程
度"这个概念来描述.例如,考虑以下括号的判断: [ ( [ ] [ ] ) ]

思路:
a. 将 0 个元素压栈;
b. 遍历字符范围[1,strlen(data)]
c. 取栈顶字符
d. 检查该括号是左括号 i. 左: 判断后⾯字符data[i] 是右括号, YES ⼊栈/ NO 出栈;
e.遍历结束. 判断栈是否为空,如果空匹配成功,匹配失败;

1.1 结构图 - 括号匹配检验

请添加图片描述

1.2 括号匹配检验 代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define Stack_Init_Size 100
#define Stack_Increment 10
//栈的定义
typedef struct {
    
    
    char* base; //栈底指针
    char* top;  //栈顶指针
    int stacksize;  //栈MaxSize
}SqStack;

//初始化栈
/*
 思路:
 1. 如果栈底为空
 2. 分配一个最大容量Stack_Init_Size的数组,栈底/栈顶都指向与它.[参考图空栈情况]
 3. 初始化栈的最大容易Stack_Init_Size
 */
int Init(SqStack *stack){
    
    

    if(!stack->base){
    
    
        stack->base=(char*)malloc(Stack_Init_Size*sizeof(char));
        stack->top=stack->base;
        stack->stacksize = Stack_Init_Size;
        printf("初始化成功\n");
        return 0; //初始化成功
    }
    else return -1;//表示无法初始化已出始化栈
}

//获取栈顶数据
/*
 思路:
 1.判断栈是否为空
 2.非空,则栈定指针-1,返回栈顶元素;
 */
char GetTop(SqStack stack){
    
    
   
    if(stack.base==stack.top){
    
    
        //printf("栈中没有数据\n");
        return '#';
    }
    //printf("获取栈顶数据成功\n");
    return *(stack.top-1);
}

//往栈中插入元素
/*
 思路:
 1.判断栈是否已满,若满则返回ERROR #问题:如何判断栈是否已满?
 2.栈满,则续容空间 #问题:如何给已满栈续容空间?
 3.将元素element压栈
 4.栈顶指针加"1"
 */
int Push(SqStack *stack,char element){
    
    
    if(stack->top-stack->base==stack->stacksize){
    
    
        stack->base=(char*)realloc(stack->base,Stack_Increment*sizeof(char));
        stack->top=stack->base+stack->stacksize;
        stack->stacksize+=Stack_Increment;
    }
    *stack->top=element;
    stack->top+=1;
    return 0;
}

//删除栈顶元素
/*
 思路:
 1.判断栈是否已空
 2.非空,则获取栈顶元素,并将栈顶减"1";
 */
char Pop(SqStack *stack){
    
    
    if(stack->top==stack->base){
    
    
        printf("栈为空\n");
        return '#';
    }
    //printf("删除数据成功");
    return *--stack->top;
}

//释放栈空间
int Destroy(SqStack *stack){
    
    
    free(stack->base);
    stack->stacksize=0;
    return 0;
}

//处理数据,借助栈判断
/*
 思路:
 1. 将第0个元素压栈
 2. 遍历[1,strlen(data)]
    (3). 取栈顶字符
    (4). 检查该字符是左括号("(","[")
         a.是左"(",则判断紧接其后的data[i]是为右")"
            YES->压栈,NO->出栈
         b.是左"[",则判断紧跟其后的data[i]是为右"]"
            YES->压栈,NO->出栈
         c.表示式如果以"#"结尾,则判断紧跟其后的data是为左"(""]"
            YES->压栈,NO->-1;
 
 3.遍历结束,则判断栈是否为空,为空则表示匹配成功;否则匹配失败;
 [ ( [ ] [ ] ) ]
 1 2 3 4 5 6 7 8
 */
int ExecuteData(SqStack stack,char* data){
    
    
    Push(&stack,data[0]);
    for(int i=1;i<strlen(data);i++){
    
    
        char top = GetTop(stack);
        switch(top){
    
    
            case '(':
                if(data[i]==')')Pop(&stack);
                else Push(&stack,data[i]);
                break;
            case '[':
                if(data[i]==']')Pop(&stack);
                else Push(&stack,data[i]);
                break;
            case '#':
                if(data[i]=='('||data[i]=='['){
    
    
                    Push(&stack,data[i]);
                    break;
                }
                else
                    default:return -1;break;
        }
    }
    
    //如果栈为空,则返回"0"->匹配成功 否则返回"-1"匹配失败
    if(stack.top==stack.base){
    
    
        Destroy(&stack);
        return 0;
    }
    else{
    
    
        
        Destroy(&stack);
        return -1;
    }
}


int main(){
    
    
    
    /*
     算法问题:
     假设表达式中允许包含两种括号:圆括号与方括号,其嵌套顺序随意,即([]()) 或者[([][])]都是正确的.而这
     [(]或者(()])或者([()) 都是不正确的格式. 检验括号是否匹配的方法可用"期待的急迫程度"这个概念来描述. 例如,考虑以下括号的判断:
     
     [ ( [ ] [ ] ) ]
     1 2 3 4 5 6 7 8
     */
    
    SqStack stack;
    Init(&stack);
    char data[180];
    printf("请输入待匹配的字符串\n");
    scanf("%s",data);
    int result = ExecuteData(stack,data);
    if(result==0)printf("括号是正确匹配的\n");
    else printf("括号匹配不正确\n");
    return 0;
}

2. 题目 每日气温(思考如何实现)

每日气温:
题目: 根据每日气温列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会
升高超过该日的天数。如果之后都不会升高,请在该位置0来代替。例如,给定一个列
表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围
内的整数。

请添加图片描述

猜你喜欢

转载自blog.csdn.net/qq_42816425/article/details/123748273
今日推荐