【剑指offer专项突破版】队列篇——“C“

前言

剑指offer专项突破版(力扣官网)——> 点击进入
本文所属专栏——>点击进入

一、滑动窗口的平均值

题目分析

在这里插入图片描述

思路分析

 由当大于size个时,返回最新的size个数的平均值,这符合对列先进先出 。为了更好的理解,我们把这里的size换成容量capacity,而size这里我们认为是当前滑动窗口的当前元素个数
因此我们可以用对列进行解题,当size小于capacity时,我们只需要入队列即可。当size等于capacity时,我们需要出队列,再进行入对列。当然,在入队列时,我们可以顺便求一下和,出队列时,和要减去对列的元素。

对列代码

typedef struct QNode
{
    
    
    struct QNode *next;
    struct QNode *prev;
    int val;
}QNode;
typedef struct 
{
    
    
    QNode* head;
    int sum;//为了方便记录我们这里采用插一个就记录当前的和
    int capacity;//这是给定的最大容量
    int size;//这是当前的size
}MovingAverage;
QNode* BuyNode(int val)
{
    
    
    QNode* NewNode = (QNode*)malloc(sizeof(QNode));
    NewNode->val = val;
    NewNode->next = NULL;
    NewNode->prev = NULL;
    return NewNode;
}
//需要写一个头插和尾删
void PushFront(QNode** head,int val)
{
    
    
    if(head == NULL)
    {
    
    
        printf("头插传入指针为空!\n");
        return;
    }
    QNode* New = BuyNode(val);
    QNode* Head = *head;
    
    if(Head == NULL)
    {
    
    
        //链表为空
        New->next = New;
        New->prev = New;
    }
    else
    {
    
    
        //进行头插操作
        New->next = Head;
        New->prev = Head->prev;

        
        Head->prev->next = New; 
        Head->prev = New;
    }
    *head = New;
}
void PopBack(QNode** head)
{
    
    
    QNode* Head = *head;
    QNode* Tail = Head->prev;
    //看是否只有一个结点
    if(Tail == Head)
    {
    
    
        *head = NULL;
    }
    else
    {
    
    
        Head->prev = Tail->prev;
        Tail->prev->next = Head;
    }
    free(Tail);
}
void QFree(QNode** head)
{
    
    
    QNode *Head = *head;
    if(Head != NULL)
    {
    
    
        QNode* cur = Head->next;
        while(cur != Head)
        {
    
    
            QNode* next = cur->next;
            free(cur);
            cur = next;
        }
    }
    free(Head);
}

题解代码

MovingAverage* movingAverageCreate(int size) 
{
    
    
    MovingAverage* init = \
     (MovingAverage*)malloc(sizeof(MovingAverage));
     // 说明:\是续行符,这里是为了防止代码过长。
    init->head = NULL;
    init->capacity = size;
    init->size = 0;
    init->sum = 0;

    return init;
}

double movingAverageNext(MovingAverage* obj, int val) 
{
    
    
    PushFront(&(obj->head),val);
    (obj->sum)+=val;
    (obj->size)++;
    //当这里的size会大于capacity
    if(obj->size > obj->capacity)
    {
    
    
        int tail = (obj->head)->prev->val;
        PopBack(&(obj->head));
        (obj->sum)-=tail;
        (obj->size)--;
    }
    double average = (double)(obj->sum)/(obj->size);
    return average;
}

void movingAverageFree(MovingAverage* obj) 
{
    
    
    QFree(&(obj->head));
    free(obj);
}

二、最近请求次数

题目分析

在这里插入图片描述

思路分析

 给定时间t,返回[t-3000,t]的请求次数,再加上每次请求的 t,都会严格递增,因此 t-3000会逐渐增大,因此可能有以前的t,不符合此状态。由此当有最开始的t 不符合此状态时,在此范围的请求数就减一。以前的 t 需要删除,符合先进先出,因此采用对列。因此我们只需拿当前的t-3000,跟最开始的t比较,如果大,就出对列,如果小,就入对列。当前对列的个数,即为最近请求数。

