练习
使字符串平衡的最少删除次数
给你一个字符串 s ,它仅包含字符 'a' 和 'b' 。
你可以删除 s 中任意数目的字符,使得 s 平衡 。当不存在下标对 (i,j) 满足 i < j ,且 s[i] = 'b' 的同时 s[j]= 'a' ,此时认为 s 是 平衡 的。
请你返回使 s 平衡 的 最少 删除次数。
示例 1:
输入:s = "aababbab"
输出:2
解释:你可以选择以下任意一种方案:
下标从 0 开始,删除第 2 和第 6 个字符("aababbab" -> "aaabbb"),
下标从 0 开始,删除第 3 和第 6 个字符("aababbab" -> "aabbbb")。
示例 2:
输入:s = "bbaaaaabb"
输出:2
解释:唯一的最优解是删除最前面两个字符。
提示:
1 <= s.length <= 105
s[i] 要么是 'a' 要么是 'b' 。
class Solution {
public int minimumDeletions(String s) {
int i=0; int j =0;
for (char str : s.toCharArray()){
if(str =='b'){
i++;
}else {
j=Math.min(j+1,i);
}
}
return j;
}
}
总结下
这次没写注释,因为理解的不透彻 就是写写自己的想法
我对题意的理解是 存在数组i和数组j 要求它们连贯 且i>=j
i和j都是只能a或者b
因为不用写出来最后的字符串,只记录它们最少删除次数
其实就是出现ba 或者ab时,要删除一个字符来保持连贯
至于a多还是b多 其实在不写出来字符串时都是一样的
也就是说 只要比一个分支就行
以前面的都是a,后面的都是b为例
要么b是最后 比它之前的是不是都是b最后 如果是,就不用管 因为没说要输出字符串,就比情况,无论是b多还是a多 都符合上面的条件
要么a是最后 两种情况
一种是它之前的b全删
一种是删a
因为设置了条件 前面全是a 后面全是b
也就是说 需要对比 Math.min(a+1,b) 是删b划算还是删a划算
另一种也是大神写的
要删除的a都是后缀 要删除的b都是前缀
以这个想法 分割字符串,前面统计b后面统计a 得出删的最少的
分割线就以指针为例
遍历移动指针 统计前面的b 统计后面的a 当两者相加最少时 就是最少删除次数
class Solution {
public int minimumDeletions(String S) {
var s = S.toCharArray();
int del = 0;
for (var c : s)
del += 'b' - c; // 统计 'a' 的个数
//我的理解:字符串是有自己的值的 减自己是0 减前面一位是1
int ans = del;
for (var c : s) {
// 'a' -> -1 'b' -> 1
del += (c - 'a') * 2 - 1;
//我的理解:不用if-else确实很大神 但是理解就要理解一下了
//a-a=0-1=-1 b-a=1*2-1=1 每次计算都要加上del 这样得到每次指针移动要删除a b的值
ans = Math.min(ans, del);
//我的理解:就是记录最少删除的值
}
return ans;
}
}
八股
什么是死锁?
当两个或者两个以上的进程在执行过程中,由于竞争资源导致彼此都无法完整的进行推进,这时候就系统就成为了一个死锁
怎么防止死锁?
死锁形成的原因
互斥条件 资源被一个进程获取后,别的进程是无法访问这个资源的,只能等待该进程完成才可以获得。
请求保持条件 进程获取资源后会请求下一个资源,如果下一个资源已经被别的进程获得,它就会请求阻塞,但又不释放自己已经获取的资源
不可剥夺条件 进程获取的资源在不使用的之前是不可释放的
环路等待条件 多个进程之间发生死锁,必然是头尾相接的循环等待资源条件
一旦发生死锁,上述四个条件必然发生,所以理解死锁的形成,就可以避免预防阻止死锁
为避免进程永久占用系统资源,要合理分配资源
在 java 程序中怎么保证多线程的运行安全?
原子性 线程之间互不干扰,同一时刻只有一个线程可以进行数据操作
可见性 线程的对内存的修改可以被其他线程看见
有序性 一个线程观察其他线程指令执行顺序,因为指令的重排序,观察结果是无序的(遵循happens-before原则)
PS:happens-before原则
A 和 B 彼此不具有数据依赖性 在不影响结果的前提下 允许对AB两个操作重排序
多线程锁的升级原理是什么?
有四种状态 无状态锁 偏向锁 轻量级锁 重量级锁(
锁只能升不能降
当线程未活动时 偏向锁转成无状态锁
当线程活动时 偏向锁转成轻量级锁
自旋失败 轻量级锁转成重量级锁
ps
重量级锁:我们知道,我们要进入一个同步、线程安全的方法时,是需要先获得这个方法的锁的,退出这个方法时,则会释放锁。如果获取不到这个锁的话,意味着有别的线程在执行这个方法,这时我们就会马上进入阻塞的状态,等待那个持有锁的线程释放锁,然后再把我们从阻塞的状态唤醒,我们再去获取这个方法的锁。这种获取不到锁就马上进入阻塞状态的锁,我们称之为重量级锁。
轻量级锁:线程在抢锁失败后进入阻塞状态,放弃 CPU,需要过很久才能再次被调度。但经 过测算,实际的生活中,大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。基于这个 事实,轻量级锁 / 自旋锁诞生了。(也就是重量级是一直阻塞 轻量级会隔一段时间去访问一次)
nginx第四天
虚拟主机
default.conf中的server
原本一台服务器只能对应一个站点,通过虚拟主机技术可以虚拟化成多个站点同时对外提供服务
servername匹配规则
我们需要注意的是servername匹配分先后顺序,写在前面的匹配上就不会继续往下匹配了。
完整匹配
我们可以在同一servername中匹配多个域名
server_name vod.mmban.com www1.mmban.com;
通配符匹配
server_name *.mmban.com
通配符结束匹配
server_name vod.*;
正则匹配
server_name ~^[0-9]+\.mmban\.com$; 以0-9数字可以有好多个
域名解析相关企业项目实战技术架构
多用户二级域名
eg ym.weibo.com *.weibo.com
ym 就是二级域名 保持唯一
![](https://i-blog.csdnimg.cn/blog_migrate/9bb579956ce999c9491d933fc9a997f8.png)
短网址
点击一个短网址(唯一 key)会跳转到真实地址(value)
![](https://i-blog.csdnimg.cn/blog_migrate/fa8d800216982a17a8fa62d56af0f14c.png)
其实还是反向代理
全图
![](https://i-blog.csdnimg.cn/blog_migrate/1249c875d7c14f9d42a2788f41ab99a6.png)
HttpDns
一般存在客户端 不适合网页 适合c/s架构 或者app 会存一个ip地址
会请求一个真实的地址
例如 baidu.com 发向nginx 之后会从db查到该信息 再返回(域名解析)
会传回来一个192.168.11.101/get.php?name=baidu.com
反向代理
网关、代理与反向代理
用户想要直接访问tomcat服务器是不可行的,需要nginx转发给tomcat
再由tomcat转发给nginx 再给用户 nginx就相当于中间商一样
隧道式代理
由nginx转发给后端 再由后端转发给nginx
![](https://i-blog.csdnimg.cn/blog_migrate/ba2775ab5ec31c588a179ff711652ded.png)
DR --依靠虚拟伪装IP完成
用户请求时转发给nginx nginx再转发给后端
由后端直接转发给用户
例如如果要下载文件 后端传递给nginx会占用大量资源 所以传向用户的代理服务器会节省很多资源
lvs 则是包容了上两个方式代理
正向代理是
用户通过网关(代理服务器) 去访问外网
另外说明 即使访问又100M的速度
但是网关只有10M 那最大访问也是10M
怎么提升呢? 只能提升代理服务器
![](https://i-blog.csdnimg.cn/blog_migrate/62f85e7ed5dfb1d6a391c4c6d3d64777.png)
全图
![](https://i-blog.csdnimg.cn/blog_migrate/4d1f9617bdae24e11b2b16bd6a7792c2.png)
反向代理在系统架构中的应用场景
传统项目
用户经过网络 解析域名IP(DNS) 发到互联网
打入机房网关中转请求 再经过防火墙 访问nginx服务器(进行反向代理)
nginx就是吧所有请求转向给后端服务器
中小型项目
前面一样
到nginx 会启动更多的功能,会伪装url 之后再发给服务器
可能一台nginx后面有几十台服务器
eg: /itemservice?id=100 伪装后 /item/100.html 其实没有这么多html 但是例如
京东 淘宝 这种上亿的商品 就像是独立页面
如果是文件下载,就会分成好几组nginx(负载均衡)
QPS 并发量(EG:同时点击300)
Nginx的反向代理配置
设置多个nginx后
在default.conf里面的service
proxy_pass http://atguigu.com/
配一个的方式
location / {
proxy_pass http://wwww.atguigu.com/;
这样用户访问自己的LINUX的IP 会进去这个网页
然后点击别的网页都会加载到.com/aaa.html斜杠后面
proxy_pass http://atguigu.com/;
地址竟然是www.atguigu.com 竟然跳转了(一个站点会跳转另一个站点)
因为控制台返回302跳转请求 找不到 会去重新加载地址
proxy_pass http://192.168.44.102;
在192.168.44.101上代理102的nginx
}
负载均衡
同样在在default.conf里面的service
例如在192.168.44.101:80里面 简单的轮询 一人一下
upstream httpds {
server 192.168.44.102:80;
server 192.168.44.103:80;
}
server{
....
location/{
proxy_pass http://httpds;
}
}
基于反向代理的负载均衡器
什么是负载均衡
如果一台服务器不可用(故障) nginx可以转移到另一台服务器
而这种可以承载这种可以转移数据的服务器 就是集群
而这种集群就可以通过算法 去逐个转发到别的服务器
例如轮询(一人一下) 如果有一台服务器访问失败(故障了) 就下线 通过retry去访问别的服务器
负载均衡就是用于故障,维护,承载更多数据等作用
负载均衡策略
轮询
默认情况下使用轮询方式,逐一转发,这种方式适用于无状态请求。
weight(权重)
权重越大 轮询的越多 weight=10就是轮询10次再转发下面
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream httpd {
server 127.0.0.1:8050 weight=10 down;
server 127.0.0.1:8060 weight=1;
server 127.0.0.1:8060 weight=1 backup; 备用机
}
down:表示当前的server暂时不参与负载(例如维护时)
weight:默认为1.weight越大,负载的权重就越大。
backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。
每次修改都需要重启下nginx
不常用 因为没法动态上线
ip_hash
根据客户端的ip地址转发同一台服务器,可以保持回话。
least_conn
最少连接访问
url_hash
根据用户访问的url定向转发请求
fair
根据后端服务器响应时间转发请求
无状态回话解决方案
尚硅谷nginx 26讲的很多 但是很多都没遇上,所以以后请回来看一看
因为今天事情比较多,上午有点事,下午三点前还在听班会
用了两个小时写算法和背八股(算法小白的我)
真正听课已经都五点了
但是能听到20.48 也不错了,中间又发生了一些小问题,不过很快就解决了
而且更多的是重复听一节课 例如26 我听了快四十分钟了,现在又忘了
真的 学着忘着 太离谱 讲了很多问题,但是我记不住,因为很多都没遇见
特别是最后的无状态回话,session cookie问题 而是让令牌去控制 真的很实用,而且遇到过
为了防止以后记不起来,写一段话来让自己记住