239. 滑动窗口最大值
题目链接/文章讲解/视频讲解:代码随想录
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
单调队列
分析及思路
入队出队返回最大值,就是该题目的思路,我们再次思路优化即可。使用双链表进。
插入时判断尾部元素是否小于插入值,若小于则弹出,若大于则直接插入。我们用的是双端队列能插入也能输出。 呈现出递减趋势。头部是用来出栈的,尾部是用来实现单调递减的。
代码及注释
// 双链表结点结构体定义
typedef struct Node {
int data; // 数据
struct Node* prev; // 前驱指针
struct Node* next; // 后继指针
} Node;
// 双链表结构体定义
typedef struct DoubleLinkedList {
Node* head; // 头结点
Node* tail; // 尾结点
int size; // 链表大小
} DoubleLinkedList;
// 创建双链表
DoubleLinkedList* createDoubleLinkedList() {
// 分配内存空间
DoubleLinkedList* list = (DoubleLinkedList*)malloc(sizeof(DoubleLinkedList));
// 初始化头结点、尾结点和链表大小
list->head = NULL;
list->tail = NULL;
list->size = 0;
return list;
}
// 在链表头部插入结点
void insertAtHead(DoubleLinkedList* list, int data) {
// 创建新结点
Node* newNode = (Node*)malloc(sizeof(Node));
// 设置新结点的数据和指针
newNode->data = data;
newNode->prev = NULL;
newNode->next = list->head;
// 更新链表头结点和尾结点
if (list->head != NULL) {
list->head->prev = newNode;
} else {
list->tail = newNode;
}
list->head = newNode;
list->size++;
}
// 在链表尾部插入结点
void insertAtTail(DoubleLinkedList* list, int data) {
// 创建新结点
Node* newNode = (Node*)malloc(sizeof(Node));
// 设置新结点的数据和指针
newNode->data = data;
newNode->next = NULL;
newNode->prev = list->tail;
// 更新链表头结点和尾结点
if (list->tail != NULL) {
list->tail->next = newNode;
} else {
list->head = newNode;
}
list->tail = newNode;
list->size++;
}
// 从链表头部删除结点
void deleteAtHead(DoubleLinkedList* list) {
// 判断链表是否为空
if (list->head == NULL) {
return;
}
// 移除头结点并更新链表头结点和尾结点
Node* temp = list->head;
list->head = list->head->next;
if (list->head != NULL) {
list->head->prev = NULL;
} else {
list->tail = NULL;
}
free(temp);
list->size--;
}
// 从链表尾部删除结点
void deleteAtTail(DoubleLinkedList* list) {
// 判断链表是否为空
if (list->tail == NULL) {
return;
}
// 移除尾结点并更新链表头结点和尾结点
Node* temp = list->tail;
list->tail = list->tail->prev;
if (list->tail != NULL) {
list->tail->next = NULL;
} else {
list->head = NULL;
}
free(temp);
list->size--;
}
// 判断双链表是否为空
int isDoubleLinkedListEmpty(DoubleLinkedList* list) {
return list->size == 0;
}
// 销毁双链表
void destroyDoubleLinkedList(DoubleLinkedList* list) {
// 释放链表内存空间
while (list->head != NULL) {
deleteAtHead(list);
}
free(list);
}
// 弹出链表头部指定值的结点
void pop(DoubleLinkedList* list,int value){
if( (!isDoubleLinkedListEmpty(list)) && (value == list->head->data) )
deleteAtHead(list);
}
// 将值插入链表尾部
void push(DoubleLinkedList* list,int value) {
while( (!isDoubleLinkedListEmpty(list)) && (value > list->tail->data ) )
deleteAtTail(list);
insertAtTail(list,value);
}
// 获取链表头部的值
int front(DoubleLinkedList* list){
return list->head->data;
}
// 滑动窗口最大值
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
// 分配结果数组内存空间
int* result = (int*)malloc(sizeof(int)*(numsSize-k+1));
// 创建双链表
DoubleLinkedList* list = createDoubleLinkedList();
// 将前k个元素插入双链表
for(int i=0;i<k;i++)
push(list,nums[i]);
result[0] = front(list);
int count = 1;
// 滑动窗口,更新结果数组
for(int i=k;i<numsSize;i++){
pop(list,nums[i-k]);
push(list,nums[i]);
result[count++] = front(list);
}
// 更新返回结果的大小
*returnSize = count;
return result;
}
347.前 K 个高频元素
题目链接/文章讲解/视频讲解:代码随想录
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。
分析及思路
统计频率我们就可以使用map来实现,然后使用小根堆把小的频率排除,剩下的都是大的。然后输出即可。
堆排序可以看小根堆与大根堆思路代码解释-CSDN博客
代码及注释
// 定义一个结构体map,包含键值对和哈希表的句柄
typedef struct {
int key;
int value;
UT_hash_handle hh;
} map;
// 哈希表指针
map* hashMap = NULL;
// 向哈希表中添加键值对
void hashMapAdd(int key, int value) {
map* s;
HASH_FIND_INT(hashMap, &key, s); // 在哈希表中查找指定键
if (s == NULL) {
s = (map*)malloc(sizeof(map)); // 如果找不到则分配内存
s->key = key; // 设置键
HASH_ADD_INT(hashMap, key, s); // 向哈希表中添加键值对
}
s->value = value; // 设置值
}
// 在哈希表中查找指定键的值
map* hashMapFind(int key) {
map* s;
HASH_FIND_INT(hashMap, &key, s); // 在哈希表中查找指定键
return s; // 返回找到的键值对
}
// 清空哈希表
void hashMapCleanup() {
map* cur, *tmp;
HASH_ITER(hh, hashMap, cur, tmp){ // 遍历哈希表
HASH_DEL(hashMap, cur); // 从哈希表中删除当前节点
free(cur); // 释放内存
}
}
// 打印哈希表中的键值对
void hashPrint(){
map* s; // 定义一个map类型的指针s
for(s = hashMap; s != NULL; s=(map*)(s -> hh.next)){ // 遍历哈希表
printf("key %d, value %d\n", s -> key, s -> value); // 打印键和值
}
}
// 调整小根堆,使以k为根的子树成为小根堆
void HeadAdjust(map* MapHash,int k,int len){
int i;
MapHash[0] = MapHash[k]; // 保存根节点的值
for(i=2*k;i<=len;i*=2){ // 遍历子节点
if(i<len&&MapHash[i].value>MapHash[i+1].value) // 找出子节点中较小的节点
i++;
if(MapHash[0].value<=MapHash[i].value) break; // 如果根节点小于等于子节点,则退出循环
else{
MapHash[k] = MapHash[i]; // 将子节点的值赋给根节点
k = i; // 更新k的值
}
MapHash[k] = MapHash[0]; // 将根节点的值赋给最终位置
}
}
// 建立小根堆
void BuildMinHeap(map* MapHash,int len){
int i;
for(i=len/2;i>0;i--) // 从最后一个非叶子节点开始调整堆
HeadAdjust(MapHash,i,len);
}
// 返回前k个出现频率最高的元素
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {
map* MapHash = (map*)malloc(sizeof(map)*(k+1)); // 分配存储堆的空间
// 设置哈希表
for (int i = 0; i < numsSize; i++) {
map* found = hashMapFind(nums[i]);
if (found) {
found->value++;
} else {
hashMapAdd(nums[i], 1);
}
}
// 初始化建立小根堆
map* TempHash = hashMap;
for(int i=1;i<k+1;i++){
MapHash[i].key = TempHash->key;
MapHash[i].value = TempHash->value;
TempHash = (map*)(TempHash -> hh.next);
}
// 建立小根堆
BuildMinHeap(MapHash,k);
map* s;
for(s = TempHash; s != NULL; s=(map*)(s -> hh.next)){ // 遍历哈希表
if(MapHash[1].value < s->value){
MapHash[1].key = s->key;
MapHash[1].value = s->value;
BuildMinHeap(MapHash,k);
}
}
int* elements = (int*)malloc(sizeof(int) * numsSize); // 分配存储返回结果的空间
int j=k;
for(int i=0;i<k;i++){ // 将堆中的元素存入结果数组
elements[i] = MapHash[j--].key;
}
*returnSize = k; // 设置返回结果的大小
hashMapCleanup(); // 清空哈希表
free(MapHash); // 释放堆的内存
return elements; // 返回结果数组
}