队列代码

typedef struct QNode
{
    
    
    struct QNode* prev;
    struct QNode* next;
    int top;
}QNode;

QNode* BuyNode(int top,int bottom)
{
    
    
    QNode* NewNode = (QNode*)malloc(sizeof(QNode));
    NewNode->top = top;
    NewNode->prev = NULL;
    NewNode->next = NULL;
    return NewNode;
}
void PushFront(QNode** head,int t)
{
    
    
    QNode* New = BuyNode(t,t-3000);
    QNode* Head = *head;
    if(Head == NULL)
    {
    
    
        New->next = New;
        New->prev = New;
    }
    else
    {
    
    
        New->next = Head;
        New->prev = Head->prev;

        Head->prev->next = New;
        Head->prev = New;
    }
    *head = New;
}
void PopBack(QNode** head)
{
    
    
    QNode* Head = *head;
    QNode* Tail = Head->prev;
    if(Head == Tail)
    {
    
    
        *head = NULL;
    }
    else
    {
    
    
        Head->prev = Tail->prev;
        Tail->prev->next = Head;
    }
    free(Tail);
}
void QFree(QNode** head)
{
    
    
    QNode* Head = *head;
    if(Head != NULL)
    {
    
    
        QNode* cur = Head->next;
        while(cur!=Head)
        {
    
    
            QNode* next = cur->next;
            free(cur);
            cur = next;
        } 
    }
    free(Head);
}
int Top(QNode** head)
{
    
    
    return (*head)->prev->top; 
}

bool is_empty(QNode** head)
{
    
    
    if(*head == NULL)
    {
    
    
        return true;
    }
    return false;
}

题解代码

typedef struct 
{
    
    
    QNode* head;
    int count;
}RecentCounter;
RecentCounter* recentCounterCreate() 
{
    
    
    RecentCounter* init =\
    (RecentCounter*)malloc(sizeof(RecentCounter));
    
    init->head = NULL;
    init->count = 0;
    return init;
}
int recentCounterPing(RecentCounter* obj, int t) 
{
    
    
    //先进行出栈,将范围小的出对列
    while(!is_empty(&(obj->head))&& (t-3000) > Top(&(obj->head)))
    {
    
    
        PopBack(&(obj->head));
        (obj->count)--;
    }
    //再将其入栈
    PushFront(&(obj->head),t);
    return ++(obj->count);
}
void recentCounterFree(RecentCounter* obj) 
{
    
    
    QFree(&(obj->head));
    free(obj);
}

三、往完全二叉树添加节点

题目分析

在这里插入图片描述
理论知识:

二叉树——理论篇
二叉树——顺序结构
点击即可进入

补充:
本题所用完全二叉树结论,利用数组存储的完全二叉树,其子结点和父结点的关系为~

  1. 前提:节点的父节点存在,且不为根节点。
  2. 设节点的下标为N
  3. 父节点下标 = (N-1)/2

思路分析

 由于是完全二叉树,因此我们需要层序遍历一遍,将完全二叉树转化为顺序结构,然后才可方便我们进行插入结点,那如何将完全二叉树转换为顺序结构呢?
 过程:先将根结点入对列,用0下标访问第一个节点,如果根结点不为空,则将其左节点和右节点再入对列,此时对列有三个结点,用下标访问下一个结点,即第二个节点,此时如果第二个节点不为空,则将其左右孩子再入对列,重复循环,直到访问到空节点为止。
  当需要添加节点时,我们只需要在遍历后,顺便记录一下,非空结点的个数,此时非空结点的个数即为添加节点的下标,再用上面的结论 计算父节点的下标,访问其父节点,并将此节点链接到父节点上即可。

队列与接口代码

typedef struct TreeNode TNode;
typedef struct Queue
{
    
    
    TNode** arr;
    int size;
    int capacity;
}Queue;

