文章目录
- 前言
- 一、滑动窗口的平均值
- 题目分析
- 思路分析
- 对列代码
- 题解代码
- 二、最近请求次数
- 题目分析
- 思路分析
- 队列代码
- 题解代码
- 三、往完全二叉树添加节点
- 题目分析
- 思路分析
- 队列与接口代码
- 题解代码
- 四、二叉树每层的最大值
- 题目分析
- 思路分析
- 队列代码
- 题解代码
- 五、二叉树最底层最左边的值
- 题目分析
- 思路分析
- 代码
- 六、二叉树的右侧视图
- 题目分析
- 思路分析
- 代码
- 总结
前言
剑指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);
}
三、往完全二叉树添加节点
题目分析
理论知识:
二叉树——理论篇
二叉树——顺序结构
点击即可进入
补充:
本题所用完全二叉树结论,利用数组存储的完全二叉树,其子结点和父结点的关系为~
- 前提:节点的父节点存在,且不为根节点。
- 设节点的下标为N
- 父节点下标 = (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;
}
总结
今天的分享就到这里了,如果觉得文章不错,点个赞鼓励一下吧!我们下篇文章再见
!