Bootstrap

力扣(LeetCode)496. 下一个更大元素 I(C语言)

一、环境说明

  1. 本文是 LeetCode 496题 : 下一个更大元素 I,使用c语言实现。
  2. 单调栈+哈希链表。
  3. 测试环境:Visual Studio 2019。

二、代码展示

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
//找到nums1[i]在nums2的位置j,往后遍历
//if(nums2[j]>nums1[i]){return ans;}
//return -1;
#define MaxSize 769 //桶的数量
typedef struct LinkNode{
    int elem;//值
    int key;//键
    struct LinkNode *next;
}LinkNode,*LinkList;//链表

typedef struct {
    LinkList hashHead[MaxSize];//桶
} MyHashSet;//哈希表

MyHashSet* myHashSetCreate() {//创建哈希表
    MyHashSet *newHash = (MyHashSet*)calloc(1,sizeof(MyHashSet));
    return newHash;
}

LinkNode* myHashSetGet(MyHashSet* obj, int elem) {//返回存传入值的结点。
    LinkList curList=elem<0?obj->hashHead[(-elem)%MaxSize]:obj->hashHead[elem%MaxSize];//curList指向被搜索的桶
    while(curList){
        if(curList->elem==elem){
            return curList;
        }
        curList=curList->next;
    }
    return NULL;//没有这个元素
}

void myHashSetAdd(MyHashSet* obj, int elem, int key) {//增加元素
    LinkList curList = elem<0?obj->hashHead[(-elem)%MaxSize]:obj->hashHead[elem%MaxSize];
    if(myHashSetGet(obj,elem)){//已存在该元素,插入失败
        return;
    }
    //插入操作
    LinkList inNode = (LinkList)calloc(1,sizeof(LinkNode));
    inNode->elem = elem;
    inNode->key = key;
    inNode->next = curList;//头插法,头结点也有元素
    if(elem>=0){
        obj->hashHead[elem%MaxSize] = inNode;
    }else{
        obj->hashHead[(-elem)%MaxSize] = inNode;
    }
}

void myHashSetFree(MyHashSet* obj) {//释放哈希表空间
    int i=0;
    LinkList freeNode,curNode;
    for(i=0;i<MaxSize;i++){//遍历每个桶
        freeNode = NULL;
        curNode = obj->hashHead[i];
        while(curNode){//释放桶中结点
            freeNode = curNode;
            curNode=curNode->next;
            free(freeNode);
        }
        obj->hashHead[i]=NULL;
    }
    free(obj);
}
//存nums2每个元素的下一个更大元素的值
int* nextGreaterElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
    MyHashSet *hash = myHashSetCreate();
    int s[nums2Size+2];
    int top = 0,bottom = 0;//栈顶和栈底
    returnSize[0]=nums1Size;//答案规模是nums1的元素个数
    for(int i = nums2Size-1;i>=0;i--){//从后往前遍历nums2
        if(top==bottom){//栈为空
            s[++top] = nums2[i];//nums2[i]入栈
        }
        while(top>bottom&&nums2[i]>s[top]){//栈非空,nums2[i]大于栈顶元素
            top--;//出栈
        }
        if(top!=bottom&&s[top]>nums2[i]){//栈非空,nums2[i]小于栈顶元素
            myHashSetAdd(hash,nums2[i],s[top]);//值是nums[i],键是s[top]。
            s[++top]=nums2[i];//nums2[i]入栈
        }else{//栈为空
            myHashSetAdd(hash,nums2[i],-1);//值是nums[i],键是-1。
            s[++top]=nums2[i];//nums2[i]入栈
        }
    }
    int *ans =(int*)calloc(nums1Size,sizeof(int));
    for(int i = 0;i<nums1Size;i++){//
        ans[i] = myHashSetGet(hash,nums1[i])->key;//保存nums1[i]对应的键。
    }
    return ans;
}

三、思路分析

  • 建立一个单调栈 s s s,从后往前遍历 n u m s 2 nums2 nums2,当前位置记作 i i i。执行如下操作:
  1. 如果 s s s空, n u m s 2 [ i ] nums2[i] nums2[i]入栈;如果 s s s非空,且 s [ t o p ] > n u m s 2 [ i ] s[top]>nums2[i] s[top]>nums2[i],栈顶元素 s [ t o p ] s[top] s[top]弹栈。
  • 重复上述操作,每次操作时,如果栈空,说明 n u m s 2 + i nums2+i nums2+i后没有数比 n u m s 2 [ i ] nums2[i] nums2[i]大,对应 n u m s 1 nums1 nums1的答案就是-1;如果栈非空,说明 n u m s 2 + i nums2+i nums2+i后有数比 n u m s [ i ] nums[i] nums[i]大,且最靠近 n u m s [ i ] nums[i] nums[i]的数就是 s [ t o p ] s[top] s[top]
  • 寻找 n u m s 2 [ i ] nums2[i] nums2[i]右侧最近的大于 n u m s 2 [ i ] nums2[i] nums2[i]的数,特别像栈后进先出的性质,这就是本题使用栈的原因。

四、代码分析

  • 寻找到的数,我们用哈希表保存。
  • C没有内置的hashmap。
  • 力扣常用的UThash是用户写的,vs环境默认没有UThash,不好调试。
  • 博主自己写一个简易的链表hash,大家看看就行,重在理解hash思想。
  • 想了解hash思想的同学,推荐设计哈希集合

五、AC

AC

AC

六、复杂度分析

  1. 时间复杂度: O ( m + n ) O(m+n) O(m+n) , n u m s 1 nums1 nums1数组的大小= m m m, n u m s 2 nums2 nums2数组的大小= n n n。一次遍历 n u m s 1 和 n u m s 2 nums1和nums2 nums1nums2的时间复杂度是 O ( m + n ) O(m+n) O(m+n)
  2. 空间复杂度: O ( n ) O(n) O(n),哈希链表的大小和 n u m s 2 nums2 nums2的大小相等,哈希链表的空间复杂度: O ( n ) O(n) O(n)
;