void QPushBack(Queue* que,TNode *val)
{
    
    
    if(que->capacity == que->size)
    {
    
    
        int capacity = que->capacity == 0 ? 4 : que->capacity * 2;
        TNode** tmp = (TNode**)realloc\
        (que->arr,sizeof(TNode*)*capacity);
        que->arr = tmp;
        que->capacity = capacity;
    }
    (que->arr)[que->size] = val;
    (que->size)++;
}
void Init(Queue* que)
{
    
    
    que->arr = NULL;
    que->size = 0;
    que->capacity = 0;
}
TNode* BuyNode(int v)
{
    
    
    TNode* NewNode = (TNode*)malloc(sizeof(TNode));
    NewNode->val = v;
    NewNode->left = NULL;
    NewNode->right = NULL;
    return NewNode;
}

题解代码

typedef struct 
{
    
    
    Queue que;
}CBTInserter;

CBTInserter* cBTInserterCreate(struct TreeNode* root) 
{
    
    
    CBTInserter* init = (CBTInserter*)malloc(sizeof(CBTInserter));
    Init(&(init->que));
    QPushBack(&(init->que),root);
    //插入根节点之后,进行层序遍历
    int sut = 0;//记录非空结点的个数
    while((init->que.arr)[sut]!=NULL)
    {
    
    
        TNode* cur = (init->que.arr)[sut];
        TNode* left = cur->left;
        TNode* right = cur->right;
        QPushBack(&(init->que),left);
        QPushBack(&(init->que),right);
        sut++;
    }
    init->que.size = sut;//记录非空结点的个数
    return init;
}

int cBTInserterInsert(CBTInserter* obj, int v)
{
    
    
    //初始化结点
    TNode* NewNode = BuyNode(v);
    int size = obj->que.size;//新节点的下标
    int pare = (size-1)/2;//父节点的下标
    TNode* pa = (obj->que.arr)[pare];//父节点
    if(pa->left == NULL)
    {
    
    
        pa->left = NewNode;
    } 
    else
    {
    
    
        pa->right = NewNode;
    }
    QPushBack(&(obj->que),NewNode);
    return pa->val;
}

struct TreeNode* cBTInserterGet_root(CBTInserter* obj)
{
    
    
    return (obj->que.arr)[0];
}

void cBTInserterFree(CBTInserter* obj) 
{
    
    
    free(obj->que.arr);
    free(obj);
}

四、二叉树每层的最大值

题目分析

在这里插入图片描述

思路分析

 此题要进行层序遍历,我们这里的应用非常灵活,先记录第一层的结点数,设为cur,进行出对列时,cur减减,看是否能更新当前一层的最大值,顺便把出队列结点的左右非空结点入队列,顺便记录一下此非空结点的个数,设为next,当cur减到0时,此时next就为下一层的非空结点个数,更新cur为next,next更新为0,顺便更新一下此层的max,对下一层的max进行初始化。cur与next循环更新,即为破题关键

队列代码

typedef struct TreeNode TNode;
typedef struct QNode
{
    
    
    struct QNode* prev;
    struct QNode* next;
    TNode* val;
}QNode;
QNode* BuyNode(TNode*val)
{
    
    
    QNode* NewNode = (QNode*)malloc(sizeof(QNode));
    NewNode->val = val;
    NewNode->next = NULL;
    NewNode->prev = NULL;
    return NewNode;
}
void PushFront(QNode** head, TNode* val)
{
    
    
    QNode* New = BuyNode(val);
    QNode* Head = *head;
    if(Head == NULL)
    {
    
    
        New->next = New;
        New->prev = New; 
    }
    else
    {
    
    
        New->next = Head;
        New->prev = Head->prev;

        Head->prev->next = New;
        Head->prev = New; 
    }
    *head = New;
}
void PopBack(QNode**head)
{
    
    
    QNode* Head = *head;
    QNode* Tail = Head->prev;
    if(Tail == Head)
    {
    
    
        free(Tail);
        *head = NULL;
    }
    else
    {
    
    
        Head->prev = Tail->prev;
        Tail->prev->next = Head;
        free(Tail);
    }
}
bool is_empty(QNode** head)
{
    
    
    if(*head == NULL)
    {
    
    
        return true;
    }

    return false;
}

