代码随想录第11天打卡·leetcode· 栈与队列part02:150. 逆波兰表达式求值;239. 滑动窗口最大值;

150. 逆波兰表达式求值

力扣题目链接(opens new window)

根据 逆波兰表示法,求表达式的值。

有效的运算符包括 + ,  - ,  * ,  / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

  • 输入: ["2", "1", "+", "3", " * "]
  • 输出: 9
  • 解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

思路:

1、遇到数字进栈

2、遇到运算符,pop出两个数字进行运算,将结果进栈

**注意:

1、字符串比较 注意Comparison with string literal results in unspecified behaviour

在C ++中,== 仅在内部为原始类型(如int、char、bool…)实现,而 const char* 不是原始类型,因此const char* 和字符串会作为两个char* 比较,即两个指针的比较,而这是两个不同的指针,“connection”== nextElement->Name()不可能为真。

可以使用:
1. 使用std::string保存字符串,通过其"==" 运算符重载比较两字符串,无需获取指针;
2. 使用 strcmp 比较char*和const char*,字符串相等则返回0;
原文链接:https://blog.youkuaiyun.com/xb_2015/article/details/118738192

2、atoi函数

将字符串直接转换成整型数据的操作

eg:将字符串 “ -12345” 转换成了整数 -12345  atoi("-12345")=-12345

3、注意是怎么判断是数字\符号的

符号:char==‘+’? 注意是单引号

        单引号‘+’ 是指向ascii码,“+”表示这是一个字符串

数字:有两个问题:要把字符串转化成数字(用atoi解决);要区分0~9和字符

        其实理论上也可以先把符号挑出来,其他的全部丢进atoi

4、char**怎么处理

char** 指向一个数组,数组中每一项的内容是字符串

形如:["2","1","+","3","*"]

通过这种方式读出每一项:

for (int i=0;i<tokensSize;i++){

        char* p=tokens[i];

代码:

typedef struct {
    int data[20000];
    int top;
}listnode;

void push(listnode *l,int e){
    l->data[++l->top]=e;
}

int pop(listnode *l){
    int e=l->data[l->top--];
    return e;
}

int evalRPN(char** tokens, int tokensSize) {
    listnode*l=(listnode*)malloc(sizeof(listnode));
    l->top=-1;

    for (int i=0;i<tokensSize;i++){
        char* p=tokens[i];
        
        if(strlen(p)>1 || (p[0]>='0' && p[0]<='9')){
            //为数字,考虑是多位数 一位数(用ascii码区别符号和一位数)的情况
            push(l,atoi(p));
        }
        else{
            int b=pop(l);
            int a=pop(l);
            if(p[0]=='+'){
                push(l,a+b);
            }
            else if(p[0]=='-'){
                push(l,a-b);
            }
            else if(p[0]=='*'){
                push(l,a*b);
            }
            else{
                push(l,a/b);
            }
        }
    }
    return pop(l);
    
}

239. 滑动窗口最大值

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

进阶:

你能在线性时间复杂度内解决此题吗?

思路:

只需要关心某一段中最大的元素,最小的元素可以丢掉

采用 单调队列,出口处是最大值

设计单调队列的时候,pop,和push操作要保持如下规则:

1、pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作

2、push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

在实际操作时,前k个元素进队时不需要返回最大值,故而单独拎出来操作

**注意:

一、在leetcode中返回数组

1、需要再分配空间

2、需要给*returnsize赋值 / 用*returnsize作为answer的序号

eg:

int *answer=(int *)malloc(sizeof(int)*(numsSize-k+1));
int *returnSize=numsSize-k+1;

 二、两端都是入口出口的队列

通过改变push pop中 front和rear的变化规则可以实现

区别++front &front++

代码:

typedef struct {
    int data[500000];
    int front,rear;
}listnode;

bool empty(listnode *l){
    if(l->front==l->rear) return true;;
    return false;
}

void push(listnode *l,int e){
    while(!empty(l) && e>l->data[l->rear]){//新来的元素,比队列前面的元素大,把队列里的全pop
        l->rear--;
    }
    l->data[++l->rear]=e;//队列为空,新来的元素小,新来的元素大——最终都要放入新来的元素
}

void pop(listnode*l,int nowmax){
    if(nowmax ==l->data[l->front+1]){//需要pop的情况
        l->front++;
    }
}

int getmax(listnode*l){
    return l->data[l->front+1];
}


int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {
    listnode *l=(listnode *)malloc(sizeof(listnode));
    l->front=-1;
    l->rear=-1;
    int maxnumber;

    //对于0~k-1 第一个窗口需要push k次,getmax一次,故而单独写出
    for (int i=0;i<k;i++){
        push(l,nums[i]);
    }

    int *answer=(int *)malloc(sizeof(int)*(numsSize-k+1));
    answer[0]=getmax(l);
    int answer_n=1;

    //对于k~numszie-1,每移动一次窗口:是否需要移出元素,是否需要移入元素,修改窗口后 得到最大值
    for (int i=k;i<numsSize;i++){
        pop(l,nums[i-k]);
        push(l,nums[i]);
        answer[answer_n++]=getmax(l);
    }
    *returnSize=numsSize-k+1;
    return answer;

}

347.前 K 个高频元素

力扣题目链接(opens new window)

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

  • 输入: nums = [1,1,1,2,2,3], k = 2
  • 输出: [1,2]

1、用快速排序的方法(利用qsort函数简化)

qsort函数:

qsort写法:void qsort(s, n, sizeof(s[0]), cmp);

第一个参数是参与排序的数组名(也就是开始排序的地址,所以&s[i],也是可以的)。
第二个参数是参与排序的元素的个数。
第三个参数是单个元素的大小(sizeof(s[0])这样就是获取到s[0]的元素大小)。
第四个参数是一个函数,定义qsort排序排序规则的函数。
原文链接:https://blog.youkuaiyun.com/qq_43448856/article/details/112749222

//定义结构体,记录数字及其出现的次数
struct Times{
    int num;
    int cnt;
};
//用于初始数组nums的排序
int cmp(const void* a,const void* b){
    return *(int*)a - *(int*)b;
}
//用于结构体的一级排序
int cmps(const void* a,const void* b){
    return (*(struct Times*)b).cnt - (*(struct Times*)a).cnt;
}

int* topKFrequent(int* nums, int numsSize, int k, int* returnSize){
    *returnSize = k;
    int* res = (int*)malloc(sizeof(int)*k);
    struct Times time[numsSize];
    int index=0;
    //1.对初始数组nums进行排序
    qsort(nums,numsSize,sizeof(int),cmp);
    //2.遍历计算各个数字出现的次数
    time[index].num = nums[0];
    time[index].cnt = 1;
    for(int i=1;i<numsSize;i++){
        if(nums[i]==nums[i-1])time[index].cnt++;
        else{
            time[++index].num = nums[i];
            time[index].cnt = 1;
        }
    }
    //3.对结构体数组按出现次数降序排序
    qsort(time,index+1,sizeof(struct Times),cmps);
    //4.返回前k个数字
    for(int i=0;i<k;i++){
        res[i] = time[i].num;
    }

    return res;
}
//作者:Shaw 链接:https://leetcode.cn/problems/top-k-frequent-elements/solutions/404234/bi-jiao-zhi-bai-de-si-lu-he-jian-dan-de-dai-ma-jie/

2、堆排序 

优先队列

1、用大顶堆还是小顶堆:

假如用大顶堆:定义一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。

故而用小顶堆要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

不断把较小元素弹出,留下较大元素

2、​​​​​​​具体过程

先放入k个元素,初始化堆

若新的元素比堆顶大,则弹出堆顶,放入新的元素至堆顶,调整至小顶堆

**使用的注意事项:

1、sift函数:将low位置的元素,按大顶堆or小顶堆的规则向下安置至合适的位置,high是实取

        以小顶堆为例:

        找到孩子中较小的那个(要求孩子结点编号j<high)

        与孩子中较小的项进行比较,父结点>较小孩子,则父结点与较小孩子 交换位置

        直至父结点<孩子中较小的那个(即比两个孩子都小)

2、初始化堆:

        找到倒数第二排最后一个结点,从这个结点向前sift(堆,移动结点i,总长度);

        (因为最后一排的结点不可能再下沉了)

3、要记得!比较的是value值!

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

typedef struct {
    int key;
    int value;
}record;

//用于初始数组nums的排序
int cmp(const void* a,const void* b){
    return *(int*)a - *(int*)b;
}

void sift(record* a,int low,int high){//给low安置一个合适的位置
    int i=low,j=2*i+1;//因为是从0开始,两个孩子分布对应2i+1 和2i+2
    record tmp=a[i];
    while(j<=high){
        if(j<high && a[j].value>a[j+1].value) j++;//j落在更小的孩子上
        if(tmp.value>a[j].value){
            a[i]=a[j];
            i=j;
            j=2*i+1;
        }
        else break;
    }
    a[i]=tmp;
}


int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {
    record *r=(record*)malloc(sizeof(record)*numsSize);//记录频率
    record *a=(record *)malloc(sizeof(record)*k);//记录堆排序的过程
    int *ans=(int *)malloc(sizeof(int)*k);//记录key
    *returnSize=k;

    int t=0;
    qsort(nums,numsSize,sizeof(int),cmp);//从大到小排序,方便录入record

    r[0].key=nums[0];
    r[0].value=1;
    for(int i=1;i<numsSize;i++){
        if(nums[i]==r[t].key) r[t].value++;
        else{
            r[++t].key=nums[i];
            r[t].value=1;
        }
    }

    int j=0;
    for (int i=0;i<k;i++){
        a[j++]=r[i];
    }
    for(int i=(k-2)/2;i>=0;i--){
        sift(a,i,k-1);
    }
    for(int i=k;i<=t;i++){
        if(r[i].value>a[0].value){
            a[0]=r[i];
            sift(a,0,k-1);
        }

    }
    for(int i=0;i<k;i++){
        ans[i]=a[k-1-i].key;
    }
    return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值