【代码随想录】【算法训练营】【第13天】 [239]滑动窗口最大值 [347]前k个高频元素

前言

思路及算法思维,指路 代码随想录
题目来自 LeetCode

day 12,美好的周末,休息~
day 13,周一又来了~

题目详情

[239] 滑动窗口最大值

题目描述

239 滑动窗口最大值
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个高频元素

题目描述

347 前k个高频元素
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;
}

今日收获

  1. 队列的使用,结合链表的实现。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值