51 和的逆运算

这题在给定的和不重复的情况下很简单:

  1. 首先升序排序好数组sums,生成答案数组nums[n]
  2. nums[0] + nums[1] 必然等于sums[0](最小值),nums[0] + nums[2] 必然等于sums[1](次小值), … , nums[n-2] + nums[n-1] 必然等于sums[lastIndex](最大值)。
  3. 可以反向推测出nums[0] = (sums[0] + sums[1] - sums[n-1]) / 2,论证看下方:
nums[n] = {a, b, c, d, e} 从小到大排列
a + b = sums[0] (1) // 最小
a + c = sums[1] (2)
a + d = sums[2]
a + e = sums[3]
b + c = sums[4] (3)
...
(1) + (2) = 2a + (3)
2a = sums[0] + sums[1] - sums[n-1] = (1) + (2) - (3)
  1. 得出了nums[0],其他数字都可以用nums[i] = sums[i-1] - nums[0]推出来

但是给定的和重复的情况下,上面的第2条就不成立了。例如测试用例3:

sums[] = { 223, 224, 225, 225, 226, 226, 227, 227, 228, 229 }
nums[n] = { 111, 112, 113, 114, 115 }
a + b = 223
a + c = 224
a + d = 225
a + e = 226
b + c = 225 // 打破了第2条假设,不是按升序排序!

在给定的和有重复元素的情况下,我们再按照上面的步骤1排序好数组sums,算出来的nums[0]就是错误的,因为这个时候b+c < a+ea+e排到了b+c的位置,再套用步骤3的算法就不对了。
a+b+a+c2a+a+ea + b + a + c \neq 2a + a + e

综上所述,重新整理后我们会发现,排序数组不是目的。真正的目的是能够让数组符合步骤3的公式nums[0] = (sums[0] + sums[1] - sums[n-1]) / 2,让b+c能够正确地出现在sums[n-1]的位置。
可是,我们并没有原数组,怎么做的出来呢?

数学家思维:给定一个有序的数组nums,其中元素按升序排列,一定存在一个有序两两和序列sums。

找到这个有序两两和序列,就可以根据上面的算法反推出原来的数组。

我不知道有没有一种数学算法可以优雅地找到有序两两和序列sums,一举这个问题。但是全排列一定能找到这个sums。以下是第51题的全部代码:

import java.util.Arrays;

public class Main {

static String ans;

public static String solution(int n, int[] sums) {
ans = "Impossible";
fullArrange(sums, 0, n);
// System.out.println(ans);
return ans;
}

private static void check(int n, int[] sums) {
int[] nums = new int[n];
nums[0] = (int) Math.round((sums[0] + sums[1] - sums[n-1]) / 2.0);
for (int i = 1; i < n; i += 1) {
nums[i] = sums[i-1] - nums[0];
}

for (int i = 0, k = 0; i < n; i += 1) {
for (int j = i+1; j < n; j += 1) {
if (nums[i] + nums[j] != sums[k++]) {
return;
}
}
}

StringBuilder result = new StringBuilder();
Arrays.sort(nums);
for (int num: nums) {
result.append(num + " ");
}
ans = new String(result.deleteCharAt(result.length()-1));
}

// 全排列,确定sums第k位的值
private static void fullArrange(int[] sums, int k, int n) {
if (k == sums.length) {
check(n, sums);
return;
}

for (int i = k, l = sums.length; i < l; i += 1) {
swap(sums, i, k);
fullArrange(sums, k + 1, n);
swap(sums, i, k);
}
}

private static void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

public static void main(String[] args) {
// You can add more test cases here
int[] sums1 = {1269, 1160, 1663};
int[] sums2 = {1, 1, 1};
int[] sums3 = {226, 223, 225, 224, 227, 229, 228, 226, 225, 227};
int[] sums4 = {-1, 0, -1, -2, 1, 0, -1, 1, 0, -1};
int[] sums5 = {79950, 79936, 79942, 79962, 79954, 79972, 79960, 79968, 79924, 79932};

System.out.println(solution(3, sums1).equals("383 777 886"));
System.out.println(solution(3, sums2).equals("Impossible"));
System.out.println(solution(5, sums3).equals("111 112 113 114 115"));
System.out.println(solution(5, sums4).equals("-1 -1 0 0 1"));
System.out.println(solution(5, sums5).equals("39953 39971 39979 39983 39989"));
}
}