题解代码

int* largestValues(struct TreeNode* root, int* returnSize)
{
    
    
    int *arr = (int*)malloc(sizeof(int)*10000);
    int size = 0;
    int max = 0;
    QNode* head = NULL;
    int cur = 0;//当前层的个数
    int next = 0;//下一层的个数
    if(root!=NULL)
    {
    
    
        PushFront(&head,root);
        cur = 1;//第一层的个数
        max = root->val;
    }
    while(!is_empty(&head))
    {
    
    
        TNode* tail = head->prev->val;
        if(tail->val > max)
        {
    
    
            max = tail->val;
        }
        TNode* left = tail->left;
        TNode* right = tail->right;
        PopBack(&head);
        cur--;
        if(left != NULL)
        {
    
    
            PushFront(&head,left);
            next++;
        }
        if(right != NULL)
        {
    
    
            PushFront(&head,right);
            next++;
        }
        if(cur == 0)
        {
    
    
            //更新最大值
            arr[size++] = max;

            cur = next;
            next = 0;
            if(head!=NULL)
            {
    
    
                TNode* tail = head->prev->val;
                max = tail->val;
            }
        }
    }
    *returnSize = size;
    return arr;
}

下面的五、六题与第四题雷同。 对列代码也相同,下面就不给出了。

五、二叉树最底层最左边的值

题目分析

在这里插入图片描述

思路分析

思路与第四题,雷同,当进行换层时我们需要更新一下最左边的值,然后当next与cur都等于0时,此时保存的就是最左边的值。

代码

int findBottomLeftValue(struct TreeNode* root)
{
    
    
    QNode* head = NULL;
    PushFront(&head,root);
    int val_left = root->val;
    int cur = 1;
    int next = 0;
    while(!is_empty(&head))
    {
    
    
        TNode* Tail = head->prev->val;
        TNode* left = Tail->left;
        TNode* right = Tail->right;
        PopBack(&head);
        cur--;

        if(left != NULL)
        {
    
    
            PushFront(&head,left);
            next++;
        }
        if(right != NULL)
        {
    
    
            PushFront(&head,right);
            next++;
        }
        
        if(cur == 0)
        {
    
    
            if(next == 0)
            {
    
    
                //直接跳出循环即可。
                break;
            }

            cur = next;
            next = 0;
            if(head != NULL)
            {
    
    
                TNode* Tail = head->prev->val;
                val_left = Tail->val;
            }

        }
    }
    return val_left;
}

六、二叉树的右侧视图

题目分析

在这里插入图片描述

思路分析

 思路与第四题雷同,不过这里是,当cur等于0时,保存此层的最后一个结点的值

代码

int* rightSideView(struct TreeNode* root, int* returnSize)
{
    
    
    QNode* head = NULL;

    int *arr = (int*)malloc(sizeof(int)*100);
    int size = 0;
    int cur = 0;
    int next = 0;
    if(root != NULL)
    {
    
    
        PushFront(&head,root);
        cur = 1;
    }
    while(!is_empty(&head))
    {
    
    
        TNode* Tail = head->prev->val;
        TNode* left = Tail->left;
        TNode* right = Tail->right;
        cur--;
        PopBack(&head);
        if(left != NULL)
        {
    
    
            PushFront(&head,left);
            next++;
        }
        if(right != NULL)
        {
    
    
            PushFront(&head,right);
            next++;
        }
        if(cur == 0)
        {
    
    
            arr[size++] = Tail->val;
            cur = next;
            next = 0;
        }
    }
    *returnSize = size;
    return arr;
}

总结

 今天的分享就到这里了,如果觉得文章不错,点个赞鼓励一下吧!我们下篇文章再见

猜你喜欢

转载自blog.csdn.net/Shun_Hua/article/details/131328537