剑指Offer17
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路:将这个矩阵从外层进行遍历,先算出需要遍历几层,然后再根据二维数组的性质,遍历每层的上、右、下、左四层的元素。
public static ArrayList<Integer> solution(int[][] arr){
if (arr==null){
return null;
}
ArrayList<Integer> list = new ArrayList<>();
int a=arr.length;//二位数组中有几列(矩阵的行数)
int b =arr[0].length;//每列中有几个元素(矩阵的列数)
//打印几圈
int time = Math.min(a,b)%2==0?Math.min(a,b)/2:Math.min(a,b)/2+1;
int c1 =0 ;
int c2 = b-1;
int r1 = 0;
int r2 = a-1;
for (int i =0 ;i<time;i++){
for (int c = c1 ; c<=c2 ; c++){
list.add(arr[r1][c]);
}
for (int r = r1+1;r<=r2;r++){
list.add(arr[r][c2]);
}
if (r1<r2&&c1<c2){
for (int c = c2-1;c>c1;c--){
list.add(arr[r2][c]);
}
for (int r = r2;r>r1;r--){
list.add(arr[r][c1]);
}
}
r1++;
r2--;
c1++;
c2--;
}
return list;
}
剑指Offer18
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
思路:定义两个栈,一个为放入元素的栈,另一个辅助栈记录放入元素最小值,当第一个栈需要放入的元素为4、3、7、1,放入第一个元素为4时,辅助栈放入元素4,当栈中放入3时,将辅助栈中的顶层元素与将要放入栈中元素进行比较,如果大于3则将3也同时放入辅助站中,如果小于3,则将辅助栈的顶层元素再次放入辅助栈中,所以栈中最小元素就是辅助栈中的顶层元素。
public class Offer18 {
Stack<Integer> stack = new Stack<>();
Stack<Integer> minStack = new Stack<>();
public void push(int node) {
if (stack.isEmpty()){
stack.push(node);
minStack.push(node);
return;
}
Integer peek = minStack.peek();
if (node>peek){
minStack.push(peek);
}else{
minStack.push(node);
}
stack.push(node);
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int min() {
return minStack.peek();
}
}
剑指Offer 19
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路:因为栈的弹出只能从栈顶弹出,所以我们要将弹出序列的第一个元素栈顶元素对比,如果相等,就将谈定元素弹出,用弹出序列的第二个元素与栈顶元素比较,如果不相等就继续压栈,直到所有的元素都压入栈顶完毕,左后循环完,如果栈中的元素为空,则返回true
public class Offer21 {
public static boolean solution1(int[] pushA , int[] popA ){
if (pushA==null||popA==null||pushA.length==0||popA.length==0){
return false;
}
Stack<Integer> stack = new Stack<>();
int j = 0;
for (int i = 0 ;i<pushA.length;i++){
stack.push(pushA[i]);
while (j<popA.length&&stack.peek()==popA[j]){
stack.pop();
j++;
}
}
return stack.isEmpty()?true:false;
}
}
JWT
JWT简称jason web token,就是通过jason的格式作为web中的令牌,用域安全的将信息作为jason格式进行传输,再传输的过程还可以进行数据的加密,签名等操作。
传统seesion认证的缺点
1.在传统的信息传递中,我们都是通过session和cookie实现的,但是当服务段发送客服端cookie时,可能会收到拦截,有安全问题,
2.如果用户过多,每个用户都有一个session存储在服务器上,会造成服务器的压力。
3.用户端认证之后,服务端会做记录,如果下次用户请求其他的服务器,就不能得到这个session。
jwt的优点
简洁:可以使用URl、post传递参数,传递的参数小,很轻便,
自包含:jwt中包含了用户所需的信息,避免了多次查询数据库,
jwt是以jason的格式传递参数的,所以在所有的web中都可以传递。
jwt的结构
由三部分组成:header、payload、signature
中间用点进行分隔,进行base64编码。
header:包含两部分令牌的类型(即JWT)和签名的算法。
payload:包含声明,声明是用map结构,在这里可以加入用户的一些信息,但是不建议加入一些私密的信息。
sigature:JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下。
jwt的使用
加入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
@Test
public void test(){
Calendar instance = Calendar.getInstance();
//设置jwt有效时间
instance.add(Calendar.SECOND,300);
//创建jwt
String token = JWT.create()
//加入payload
.withClaim("username","zhangsan")
.withExpiresAt(instance.getTime())
//加入签名并设置使用的算法
.sign(Algorithm.HMAC256("@#@#EWEWEQ"));
System.out.println(token);
}
@Test
public void test1(){
//验证jwt
JWTVerifier build = JWT.require(Algorithm.HMAC256("@#@#EWEWEQ")).build();
DecodedJWT verify = build.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTcwNjczMjQsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.vky4fZ5hSzHv6DChQpvFkaLo16Kvh-Qex5kJENDZosk");
//获取payload中的信息
System.out.println(verify.getClaim("username").asString());
//获取过期时间
System.out.println("过期时间"+verify.getExpiresAt());
}
JWT在springboot中的使用
@RestController
@Slf4j
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("login")
public Map<String ,Object > login(User user){
Map<String, Object> map = new HashMap<>();
Map<String ,String> token = new HashMap<>();
log.info("username",user.getUsername());
try{
User userDb = userService.login(user);
token.put("id",userDb.getId());
token.put("username",userDb.getUsername());
String token1 = JWTUtil.createToken(token);
map.put("status",true);
map.put("sessage","认证成功");
map.put("token",token1);
}catch (Exception e){
map.put("status",false);
map.put("message",e.getMessage());
e.printStackTrace();
}
return map;
}
@RequestMapping("test")
public Map<String ,Object> test(String token){
Map<String, Object> map = new HashMap<>();
try{
JWTUtil.verify(token);
map.put("status",true);
map.put("message","认证成功");
}catch (Exception e){
map.put("message","认证失败");
map.put("status",false);
}
return map;
}
}
前端存放token
token可以存放在LocalStorage和SessionStorage中
LocalStorage除非主动删除,否则会永久存储在浏览器中。
SessionStorage只在当前所在窗口关闭前有效,窗口关闭后其存储数据也就会被自动清除。
.then(res =>{
if(res.data['code']==200){
localStorage.clear()
localStorage.setItem('info',1)
localStorage['flag']=1
// localStorage.setItem('flag',1)
sessionStorage.clear()
// sessionStorage['userid']=JSON.stringify(res.data.userInfo.id)
sessionStorage.setItem('userid',JSON.stringify(res.data.userInfo.id))
sessionStorage['token']=JSON.stringify(res.data.token)
this.$message({
message:'登录成功',
type:'success'
})
this.$router.push('/home')
}else{
this.$message({
message:'用户名或者密码错误',
type:'warning'
})
}
})