基本操作

  • <C-o>, <C-i>:回到前一个/后一个位置(例如,打开文件默认在第一行,<C-o>回到上次编辑位置)。注意这个操作是跨文件的
  • `0:返回上次位置
  • 词:w(下一个词),b(词初),e(词尾)
  • 行:0(行初),^(第一个非空格字符),$(行尾)
  • 文件:gg(文件头),G(文件尾)
  • 搜索:/{正则表达式},n/N用于导航匹配
  • x删除字符(等同于dl
  • s(substitute)替换字符(等同于xi
    • 替换命令:{作用范围}s/{目标文本}/{替换文本}/{替换标志}
    • :%s/s_content/o_content/g 全局替换
Sign Range
% 整个文件
. 当前行
$ 最后一行
,n 当前行到n行
n, n行到当前行
+n 当前行后n行
  • 可视化模式 + 操作
    • 选中文字,d删除(剪切) 或者c改变
  • u撤销,<C-r>重做
  • y复制 / “yank” (其他一些命令比如d也会复制)
  • p粘贴
    • +p 粘贴系统剪贴板
  • 更多值得学习的:
    • :<line> 跳到line行
    • ~改变字符的大小写
    • 3w向前移动三个词
    • A(大写)可以迅速定位到行尾进行修改
    • :!python prog.py 使用!直接运行shell命令
  • %匹配括号

多行操作

<C-v> 选中多行,Shift + i输入后Esc,即可多行同步输入

录制宏

命令模式下,

  1. q<marcoName>进行录制,<marcoName>是宏的名字,例如qa
  2. 执行一系列操作后,再次按q结束录制

使用宏

命令模式下,使用@<marcoName>即可执行;使用5@<marcoName>可以重复执行宏

文件操作

  • :e filename切换到filename文件
  • :bn/bp切换到下/上个文件
  • <C-x><C-f> 自动补全路径

微服务

非单体项目,可以用下面的脚本启动微服务。

#!/bin/bash

# 获取用户输入的服务名称
SERVICE_NAME=$1

# 检查是否输入参数
if [ -z "$SERVICE_NAME" ]; then
echo "Usage: ./run.sh <servicename|all>"
exit 1
fi

# 定义运行单个服务的函数
run_service() {
local service=$1
echo "Building and running $service..."
mvn clean install -pl $service -am
mvn spring-boot:run -pl $service
}

# 如果输入 "all",运行所有服务
if [ "$SERVICE_NAME" == "all" ]; then
echo "Building and running all services..."
mvn clean install
# 假设所有 Spring Boot 模块都在根目录下
for module in $(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout | sed -e 's/<[^>]*>//g' -e 's/\s*//g' | tr ',' '\n'); do
echo "Running $module..."
mvn spring-boot:run -pl $module &
done
else
# 运行指定的单个服务
run_service $SERVICE_NAME
fi

0.0 代码合并流程

  1. 在各自的分支self上进行开发
  2. 切换到develop分支,git pull同步最新代码
  3. 切换到自己的分支selfgit rebase develop对齐代码合并冲突

1.0 第一件事git config

  • git config --list --show-origin查看所有git配置以及所在文件
  • 使用git config --global可以设置git的基本信息(如用户名、邮箱),使用--unset取消设置
    1. 配置你的名称、邮箱以及编辑器
git config --global user.name "191220000-Zhang San" 
# 全局设置名称
git config --global user.email "zhang3@email.com"
git config --global core.editor vim

# instead of
git config --global url.git@github.com:.insteadOf https://github.com/
# alias
git config --global alias.cin "commit --amend --no-edit"

2.0 初始化仓库

  1. 本地仓库:git init创建一个新的 git 仓库,其数据会存放在一个名为 .git 的目录下
    删除仓库:删除 .git 文件夹
git add <文件名字,*表示全部>
# 提交到暂存区,并附上注释
git commit -m 'initial project version'
# 修改最近的一次提交
git commit --ammend

  1. 远程仓库:git clone克隆远端仓库
# 查看remote
git remote -v

# 添加remote
git remote add origin project_repository_url.git
# 设置push分支为自己的仓库
git remote set-url --add --push origin your_repository_url.git

git clone <网址> <仓库存放文件夹名>
# 使用http克隆

配置SSH

不推荐dsa和rsa,推荐ed25519

ssh-keygen -t ed25519 -C "your_email@email.com"

Tag

git tag v0.0.version
git tag -a v0.version -m "Your Comments to the version"
阅读全文 »

Nacos 更全能的注册中心

Nacos 相当于 Eureka + Config 的组合。

Sentinel

相当于 Hystix,用于流量控制。

Tomcat

JRE报错

一般教程会让我们配置JAVA_HOMEJRE_HOME,然后启动Tomcat;
然而,在JDK9以后,就不默认包含JRE了。
此时,我们使用命令

jlink --module-path jmods --add-modules java.desktop --output jre

生成一个JRE后,启动Tomcat,就会报错:

WARNING: Unknown module: java.rmi specified to --add-opens
Exception in thread "main" java.lang.NoClassDefFoundError: java/util/logging/Logger
at org.apache.juli.logging.DirectJDKLog.<init>(DirectJDKLog.java:61)
at org.apache.juli.logging.DirectJDKLog.getInstance(DirectJDKLog.java:181)
at org.apache.juli.logging.LogFactory.getInstance(LogFactory.java:133)
at org.apache.juli.logging.LogFactory.getInstance(LogFactory.java:156)
at org.apache.juli.logging.LogFactory.getLog(LogFactory.java:211)
at org.apache.catalina.startup.Bootstrap.<clinit>(Bootstrap.java:49)
Caused by: java.lang.ClassNotFoundException: java.util.logging.Logger
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
... 6 more

这时候,只需要把jre文件和JRE_HOME环境变量删除,Tomcat就能正常启动

阅读全文 »

微服务

微服务:解决接口越来越多,单体应用运行缓慢问题。

踩坑记录

找不到Mapper

***************************
APPLICATION FAILED TO START
***************************

Description:

Field deviceMapper in com.esagent.es.EsDataInit required a bean of type 'com.example.mapper.YourMapper' that could not be found.

The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.example.mapper.YourMapper' in your configuration.

原因是mybatis版本有问题!

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
<!-- 版本需要和其他依赖对上 -->
</dependency>
</dependencies>
</dependencyManagement>

阅读全文 »

注册中心

Eureka能够自动注册并发现微服务,然后对服务的状态、信息进行集中管理。当我们需要获取其他服务的信息时,只需要向Eureka进行查询。

a: 微服务1
b: 微服务2
c: 微服务3
E: Eureka注册中心

a -> E: 注册
b -> E: 注册
c -> E: 注册

依赖

父项目

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2024.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>

Eureka模块

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>

配置

eureka:
fetch-registry: false
registry-with-eureka: false
service-url:
defaultZone: http://yourhost:port/eureka
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}

注册服务

首先在需要注册的微服务下导入Eureka依赖:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然后修改配置appllication.yml

spring:
application:
name: yourservice
eureka:
client:
# 跟上面一样,需要指向Eureka服务端地址,这样才能进行注册
service-url:
defaultZone: http://yourhost:port/eureka

服务发现

注册RestTemplate

@Configuration
public class BeanConfiguration {

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

使用spring-application-name代替URL

@Override
public UserBorrowDetail getUserBorrowDetailByUid(int uid) {
List<Borrow> borrow = borrowMapper.getBorrowByUid(uid);
RestTemplate restTemplate = new RestTemplate();
User user = restTemplate.getForObject("http://userservice/user/"+uid, User.class);
List<Book> bookList = borrow
.stream()
.map(b -> restTemplate.getForObject("http://bookservice/book/"+b.getBid(), Book.class))
.collect(Collectors.toList());
return new UserBorrowDetail(user, bookList);
}

负载均衡

同一个服务可以注册多个端口,Eureka会为同一服务的多个端口分别进行注册。
使用上面的代码,Eureka会自动地均衡分发请求到不同端口上。

负载均衡保证了服务的安全性,只要不是所有端口的微服务都宕机,Eureka就能够分配请求到可用的端口。

Eureka高可用集群

E: Eureka高可用集群
E: {
E1: Eureka服务器1
E2: Eureka服务器2
E3: Eureka服务器3

E1 -> E2
E2 -> E3
E3 -> E1
}

a: 微服务1
b: 微服务2
c: 微服务3
a -> E: 注册
b -> E: 注册
c -> E: 注册

编写多个application.yml

# application-01.yml
server:
port: 8801
spring:
application:
name: eurekaserver
eureka:
instance:
# 由于不支持多个localhost的Eureka服务器,但是又只有本地测试环境,所以就只能自定义主机名称了
# 主机名称改为eureka01
hostname: eureka01
client:
fetch-registry: false
# 去掉register-with-eureka选项,让Eureka服务器自己注册到其他Eureka服务器,这样才能相互启用
service-url:
# 注意这里填写其他Eureka服务器的地址,不用写自己的
defaultZone: http://eureka01:8802/eureka

# application-02.yml
server:
port: 8802
spring:
application:
name: eurekaserver
eureka:
instance:
hostname: eureka02
client:
fetch-registry: false
service-url:
defaultZone: http://eureka01:8801/eureka

微服务写入所有Eureka服务器的地址

eureka:
client:
service-url:
# 将两个Eureka的地址都加入,这样就算有一个Eureka挂掉,也能完成注册
defaultZone: http://localhost:8801/eureka, http://localhost:8802/eureka

LoadBalance 随机分配

默认的LoadBalance是轮询模式,想修改为随机分配,需要修改LoadBalancerConfig(注意,不需要@Configuration注解)并在BeanConfiguration中启用

public class LoadBalancerConfig {
//将官方提供的 RandomLoadBalancer 注册为Bean
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}

@Configuration
@LoadBalancerClient(value = "userservice",
configuration = LoadBalancerConfig.class)
public class BeanConfiguration {

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

OpenFeign 更方便的HTTP客户端请求工具

OpenFeign和RestTemplate有一样的功能,但是使用起来更加方便

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

使用方法与Mybatis非常类似。

  1. 首先,启用OpenFeign
@SpringBootApplication
@EnableFeignClients
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
}
  1. 接下来注册一个interface
@FeignClient("userservice")   // 声明为userservice服务的HTTP请求客户端
public interface UserClient {

// 路径保证和UserService微服务提供的一致即可
@RequestMapping("/user/{uid}")
User getUserById(@PathVariable("uid") int uid); // 参数和返回值也保持一致
}
  1. 直接注入使用
@Resource
UserClient userClient;

@Override
public UserBorrowDetail getUserBorrowDetailByUid(int uid) {

// RestTemplate方法
RestTemplate template = new RestTemplate();
User user = template.getForObject("http://userservice/user/"+uid, User.class);
// OpenFeign方法,更直观的方法调用
User user = userClient.getUserById(uid);

}

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development"> <!-- 设置环境 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${驱动类(含包名)}"/>
<property name="url" value="${数据库连接URL}"/>
<property name="username" value="${用户名}"/>
<property name="password" value="${密码}"/>
</dataSource>
</environment>
</environments>
</configuration>

Util

一般只需要创建一次,所以创建一个工具类

public class MybatisUtil {

//在类加载时就进行创建
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(new FileInputStream("mybatis-config.xml"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

/**
* 获取一个新的会话
* @param autoCommit 是否开启自动提交(跟JDBC是一样的,如果不自动提交,则会变成事务操作)
* @return SqlSession对象
*/
public static SqlSession getSession(boolean autoCommit){
return sqlSessionFactory.openSession(autoCommit);
}
}
阅读全文 »

SpringBoot Mail 邮箱验证码

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
spring:
mail:
host: your.SMTP.host
username: your_server_email@email.com
password: your_passowrd
@Resource
JavaMailSender sender;

@PostMapping("/verification-email")
public String sendVerificationEmail(@RequestParam String targetEmail,
HttpSession session) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject(EMAIL_TITLE);
int vCode = getVerificationCode();
session.setAttribute("vcode", vCode);
session.setAttribute("uemail", email);

message.setText(EMAIL_CONTEXT + code);
message.setTo(targetEmail);
message.setFrom(EMAIL_SERVEREMAIL); // 与配置文件中的保持一致

sender.send(message);
return "发送成功"; // 前端弹窗可以接受此参数
}

@PostMapping("/register")
public String register(@RequestParam String username,
@RequestParam String email,
@RequestParam String code,
@RequestParam String password,
HttpSession session) {
String sessionCode = session.getAttribute("vcode").toString;
String sessionEmail = session.getAttribute("uemail").toString;

if (sessionCode == null) {
return "验证码为空";
}
if (!sessionCode.equals(code)) {
return "验证码错误!";
}
if (!sessionEmail.equals(email)) {
return "请获取验证码";
}
}
阅读全文 »
0%