【review】程序语言、编译过程、符号表、数组续(滑动窗口---变双指针)

Intern plan


程序语言【汇编】、数组专题续【二分、双指针----> 最大连续字段】


昨天已经介绍了计算机的可靠性,计算机的子系统的连接的方式就传串联、并联等,注意可靠性R(t)和失效率λ的关系; R = e^(-λ)t ; 以及计算机的加密技术和认证技术

分析了一个双指针的复杂一点的问题,字符串比较最主要就是让index停在正确的位置等待比较,需要注意错误的情况

程序语言

程序语言概念

程序实际语言的基本成分包括数据、运算、传输、控制等;高级程序设计语言是不依赖具体的机器硬件的;只是其他的高级没有java的跨平台性,但是也是可以在不同类型的机器上都可以编写C程序的

低级语言和高级语言

这个问题其实已经是非常的浅显了,计算机识别的语言就是机器码【0、1组成】,低级语言就是机器语言和汇编语言的统称,是面向机器的语言,格式取决于计算机的机器指令,programmer难以理解,可读性不好,设计效率低

高级语言: 面向各类应用的语言,比如java、C等,这些语言对于programmer来说更加容易理解,效率较高

高就程序设计语言中,PHP就是适合处理动态网页的处理,HTML是静态网页的处理,JavaScript是编写前台的脚本

汇编、编译、解释

这几个名词也是常见的名词,还是比较容易理解的;他们都是高级程序语言翻译为计算机硬件理解的语言所使用的方式

  • 汇编语言编写的: 使用汇编程序将源程序翻译为目标程序,然后执行目标程序
  • 高级语言编写的,需要编译程序或者解释程序进行翻译,然后运行
编译器【javac】   将源程序翻译为目标语言程序,机器上运行的是翻译后的程序,和源程序和编译程序无关 ,java只是翻译为字节码bytecode,.class文件

解释器:直接解释执行源程序,或者翻译成某种中间代码执行,并且与逆行过程于言承旭和解释程序有关;----  这是和编译器最大的区别 

比如100行代码,解释器就是一边解释一边执行; 而编译器将100行代码编译完成之后,执行编译的程序

系统程序开发更加适合使用编译型语言

编译器生成独立的目标程序,编译完之后执行目标程序, 解释器不生成独立的目标程序,解释之后立即执行—JavaScript、python

  • 编译可能闭解释效率高
  • 解释方式比编译方式更加灵活、可移植性好 【java跨平台 - JVM 半编译半解释】

程序设计语言定义 — 语法、语义、语用

这几个概念还是提一下,我乍看都不知道语用是个啥

  • 语法: 就是一组规则

  • 语义: 静态语义—编译时可以去顶成分的含义和动态语义-----运行时才确定

  • 语用: 构成语言的记号和使用者的关系

关于数据成分、运算成分、和控制成分(顺序、选择、循环)就不细说了,easy~

【特殊类型:void 用户定义类型: enum 构造类型: 数组、结构、联合 指针类型 抽象数据类型】

系统为全局变量分配的存储单元一般是不可改变的,而局部变量的存储单元是可以动态改变的

堆和栈 --- 都是动态存储  --- 局部变量

全局变量存储在静态数据区

编译过程

编译程序的作用就是将源高级语言程序翻译成与之等价的目标程序,工作过程一般分为六个阶段:

查看源图像

前面的词法分析、语法分析、语义分析、中间代码生成 ---- 前端 和高级语言相关; 后面的两个过程(代码优化、目标代码生成) — 后端 和机器相关

在对高级语言程序进行编译的过程中,为源程序变量分配的存储单元的地址为逻辑地址,最后运行的时候才转化为物理地址(之前的CPU的中的地址转换也是硬件和操作系统—主存和辅存)

词法分析

单词法则,就是对源程序从前到后逐个字符扫描,【从中识别出一个个单词】,单词就是程序设计语言的基本语法单位 ----- 关键字、保留字、标识符、常数、括号 ,标点符号 - 都是词法分析的内容

所以比如将int 写成了 ant ,英文分号写成中文分号,那么词法分析就发现问题,出现错误,所谓编译阶段就过不去【第一个阶段都过不去:happy:

语法分析

根据语言的语法规则(不同的语言的表达不同),在词法分析的基础上,分析单词串是否构成短语和句子,判断是否合法,同时检查语法错误; 如果源程序没有语法错误,语法分析后就可以构造出其语法树

比如for(i = 0; i < num; i ++) 这里i没有进行声明,所以编译的第二阶段就报错了

语义分析

分析各语法的结构的含义,检查源程序是否包含静态语义错误,手机类型的信息供代码生成阶段使用; 语义分析的主要工作就是进行类型的分析和检查;

比如一个int num = 11.2%2; 这个表达式没有词法错误,也没有语法错误,但是就是语义出现了问题,=后边的含义是用浮点数取余,出现了问题,类型不匹配呢

中间代码生成

根据语义分析的结果生成中间代码,常用的中间代码有: 后缀式【逆波兰式】、四元式【三地址码】、树型表示

符号表管理

符号表的作用就是记录源程序中各种符号的必要信息,辅助语义的正确性检查和代码生成,在编译过程中需要对符号表进行快速有效查找、插入、修改、删除等

//符号表的主要作用就是辅助后面的语义分析和代码检查
num   int   1
num2  double 2.2
stu   Student  [....]

