前言
思路及算法思维,指路 代码随想录。
题目来自 LeetCode。
day 12,美好的周末,休息~
day 13,周一又来了~
题目详情
[239] 滑动窗口最大值
题目描述
解题思路
前提:数组相邻k个元素中的最大值。
思路:暴力解决(每次移动均判断k中最大值)会超时;将元素依次保存在一个单调递减的队列中,保证每次元素入队列时,队列中小于该元素的值弹出,保证队列单调递减,此时队列头如果不是k元素的第一个元素的前一位,即为所求k元素中的最大值。
重点:维护单调递减队列,并判断队列头元素是否依旧存在于k个元素中。
代码实现
C语言
暴力 (会超时)
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int maxVal(int *queue, int queueSize)
{
int max = queue[0];
for (int i = 1; i < queueSize; i++)
{
if (queue[i] > max)
{
max = queue[i];
}
}
return max;
}
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
// 滑动窗口大小 > 数组元素个数
if (k > numsSize)
{
*returnSize = 1;
int *res = (int *)malloc(sizeof(int));
*res = maxVal(nums, numsSize);
return res;
}
*returnSize = numsSize - (k - 1);
int *res = (int *)malloc(sizeof(int) * (*returnSize));
for (int i = 0; i < *returnSize; i++)
{
res[i] = maxVal(&nums[i], k);
}
return res;
}
暴力,但改善一点点(还是会超时)
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int maxVal(int *queue, int queueSize)
{
int max = queue[0];
for (int i = 1; i < queueSize; i++)
{
if (queue[i] > max)
{
max = queue[i];
}
}
return max;
}
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
// 滑动窗口大小 > 数组元素个数
if (k > numsSize)
{
*returnSize = 1;
int *res = (int *)malloc(sizeof(int));
*res = maxVal(nums, numsSize);
return res;
}
*returnSize = numsSize - (k - 1);
int *res = (int *)malloc(sizeof(int) * (*returnSize));
res[0] = maxVal(nums, k);
for (int i = 1; i < *returnSize; i++)
{
// 最大值是否为当前窗口左侧元素
if (res[i - 1] != nums[i - 1])
{
// 最大元素还在当前窗口
if (res[i - 1] <= nums[i + k - 1])
{
res[i] = nums[i + k - 1];
}
else
{
res[i] = res[i - 1];
}
}
else
{
// 重新计算当前窗口最大值
res[i] = maxVal(&nums[i], k);
}
}
return res;
}
单调队列 + 双向链表
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
typedef struct Node {
int val;
struct Node *pre;
struct Node *next;
} Node;
typedef struct Queue {
int size;
int maxSize;
Node *head;
Node *tail;
} Queue;
Node *creatNode()
{
Node *ret = (Node *)malloc(sizeof(Node));
ret->val = 0;
ret->pre = NULL;
ret->next = NULL;
return ret;
}
// 仅涉及删除头节点或者尾结点, 0 - head, 1 - tail
void deleteNode(Queue *queue, int deleteKind)
{
if (queue->size <= 0)
{
return ;
}
Node *deleteNode = NULL;
if (deleteKind == 0)
{
deleteNode = queue->head;
queue->head = deleteNode->next;
if (queue->head != NULL)
{
queue->head->pre = NULL;
}
else
{
queue->tail = NULL;
}
deleteNode->next = NULL;
}
else
{
deleteNode = queue->tail;
queue->tail = deleteNode->pre;
if (queue->tail != NULL)
{
queue->tail->next = NULL;
}
else
{
queue->head = NULL;
}
deleteNode->pre = NULL;
}
(queue->size)--;
free(deleteNode);
return ;
}
// 仅涉及增加尾结点, 0 - head
void addNode(Queue *queue, int val)
{
Node *addNode = (Node *)malloc(sizeof(Node));
addNode->val = val;
addNode->next = NULL;
if (queue->size > 0)
{
addNode->pre = queue->tail;
queue->tail->next = addNode;
queue->tail = addNode;
}
else
{
addNode->pre = NULL;
queue->head = addNode;
queue->tail = addNode;
}
(queue->size)++;
return ;
}
Queue *queueCreat(int maxSize)
{
Queue *ret = (Queue *)malloc(sizeof(Queue));
ret->size = 0;
ret->maxSize = maxSize;
ret->head = NULL;
ret->tail = NULL;
return ret;
}
void queuePush(Queue *queue, int val)
{
// 判断当前队列已满
if (queue->size >= queue->maxSize)
{
return ;
}
// 当前队列未满
// 遍历队列,删除小于val元素
while ((queue->tail != NULL) && (queue->tail->val < val))
{
deleteNode(queue, 1);
}
// 将val加入queue中
addNode(queue, val);
return ;
}
void queuePop(Queue *queue, int val)
{
// 删除队列头元素
if ((queue->head != NULL) && (queue->head->val == val))
{
deleteNode(queue, 0);
}
return ;
}
int queuePeek(Queue *queue)
{
if (queue->size <= 0)
{
return 0;
}
return queue->head->val;
}
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
Queue *queue = queueCreat(k);
// 处理numsSize小于k的情况
if (numsSize < k)
{
*returnSize = 1;
}
else
{
*returnSize = numsSize - k + 1;
}
int *ret = (int *)malloc(sizeof(int) * (*returnSize));
memset(ret, 0, sizeof(int) * (*returnSize));
// 前k个元素处理
for (int i = 0; (i < k) && (i < numsSize); i++)
{
queuePush(queue, nums[i]);
}
ret[0] = queuePeek(queue);
// 从第k + 1个元素处理
int retLoc = 1;
for (int i = k; i < numsSize; i++)
{
int leftLoc = i - k;
// 移除窗口左侧元素
queuePop(queue, nums[leftLoc]);
queuePush(queue, nums[i]);
ret[retLoc] = queuePeek(queue);
retLoc++;
}
return ret;
}
[347] 前k个高频元素
题目描述
解题思路
前提:元素出现频率,由高到底排列。
思路:暴力解决(统计每个元素的频率,再对所有元素频率排序)大概率会超时;先对数组元素大小排序,维护一个单调递减队列,大小为k,每次比较是否需要替换队列中的元素即可。
重点:维护单调递减序列。(单调递增序列也合适,便于找最小替换元素)。
代码实现
C语言
单调递减序列
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
typedef struct Node {
int key;
int cnt;
struct Node *next;
} Node;
int cmp(const void *p1, const void *p2)
{
return *(int *)p1 > *(int *)p2;
}
// preDel尾结点的前置结点
void insertNode(Node *vhead, Node *addNode, int k, Node *preDel)
{
Node *cur = vhead;
bool insert = false;
// 将结点插入非空链表中
while (cur->next)
{
if ((cur->next->cnt < addNode->cnt) && (insert == false))
{
// 插入
addNode->next = cur->next;
cur->next = addNode;
(vhead->cnt)++;
insert = true;
cur = addNode;
}
//判断尾结点前置位置
preDel = cur;
cur = cur->next;
}
// 遍历链表后,如果结点未被插入,说明频率最低,此时直接插入节点,作为尾结点
if (insert == false)
{
// 当前链表中无结点
if (preDel->next == NULL)
{
preDel->next = addNode;
}
else
{
preDel->next->next = addNode;
preDel = preDel->next;
}
(vhead->cnt)++;
}
// 判断链表元素个数是否大于k
if (vhead->cnt == (k + 1))
{
Node *del = preDel->next;
preDel->next = NULL;
(vhead->cnt)--;
free(del);
}
int i = vhead->cnt;
cur = vhead;
while (i--)
{
cur = cur->next;
}
return ;
}
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {
// 初始化链表,cnt标识当前链表数量
Node *vhead = (Node *)malloc(sizeof(Node));
vhead->key = 0;
vhead->cnt = 0;
vhead->next = NULL;
// 如果需要删除结点(尾部),标识删除结点的前置结点
Node *preDel = vhead;
// 数组排序
qsort(nums, numsSize, sizeof(int), cmp);
// 遍历数组
int start = 0;
for (int i = 0; i < numsSize; i++)
{
// 元素发生变化时,统计元素个数
if ((i > 0) && (nums[i] != nums[i - 1]))
{
Node *addNode = (Node *)malloc(sizeof(Node));
addNode->key = nums[i - 1];
addNode->cnt = i - start;
addNode->next = NULL;
insertNode(vhead, addNode, k, preDel);
// 记录该整数起始位置
start = i;
}
}
// 处理最后一个元素
Node *addNode = (Node *)malloc(sizeof(Node));
addNode->key = nums[numsSize - 1];
addNode->cnt = numsSize - start;
addNode->next = NULL;
insertNode(vhead, addNode, k, preDel);
// 输出结果
*returnSize = vhead->cnt;
int *ret = (int *)malloc(sizeof(int) * vhead->cnt);
memset(ret, 0, sizeof(int) * vhead->cnt);
Node *cur = vhead;
int idx = 0;
while ((idx < *returnSize) && (cur->next))
{
ret[idx++] = cur->next->key;
cur = cur->next;
}
return ret;
}
今日收获
- 队列的使用,结合链表的实现。