学习时间:2022-9-16
学习内容
1、leetcode
格雷编码
自己写的回溯,但是遇到了问题,不知道如何判断是否相差一位,另外,并不需要求所有解,所以其实没必要用回溯
class Solution {
/*
N = 3;
000 0
001 1
011 3
010 2
110 6
111 7
101 5
100 4
*/
public List<Integer> grayCode(int n) {
List<Integer> res = new ArrayList<>();
res.add(0);
if(n == 0) return res;
int c = 1;
while(c <= n)
{
int index = res.size()-1;
while(index >= 0)
res.add(res.get(index--) + (int)Math.pow(2,c-1));
c++;
}
return res;
}
}
LRU缓存机制
对应Java中的LinkedHashMap结构
主要实现方法:由一个双向链表和一个HashMap
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
我的解:单哨兵 正常解法应该设置两个哨兵,这样最节约时间,不设置哨兵将非常难做
class LRUCache {
//双向链表+hashMap
int capacity = 0;
Node head;//头指向最新进入
Node tail;//尾指向最久未调用
HashMap<Integer,Node> map;
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new HashMap<Integer,Node>();
this.head = new Node(0,-1);//哨兵 key=-1
this.tail = this.head;
}
public int get(int key) {
//如果存在,则返回,并将key换到头部
if(!this.map.containsKey(key)){
return -1;
}
Node node = this.map.get(key);
if(head.next == node){
//已经在最前面了 修改值即可返回
return node.value;
}
if(node==tail){
//尾部
tail = node.pre;//不可能会导致tail变到head上
}
node.pre.next = node.next;
if(node.next != null){
node.next.pre = node.pre;
}
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
return node.value;
}
public void put(int key, int value) {
//判断是否存在该元素
if(this.map.containsKey(key)){
//存在,则移动到队伍头部
Node node = this.map.get(key);
node.value = value;
if(head.next == node){
//已经在最前面了 修改值即可返回
this.map.put(key,node);
return;
}
if(node==tail){
//尾部
tail = node.pre;//不可能会导致tail变到head上
}
node.pre.next = node.next;
if(node.next != null){
node.next.pre = node.pre;
}
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
this.map.put(key,node);
}else{
//判断当前是否已满
if(this.capacity == this.map.size()){
//去除一个尾部元素
Node dropNode = tail;
tail = tail.pre;
tail.next = null;
map.remove(dropNode.key);
}
//新加入元素移动到头部
Node newNode = new Node(value,key);
if(head.next != null){
head.next.pre = newNode;
}
newNode.next = head.next;
head.next = newNode;
newNode.pre = head;
map.put(key,newNode);
if(tail == head){
tail = newNode;//没有元素时,需要tail后移
}
}
}
}
class Node {
public Node next;
public Node pre;
public int value;
public int key;
Node(int value,int key){
this.value = value;
this.key = key;
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
有效的括号
栈 或者双指针
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
for(int i = 0;i<s.length();i++){
if(stack.size() == 0){
stack.push(s.charAt(i));
}else{
char left = stack.peek();
if(s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{'){
//为左边
stack.push(s.charAt(i));
continue;
}
if(isEqual(left,s.charAt(i))){
//可以匹配,则弹出一个
stack.pop();
}else{
return false;
}
}
}
if(stack.size() == 0){
return true;
}
return false;
}
public boolean isEqual(char left,char right){
//是否可以配对
if(left == '(' && right ==')') return true;
if(left == '[' && right ==']') return true;
if(left == '{' && right =='}') return true;
return false;
}
}
41. 缺失的第一个正数
原地哈希,将数组视为hash表,然后就可以做这个题了
同类题:
442. 数组中重复的数据
448. 找到所有数组中消失的数字
原地hash,第一次遍历的时候改变数组为hash,即将元素放置在value-1的位置,如1,则放在index=0,index=0的值和1所在的值交换,通过这一轮循环以后,就可以全部放在他合适的位置上了,若发现找不到合适下标的元素,则说明该地方缺失,返回index+1,若都不缺失,则返回length+1(此题中负数直接pass)
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; ++i) {
if (nums[i] <= 0) {
nums[i] = n + 1;
}
}
for (int i = 0; i < n; ++i) {
int num = Math.abs(nums[i]);
if (num <= n) {
nums[num - 1] = -Math.abs(nums[num - 1]);
}
}
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
return n + 1;
}
}
2、Java中的线程安全是什么意思
java中的线程安全是什么: 就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问 ,也就是加上了锁(类似写锁)
3、内存工作 分段
先说内存的工作,由于代码可能写入的相对地址,但实际CPU调用时不可能调用相对地址,是需要调用实际地址的,这就出现了一个问题,我们需要让空闲的空间跑代码,假设有两个进程中都用到了相对地址[100],但他们不能都在实际地址为100的地方,因为没有办法确定那个位置是否是空闲的,所以需要重定位,重定位需要区分,在什么时候进行,如编译时,载入时,运行时,但编译时会导致程序只能放在一个空闲固定的位置上,载入时重定位将导致载入后程序无法移动,所以采用运行时重定向的方法。运行时重定向实现需要在内存中采用基址+偏移的方式,将基址存入PCB中,这样就可以保证在运行时也可以移动内存。
分段是在此基础上进行的,因为分段更加符合人们的思考方式:每一种代码段都是从他自己的0开始,每一个进程中都有多个代码段,每个代码段对应的是一个地址,分段结构为<段号:段内偏移>,每一个进程会有一个LDT图,如下所示:
这张图会记录每一个段的基址、长度、段号等信息,而每一个进程其实在操作系统中也有一个段号,操作系统的进程段表被称为GDT,因此,分段的内存实现方式就是采用GDT+LDT的方式实现的
4、文件系统实现 目录实现
文件系统实现
文件控制块(FCB)
包含所有者、权限、文件内容等
内存中的文件系统结构
虚拟文件系统
操作系统支持多种类型的文件系统,所以需要将多个类型的文件系统集成到目录结构中,实现方法如图所示:
使用一个VFS(Virtual File System)接口,使通用操作和实现分开,通过这个办法,可以在不知道它处理的是什么对象的情况下对这些对象进行操作
目录实现
有两种方式可以实现:
1、线性表 查找慢,可以加缓存优化
2、哈希表 查找快(O(n)),但是对大小有要求(冲突、扩容)