编译程序对高级语言程序进行处理的过程中,不断收集、记录和使用源程序的一些相关符号的类型和特征信息 --- 其实其存在  符号表  中

符号表就是对源程序的各个符号的必要信息进行记录

出错处理

源程序中肯定会有一些错误,分为静态错误和动态错误【在java中按照异常就是可检查异常和运行时异常】

  • 动态错误发生在程序时,比如/0,下标越界 【运行时异常jav】
  • 静态错误指编译阶段发生的程序错误,分为语法错误和静态语义错误
    • 语法错误: 单词、符号,表达式缺少、括号缺少等easy 问题
    • 静态语义错误: 语义分析时发现的运算符和运算对象不合法等

中缀、前缀、后缀

这个在二叉树变换的时候经常考【前序,中序、后序遍历】

  • 中缀表达式: 就是平常使用的表达式,运算符在中间【a + b * c]
  • 前缀表达式:(波兰表达式) 将运算符写在前面,操作数写在后面,且不使用括号 【先序遍历—根左右; +a*bc】
  • 后缀表达式:(逆波兰式) 将运算符写在后面,操作数卸载前面,不使用括号 【后续遍历,左右根---- abc*+】 ---- 可以轻松用栈实现运算

中序表达式转为前序表达式,就是按照运算的优先级依次加上括号;然后从内向外依次变形,将操作符放在括号的前面去,操作数留下;依次执行, 最后去掉括号得到波兰式; 后缀表达式和其方法类似

中序: (a+b)*c -d

前缀表达式:(((a+b)*c)-d) ----> -(*(+(ab)c)d) ---> -*+abcd


后缀表达式: (((a+b)*c)-d) ----> (((ab)+c)*d) ---> ab+c*d

值传递和引用传递就不分析了,主要是注意包装类型和String为final修饰的;java中的对象调用就是引用传递;值传递方式下,实参可以是变量,也可以是常量和表达式

Part 2

🤭,还是先看看昨天的遗留问题,昨天的复杂度高达O(N2),如何优化呢?

同时我要纠正一下昨天的问题,不要遍历数组来判断0的情况

for(int i = 0; i < nums.length; i ++) {
            sum += nums[i];
        }
         //正整数,那就可以直接path
        if(sum < target) {
           return 0;
        }

这里就是因为没有正确的使用初始值min,输出的时候加一个三元表达式即可。。。。当时太急了

 return min == 1000000 ? 0 : min;

滑动窗口【双指针变种】

  • 博主认为这里适用于双指针之间的序列有实际意义的题目

之前分析的双指针有两种形式,一般都是用于空间复杂度为O(1)的数组删除等;分为同向行驶的,i= k = 0;还有就是相向行驶的,相向行驶终止条件就是相等的时候就完了; 这里的典型就是之前的对有序的数组【含负数】进行平方排序

而这里的滑动窗口,就是两个指针也是同向行驶,但是这里将两个指针之间的序列当作窗口,最大的窗口长度就是数组整个,这里的和之前的同向的双指针不同就是之前的双指针在意的是位置,不考虑两个指针之间的序列的意义,这里就可以考虑

这里既然都是正整数,那么两个指针之间就是待判断的序列;end指针移动之后,到达了满足targert的位置之后,start指针向后滑动,如果不满足条件,end再向后滑动;【之前的暴力解法,就是start一直向前移动,end每次移动之后的位置都被浪费了,需要重新又从start开始移动,所以多了时间的复杂度】 ---- 其实思想和记忆化搜索相同,既然之前现成的end的位置,那么就不要浪费了

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
       int start = 0, end = 0;
       int min = Integer.MAX_VALUE;
       int sum = 0;
       while(end < nums.length) {//尾指针最多移动到最后
            sum += nums[end];
            while(sum >= target) {//这里的复杂度没有O(n),因为start++的次数是可数的,为常数
                //当窗口的值大于的时候就移动前面的指针
                //记录值
                min = Math.min(min,end  + 1 - start);
                //同时将窗口序列变化
                sum -= nums[start];
                //删除之后变动
                start ++;
            }
            end ++;  //while循环要自己写
       }
       return min == Integer.MAX_VALUE ? 0 : min;
    }
}

这里题目就完成了

904. 水果成篮 - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
    public int totalFruit(int[] fruits) {
        //暴力解法就是遍历,然后还是指针遍历,判断两个指针之间的种类是不是2种的最小
        //这里的要求就是两个棵树之间的满足种类为2的最大的窗口的长度
        int start = 0, end = 0;
        //存储种类的数组
        ArrayList kindList = new ArrayList();
        int kind = 0; //记录当前种类
        int max = 0;
        while(end < fruits.length) {
            //每次就判断种类
            if(!kindList.contains(fruits[end])) {
                kindList.add(fruits[end]);
                kind ++;
            }
            while(kind > 2) { //这里判断什么时候窗口尾指针不动了
                //给出当前max
                max = Math.max(max,end - start); //窗口的长度
                kind -- ; //start的位置的种类一定是算入的
               for(int i = 0; i < kindList.size(); i ++) {
                   if(kindList.get(i).equals(fruits[start])) {
                       kindList.remove(i);
                   }
               }
                start ++;
            }
            //考虑一下start不懂,尾指针划出的时候,这里有很多解
            if(end == fruits.length) {
                max = Math.max(max, end - start);
            }
            end ++;
        }
        return max;
    }
}

这个题有点意思,明天继续,今天状态不好😢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值