优选算法 - 4 ( 链表 哈希表 字符串 9000 字详解 )
一:链表
1.1 链表常用技巧和操作总结
1.2 两数相加
题目链接:两数相加
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {// 先用 cur1 指向 l1 的头节点,用 cur2 指向 l2 的头节点 -- 链表的名字代表着链表头部节点的地址// 因为要记录节点的信息,比如说 val 和 next ,所以 cur1 和 cur2 都用 ListNode 节点类型ListNode cur1 = l1, cur2 = l2;// 接着创建一个虚拟节点 newHead,当两个链表相加后延长这个节点,并把值存储在对应的节点中ListNode newHead = new ListNode(0);// 用 prev 标记一下 newHead 延长后的尾节点ListNode prev = newHead;// 用 t 临时存储两个节点相加的结果int t = 0;// 当 cur1 或 cur2 不为空并且 t 没有剩余值时就一直循环,t 不为空是为了处理两个数相加进位后剩下的数字while(cur1 != null || cur2 != null || t != 0){if(cur1 != null){t += cur1.val;cur1 = cur1.next;}if(cur2 != null){t += cur2.val;cur2 = cur2.next;}// 当 t 累加上两个节点的值后,把 t 的个位放入 newHead 延长的节点中,接着让 prev 继续指向尾节点prev.next = new ListNode(t % 10);prev = prev.next;// 接着让 t 的十位继续加t /= 10;}// 返回结果链表的第一个有效节点,我们要跳过虚拟头节点return newHead.next;}
}
1.3 两两交换链表中的结点
题目链接:两两交换链表中的结点
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode swapPairs(ListNode head) {// 先处理链表为空和链表元素为一的情况,这种情况链表无需交换if(head == null || head.next == null) return head;// 接着处理正常的情况,先创建一个虚拟头节点 newHead ,方便交换,不用处理太多的细节ListNode newHead = new ListNode(0);//接着把 newHead 当作 head 的头节点newHead.next = head;// 初始化四个指针:// `prev` 用于指向当前要交换的节点的前一个节点// `cur` 用于指向当前要交换的第一个节点// `next` 用于指向当前要交换的第二个节点// `nnext` 用于指向当前要交换的第二个节点后一个节点ListNode prev = newHead, cur = prev.next, next = cur.next, nnext = next.next;while(cur != null && next != null){prev.next = next;next.next = cur;cur.next = nnext;//交换完两个数字后更新四个指针,让接下来的节点继续交换prev = cur;cur = nnext;if (cur != null) next = cur.next; // `next` 移到下一对的第二个节点(如果存在)if (next != null) nnext = next.next; // `nnext` 移到下一对的第一个节点的后一个节点(如果存在)}//循环结束后返回 head ,当然,要去掉虚拟节点return newHead.next;}
}
1.4 重排链表
题目链接:重排链表
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public void reorderList(ListNode head) {// 先处理链表为空和链表只有一个节点以及两个节点的情况,这些情况下,链表不需要重排if(head == null || head.next == null || head.next.next == null) return;//接下来就处理正常情况了,首先先找到链表的中间节点,这里用快慢双指针的方法ListNode slow = head, fast = head;while(fast != null && fast.next != null){slow = slow.next;fast = fast.next.next;}//循环过后 slow 就是 head 的中间节点,接着我们把 head 分为两个部分 [0, slow],[slow.next, n]//我们用 head2 存储第二部分的链表,利用头插法对链表逆序ListNode head2 = new ListNode(0);//用 cur 标记第二部分链表的首元素ListNode cur = slow.next;// 接着以 slow 为界将链表前后部分分离slow.next = null; while(cur != null){// 因为我们头插 cur 时,cur 与原来的 cur.next 会断开,所以我们用 next 先记录一下这个节点,方便进行循环ListNode next = cur.next;cur.next = head2.next;head2.next = cur;cur = next;}// 接着我们就可以开始合并 head 和 head2 了// 用 ret 存储合并后的链表,并用 prev 标记 ret 的尾节点,用 cur1 标记 head 的头节点, cur2 标记 head2 的头节点ListNode ret = new ListNode(0);ListNode cur1 = head, cur2 = head2.next, prev = ret;// 因为 head 的长度一定是大于 head2 的,并且我们合并数组时是分别取出一个的,所以我们只需要判断 head 不为空while(cur1 != null){//先把 head 中的元素添加进 ret 中prev.next = cur1;prev = prev.next;cur1 = cur1.next;//因为 head2 的长度小于 head ,所以我们要判断一下 cur2 是否为空if(cur2 != null){prev.next = cur2;prev = prev.next;cur2 = cur2.next;}}// 链表重排完成,无需返回值,因为操作在原链表上完成}
}
1.5 合并 K 个升序链表
题目链接:合并 K 个升序链表
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeKLists(ListNode[] lists) {// 创建一个小根堆(优先队列),根据节点值进行排序// lambda 表达式:(v1, v2) -> v1.val - v2.val 用于实现按节点值升序排列PriorityQueue<ListNode> heap = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);// 接着将所有链表的头结点加入到小根堆中,注意!并不是取的链表所有的节点for(ListNode Li : lists)if(Li != null) heap.offer(Li);// 接着创建一个 ret 存储合并后的升序链表,用 prev 指向 ret 的尾节点ListNode ret = new ListNode(0);ListNode prev = ret;// 接着就把小堆里的元素依次放入 ret 中while(heap.isEmpty() == false){//尾插到 prev 后面,并更新 prev 的指向ListNode t = heap.poll();prev.next = t;prev = t;// 如果堆顶节点 t 有下一个节点,将其加入堆if (t.next != null) {heap.offer(t.next); // 把 t 的下一个节点加入堆中}}// 返回合并后的链表的头节点(去掉虚拟头节点 ret)return ret.next;}
}
1.6 K 个一组翻转链表
题目链接:K 个一组翻转链表
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseKGroup(ListNode head, int k) {// 先获取链表需要反转的次数 n int n = 0;ListNode cur = head; // 指针初始化为链表头节点while(cur != null){cur = cur.next;n++;}n /= k; // 此时 n 就存储了数组要反转的次数了// 接着创建一个虚拟节点 newHead ,用于存储翻转后的链表ListNode newHead = new ListNode(0);// 接着用 prev 标记要插入位置的前一个节点ListNode prev = newHead;cur = head; // 重置一下 cur ,因为前面遍历链表时修改了 cur// 先处理 n 次需要翻转 k 个元素的情况for(int i = 0; i < n; i++){ListNode tmp = cur; // 先用 tmp 临时存储一下第一个插入的节点for(int j = 0; j < k; j++){ListNode next = cur.next; // 先用 next 存储一下插入节点的下一个节点cur.next = prev.next;prev.next = cur;cur = next;}// 当一组结束完后要重新更新一下 prev ,让下一轮循环以 prev 为基准进行头插prev = tmp;}// 接着处理一下剩余的元素prev.next = cur;// 最后返回 newHead ,要跳过虚拟节点return newHead.next;}
}
二:哈希表
2.1 两数之和
题目链接:两数之和
class Solution {public int[] twoSum(int[] nums, int target) {// 键:数组元素的值,值:该元素在数组中的索引Map<Integer, Integer> hash = new HashMap<>(); // <nums[i], i>// 接着开始遍历数组for(int i = 0; i < nums.length; i++){int x = target - nums[i];if(hash.containsKey(x)) return new int[]{i, hash.get(x)};else hash.put(nums[i], i);}// 如果找不到返回 -1 和 -1 return new int[]{-1, -1};}
}
2.2 判断是否互为字符重排
题目链接:判断是否互为字符重排
class Solution {public boolean CheckPermutation(String s1, String s2) {// 先处理特殊情况,当两个字符串的长度不相同时,一定不是重排字符串2if(s1.length() != s2.length()) return false;// 接下来处理正常的情况,我们用大小为 26 的数组模拟哈尼表,用于存储 'a' - 'z' 字符int[] hash = new int[26];// 先遍历第一个字符串 s1 ,遇到一个字符让对应的下标加加for(int i = 0; i < s1.length(); i++){hash[s1.charAt(i) - 'a']++;}// 接着处理第二个字符串 s2 ,当遇到相同的字符时,让对应下标减减for(int i = 0; i < s2.length(); i++){hash[s2.charAt(i) - 'a']--;// 此时如果有某一位出现负数了。那么直接返回 falseif(hash[s2.charAt(i) - 'a'] < 0) return false;}return true;}
}
2.3 存在重复元素
题目链接:存在重复元素
class Solution {public boolean containsDuplicate(int[] nums) {// 此处我们用 set 存储数组中的元素Set<Integer> hash = new HashSet<>();// 接着开始遍历数组for(int x : nums){if(hash.contains(x)) return true;else hash.add(x);}return false;}
}
2.4 存在重复元素 II
题目链接:存在重复元素 II
class Solution {public boolean containsNearbyDuplicate(int[] nums, int k) {// 创建一个哈希表,用于记录数组中的每个元素及其对应的索引// 键:数组中的元素值,值:该元素的最新索引Map<Integer, Integer> hash = new HashMap<>();// 接着开始遍历数组for(int i = 0; i < nums.length; i++){// 先判断一下这个数之前是否存在有相同的元素if(hash.containsKey(nums[i])){if(i - hash.get(nums[i]) <= k) return true;}hash.put(nums[i], i);}return false;}
}
2.5 字母异位词分组
题目链接:字母异位词分组
class Solution {public List<List<String>> groupAnagrams(String[] strs) {// 创建一个哈希表,用于将字母异位词分组// 键:经过排序的字符串(字母异位词的特征值)// 值:与该特征值对应的所有字母异位词的列表Map<String, List<String>> hash = new HashMap<>();// 1. 遍历字符串数组,将字母异位词分组for (String s : strs) {// 将当前字符串转换为字符数组char[] tmp = s.toCharArray();// 对字符数组进行排序,使异位词具有相同的排序结果Arrays.sort(tmp);// 将排序后的字符数组转换为字符串,作为分组的键String key = new String(tmp);// 如果哈希表中不存在该键,初始化一个新的列表作为值if (!hash.containsKey(key)) hash.put(key, new ArrayList<>());// 将当前字符串添加到对应键的列表中hash.get(key).add(s);}// 2. 提取结果// 将哈希表中所有的值(分组列表)转换为一个列表返回return new ArrayList<>(hash.values());}
}
三:字符串
3.1 最长公共前缀
题目链接:最长公共前缀
class Solution {public String longestCommonPrefix(String[] strs) {// 先处理一下特殊的情况,当字符串为 null 以及 strs 中的字符串个数为 0 ,这种情况下我们直接返回空字符串if(strs == null || strs.length == 0) return "";// 如果不是的话就处理正常的情况,我们以 strs 中的第一个字符串为基准,当前缀的长度大于等于 strs 中第一个字符串的长度时,循环停止int index = 0; // 用 index 记录最后第一个字符串的最长公共前缀的索引while(true){if(index >= strs[0].length()) break;char currentChar = strs[0].charAt(index); // 先获取第一个字符串的当前位置的字符// 接着开始遍历第二个及以下的字符串for(int i = 1; i < strs.length; i++){// 同样的,index 的长度也不能超过其他字符串的长度if(index >= strs[i].length() || strs[i].charAt(index) != currentChar) return strs[0].substring(0,index);}// 如果 index 没有超过长度并且字符相同的话,让 index 继续加加,继续比较下一个字符index++;}return strs[0].substring(0, index);}
}
3.2 最长回文子串
题目链接:最长回文子串
class Solution {public String longestPalindrome(String s) {// 用 begin 记录最长回文子串的起始位置,len 记录最长回文子串的长度,n 存储字符串 s 的长度int begin = 0, len = 0, n = s.length();// 接着遍历字符串中的每一个元素for(int i = 0; i < n; i++){// 先处理奇数的情况int left = i, right = i;while(left >= 0 && right < n && s.charAt(left) == s.charAt(right)){left--; right++;}// 扩展结束后看一下要不要更新 begin 和 lenif(right - left - 1 > len){begin = left + 1; // 因为 left 和 right 都越过了回文子串的范围,他们是用完后再加加的len = right - left - 1;} // 接着处理偶数的情况2left = i; right = i + 1;while(left >= 0 && right < n && s.charAt(left) == s.charAt(right)){left--; right++;}if(right - left - 1 > len){begin = left + 1; // 因为 left 和 right 都越过了回文子串的范围,他们是用完后再加加的len = right - left - 1;} }// 循环结束后根据 len 和 begin 来截取字符串return s.substring(begin, begin + len);}
}
3.3 二进制求和
题目链接:二进制求和
class Solution {public String addBinary(String a, String b) {// 因为要频繁的对字符串进行操作,因此我们使用 StringBuffer 来存储结果字符串StringBuffer ret = new StringBuffer();// 接着用 cur1 指向字符串 a 的末尾, cur2 指向字符串 b 的末尾,t 用来累加结果int cur1 = a.length() - 1, cur2 = b.length() - 1, t = 0;// 接着就可以开始从低位相加了while(cur1 >= 0 || cur2 >= 0 || t != 0){// 因为循环的条件是或,因此我们还要再循环内再判断一次,防止 cur1 和 cur2 越界,- '0' 是为了把字符转换为数字if(cur1 >= 0) t += a.charAt(cur1--) - '0';if(cur2 >= 0) t += b.charAt(cur2--) - '0';// 接着取出 t 的当前位,进位保留, + '0' 是把数字转换成字符ret.append((char)('0' + (t % 2)));t /= 2;}// 循环结束后 ret 就存储着求和的值,但注意,这个值的逆序的ret.reverse();// 最后把 ret 转换为 String 返回return ret.toString();}
}
3.4 字符串相乘
题目链接:字符串相乘
class Solution
{public String multiply(String num1, String num2){// 获取两个数字的长度int m = num1.length(), n = num2.length();// 将数字字符串反转并转换为字符数组,方便逐位计算char[] n1 = new StringBuffer(num1).reverse().toString().toCharArray();char[] n2 = new StringBuffer(num2).reverse().toString().toCharArray();// 创建一个临时数组,用于存储无进位的乘积结果int[] tmp = new int[m + n - 1]; // 长度为m+n-1,因为每一位乘积最多扩展到m+n-1位// 1. ⽆进位相乘后累加for(int i = 0; i < m; i++) { // 遍历num1的每一位for(int j = 0; j < n; j++) { // 遍历num2的每一位// 把两位数字的乘积加到对应位置(i+j)tmp[i + j] += (n1[i] - '0') * (n2[j] - '0');}}// 2. 处理进位int cur = 0; // 当前处理的位置int t = 0; // 进位值StringBuffer ret = new StringBuffer(); // 用于存储最终的结果// 遍历tmp数组,将无进位的结果转化为正确的数字while(cur < m + n - 1 || t != 0) // 遍历到tmp的末尾或者有进位需要处理{if(cur < m + n - 1) // 如果当前位还在tmp数组范围内t += tmp[cur++]; // 加上当前位的值,并移动到下一位// 当前位的数字是t % 10,进位是t / 10ret.append((char)(t % 10 + '0')); // 保存当前位的数字(转换为字符后添加到结果中)t /= 10; // 更新进位值}// 3. 去除结果中的多余前导零while(ret.length() > 1 && ret.charAt(ret.length() - 1) == '0') {ret.deleteCharAt((ret.length() - 1)); // 删除结果最后的0,直到结果长度大于1}// 结果需要再反转一次,变为正序返回return ret.reverse().toString();}
}
相关文章:

优选算法 - 4 ( 链表 哈希表 字符串 9000 字详解 )
一:链表 1.1 链表常用技巧和操作总结 1.2 两数相加 题目链接:两数相加 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* …...
CTF-RE 从0到N: windows反调试-获取Process Environment Block(PEB)信息来检测调试
在Windows操作系统中,Process Environment Block (PEB,进程环境块) 是一个包含特定进程信息的数据结构。它可以被用于反调试中 如何获取PEB指针? 在Windows操作系统中,获取PEB指针的常见方法主要有以下几种。: 1. 使…...
STM32开发基础阶段复习
1.使用寄存器方式点亮LED灯的三个步骤是什么? 首先使能RCC_APB2ENR(外设时钟使能寄存器)对应的GPIO端口时钟,即给LED这个外设使能时钟。 配置对应GPIO端口,配置为通用推挽输出,输出速度可以选择最大。 将GPIO端口输…...
搜维尔科技:SenseGlove触觉反馈手套开箱+场景测试
搜维尔科技:SenseGlove触觉反馈手套开箱场景测试 SenseGlove触觉反馈手套开箱场景测试...

在k8s上部署Crunchy Postgres for Kubernetes
目录 一、前言二、安装Crunchy Postgres for Kubernetes三、部署一个简单的postgres集群四、增加pgbouncer五、数据备份六、备份恢复七、postgres配置参数七、数据导入 一、前言 Crunchy Postgres可以帮助我们在k8s上快速部署一个高可用、具有自动备份和恢复功能的postgres集群…...
大模型(LLMs)进阶篇
大模型(LLMs)进阶篇 一、什么是生成式大模型? 生成式大模型(一般简称大模型LLMs)是指能用于创作新内容,例如文本、图片、音频以及视频的一类深度学习模型。相比普通深度学习模型,主要有两点不…...

近几年新笔记本重装系统方法及一些注意事项
新笔记本怎么重装系统? 近几年的新笔记本默认开启了raid on模式或vmd选项,安装过程中会遇到问题,新笔记本电脑重装自带的系统建议采用u盘方式安装,默认新笔记本有bitlocker加密机制,如果采用一键重装系统或硬盘方式安装…...

小程序19-微信小程序的样式和组件介绍
在小程序中不能使用 HTML 标签,也就没有 DOM 和 BOM,CSS 也仅支持部分选择器 小程序提供了 WXML 进行页面结构的编写,WXSS 进行页面的样式编写 WXML 提供了 view、text、image、navigator等标签构建页面结构,小程序中标签称为组件…...

Chrome 浏览器开启打印模式
打开开发者工具ctrl shift p输入print 找到 Emulate CSS print media type...

Git回到某个分支的某次提交
1.切换到需要操作的分支(<branch-name>是分支名称)。 命令如下: git checkout <branch-name> 2.获取代码的提交记录 。命令如下: git log 按q退出当前命令对话。 获取到某次提交或者合并的hash值(下文…...

[前端面试]javascript
js数据类型 简单数据类型 null undefined string number boolean bigint 任意精度的大整数 symbol 创建唯一且不变的值,常用来表示对象属性的唯一标识 复杂数据类型 object,数组,函数,正则,日期等 区别 存储区别 简单数据类型因为其大小固定…...

对象的初步认识
#对象可组织数据(如统计数据的表格) 下以表格为例 1.设计一个表格:(None为初始值设定,表示无) class a; ##1None ##2None 2.创建一个表格 变量a 3.对对象的属性进行赋值 变量.##1"##" 变量.##2"##" 4.查询对象中…...

layui 输入框带清空图标、分词搜索、关键词高亮
https://andi.cn/page/621858.html...
Vue 3 + TypeScript: 类型安全的前端开发实践
引言 在现代前端开发中,TypeScript 已经成为提升代码质量和开发效率的重要工具。将 Vue 3 与 TypeScript 结合使用,能够为我们的项目带来更好的类型安全性和开发体验。 1. 项目配置 1.1 创建项目 使用 Vue CLI 创建支持 TypeScript 的 Vue 3 项目&am…...

Python爬虫知识体系-----requests-----持续更新
数据科学、数据分析、人工智能必备知识汇总-----Python爬虫-----持续更新:https://blog.csdn.net/grd_java/article/details/140574349 文章目录 一、安装和基本使用1. 安装2. 基本使用3. response常用属性 二、get请求三、post请求四、代理 一、安装和基本使用 1.…...
Swift的可选绑定(Optional binding)
在Swift中,有一种变量称为可选变量(Optional),具体说明见Swift初步入门。这种变量的值可以存在也可以为空(nil)。在Swift中,可以通过将if语句和赋值语句结合,有条件地展开࿰…...

硬石电机学习2024116
F4 概况 共模抑制线圈作用是滤波 LD3.3是将5v转为芯片用的3.3V CH340用于板子和电脑通讯 光耦隔离保护主控 16M的外部flash 1M的芯片内部的flash 10kHZ高速的光耦隔离,1M的低俗光耦隔离 F4 stm32概况 stm8和51都是一次可以运算处理8位的 32表示一次处理32位…...
行业类别-金融科技-子类别区块链技术-细分类别智能合约-应用场景供应链金融课题
1.大纲分析 针对题目“行业类别-金融科技-子类别区块链技术-细分类别智能合约-应用场景供应链金融课题”的大纲分析,以下是一个详细的结构: 一、引言 简述金融科技的发展背景与重要性引出区块链技术在金融科技中的应用强调智能合约作为区块链技术的重要细分类别提出供应链金…...
ElementPlus el-upload上传组件on-change只触发一次
ElementPlus el-upload上传组件on-change只触发一次 主要运用了:on-exceed方法 废话不多说,直接上代码 <el-uploadclass"avatar-uploader"action"":on-change"getFilesj":limit"1":auto-upload"false"accep…...

论文阅读:Uni-ISP Unifying the Learning of ISPs from Multiple Cameras
这是 ECCV 2024 的一篇文章,文章作者想建立一个统一的 ISP 模型,以实现在不同手机之间的自由切换。文章作者是香港中文大学的 xue tianfan 和 Gu jinwei 老师。 Abstract 现代端到端图像信号处理器(ISPs)能够学习从 RAW/XYZ 数据…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...

对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...
shell脚本质数判断
shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数)shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数) 思路: 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...