记录:2022-9-24 连接两字母单词得到的最长回文串 翻转二叉树 平衡二叉树 IO 显示器输入输出 BIO

学习时间:2022-9-24

学习内容

1.Leetcode

2131.连接两字母单词得到的最长回文串

在这里插入图片描述

思路

要抓住这个题的特点,只有两个,所以可以用一些非暴力解法来做.另外,这个题不能用回溯,复杂度太高了,即便是O(n)也是105的级别
我们可以成对来排列,放入哈希表中,如果出现一对,则加入计算,然后如果有落单的自回文串,则在最后结果+2即可

代码
class Solution {
    public int longestPalindrome(String[] words) {
        int ans = 0;
        boolean canbAdd = false;
        HashMap<String,Integer> map = new HashMap<String,Integer>();
        for(int i = 0;i<words.length;i++){
            if(map.containsKey(words[i])){
                int val = map.get(words[i])+1;
                map.put(words[i],val);
            }else{
                map.put(words[i],1);
            }
        }
        for(String word : map.keySet()){
            if(word.charAt(0) == word.charAt(1)){
                int count = map.get(word);
                int times = count/2;
                int newVal = count%2;
                ans+=times*4;
                map.put(word,newVal);
                if(newVal == 1){
                    canbAdd = true;
                }
            }else{
                String reverse = reverseStr(word);
                if(!map.containsKey(reverse)){
                    continue;
                }
                int reverseCount = map.get(reverse);
                int count = map.get(word);
                int min = count;
                if(count > reverseCount){
                    min = reverseCount;
                }
                ans+=min*4;
                reverseCount-=min;
                count-=min;
                map.put(reverse,reverseCount);
                map.put(word,count);
            }
        }
        if(canbAdd){
            ans+=2;
        }
        return ans;
    }
    public String reverseStr(String s){
        String temp = "";
        temp+=s.charAt(1);
        temp+=s.charAt(0);
        return temp;
    }
}

在这里插入图片描述

更优解

上述解法明显在某些地方没有优化,大体思路应该差不多

class Solution {
    public int longestPalindrome(String[] words) {
        Map<String,Integer> map=new HashMap<>();
        for(int i=0;i<words.length;i++){map.put(words[i],map.getOrDefault(words[i],0)+1);}
        int add=0;
        int ans=0;
        for(String s:map.keySet()){
            if(s.charAt(0)==s.charAt(1)){
                ans+=((map.get(s)>>1)<<2);
                if(((map.get(s)&1)==1)){add=2;}
            }
            else{
                String t=pal(s);
                if(map.containsKey(t)){ans+=Math.min(map.get(s),map.get(t))*2;}
            }
        }
        return ans+add;
    }
    public String pal(String s){
        return new StringBuilder(s).reverse().toString();
    }
}

226. 翻转二叉树

在这里插入图片描述

思路

递归,一直将左右子树替换即可

代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        order(root);
        return root;
    }
    public void order(TreeNode node){
        if(node == null) return;
        swap(node);
        order(node.left);
        order(node.right);
    }
    public void swap(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

在这里插入图片描述

更优解

只写了一个函数,代码比较短

class Solution {
	public TreeNode invertTree(TreeNode root) {
		//递归函数的终止条件,节点为空时返回
		if(root==null) {
			return null;
		}
		//下面三句是将当前节点的左右子树交换
		TreeNode tmp = root.right;
		root.right = root.left;
		root.left = tmp;
		//递归交换当前节点的 左子树
		invertTree(root.left);
		//递归交换当前节点的 右子树
		invertTree(root.right);
		//函数返回时就表示当前这个节点,以及它的左右子树
		//都已经交换完了
		return root;
	}
}

作者:wang_ni_ma
链接:https://leetcode.cn/problems/invert-binary-tree/solution/dong-hua-yan-shi-liang-chong-shi-xian-226-fan-zhua/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

110. 平衡二叉树

在这里插入图片描述

思路

算每个节点的最大高度差,如果大于1,则return false
我这里递归了两次,所以慢了 思路差不多,但我应该是多算了一些重复值,导致复杂度有点高

代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        return isB(root);
    }
    public boolean isB(TreeNode root){
        if(root == null){
            return true;
        }
        int val = Math.abs(getMaxLen(root.left)-getMaxLen(root.right));
        if(val>1) return false;
        // return true;
        return isB(root.left)&&isB(root.right);
        
    }
    //得到这个节点的最大长度
    public int getMaxLen(TreeNode root){
        if(root == null){
            return 0;
        }
        return Math.max(getMaxLen(root.left),getMaxLen(root.right)) + 1;
    }
}

在这里插入图片描述

更优解

经典的算法…

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        } else {
            return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
        }
    }

    public int height(TreeNode root) {
        if (root == null) {
            return 0;
        } else {
            return Math.max(height(root.left), height(root.right)) + 1;
        }
    }
}

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/balanced-binary-tree/solution/ping-heng-er-cha-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.IO 显示器输入输出

IO如何让外设工作,其原理是依次像设备的寄存器写内容,然后操作硬件,这里以printf说起,看如何让他输出到屏幕的:
在这里插入图片描述

当执行:

printf(“Host Name:%s”,name);

时,
首先,将会打开一个文件指向指针,如下:

int fd = open("/dev/xxx");
for(int i = 0;i<10;i++){
	write(fd,i,sizeof(int));
}
close(fd);

这里的写入,会创建缓存buf将格式化输出都写到那里,然后调用write(1,buf…)

int sys_write(unsigned int fd,char *buf,int count){
	struct file* file;
	file = current->filp[fd];
	inode = file->f_inode;
}

这个代码将读取current线程中PCB的filp数组,读取fd(这里是1号位)指向的那个file文件,得到他的inode信息
其中,filp和filp[1]是从fork的位置来的,子进程的是从父进程复制得到,父进程是init的时候得到的
子进程代码如下

int copy_process(){
	*p = *current;
	for(i=0;i<NR_OPEN;i++){
		if((f=p->file[i])) f->f_count++;
	}
}

父进程init:

void init(void){
	open("dev/tty0",O_RDWR,0);
	dup(0);
	dup(0);
	execve("/bin/sh",argv,envp);
}

两个dup(0)的意思就是拷贝两份,tty0就是显示器

在write之前,还有一个open,这里也有open的代码

int sys_open(const char* filename,int flag){
	f=open_namei(filename,flag,&inode);
	current->filp[d] = f;
	f->f_mode = inode->i_mode;
	f->f_inode = inode;
	f->f_count=1;
	return fd;

链接如下图:

在这里插入图片描述

继续回到sys_write,现在之后需要向屏幕输出了
通过上述代码,文件信息 inode 从何而来以及如何打开文件 flip 就已经比较清楚了,下面回到 sys_write 看看如何外设分支的选择以及 sys_write 向下如何引出 out 指令的

计算机的设备分为 字符设备(char device)和块设备(block device),首先分支确定是否哪个大类的设备。

块设备将信息存储在固定大小的块中,每个块都有自己的地址。数据块的大小通常在512字节到32768字节之间。块设备的基本特征是每个块都能独立于其它块而读写。磁盘是最常见的块设备。
另一种基本的设备类型是字符设备。字符设备按照字符流的方式被有序访问,像串口和键盘就都属于字符设备。如果一个硬件设备是以字符流的方式被访问的话,那就应该将它归于字符设备;反过来,如果一个设备是随机(无序的)访问的,那么它就属于块设备。

先从文件中读取信息 file->f_inode,然后再判定inode是不是字符设备 if(S_ISCHR(inode->i_mode))

分好了大类,这里拿到的 tty 是 显示器,属于 字符设备;下面要选择是字符设备中的第几个设备:
字符设备向下执行 rw_char(),读写字符设备。根据参数可知,这里的操作是 WRITE 写。
选择是字符设备中的第几个设备(设备号): inode->i_zone[0]
rw_char()向字符设备输入信息;

int sys_write(unsigned int fd,char *buf,int count){
	struct file* file;
	file = current->filp[fd];
	inode = file->f_inode;
	//继续的代码
	if(S_ISCHR(inode->i_mode)){
	return rw_char(WRITE,inode->i_zone[0],buf,cnt);}
	//....
}

这里判断得到是rw_char,所以转到此函数执行,这里将向设备输入信息

int rw_char(int rw,int dev,char *buf,int cnt){
	crw_ptr call_addr = crw_table[MAJOR(dev)];
	call_addr(rw,dev,buf,cnt);
	//...
}

在这里插入图片描述
这里找到了rw_ttyx,执行tty_write
下面是ttyx_write的代码

int tty_write(unsigined channel,char *buf,int nr){
	struct tty_struct *tty;
	tty=channel+tty_table;
	sleep_if_full(&tty->write_q);//若满则放入队列(同步问题)
	char c,*b = buf;
	while(nr>0&&!FULL(tty->write_q){
		c = get_fs_btye(b);
		if(c=='\r'){PUTCH(13,tty->write_q);continue;}
		if(O_LCUC(tty)) c = toupper(c);
		b++;nr--;
		PUTCH(c,tty->write_q);
	}
	tty->write(tty);
}

注:为了弥补CPU计算与显示器写时两种速度的不平衡,会将数据先写在缓冲区,再从缓冲区向显示器写,这也是缓冲区的意义

tty 之所以 能够指向 write_q 缓冲队列,以及这里的 write 函数,是因为定义它是一个指向 tty_struct 结构体的指针。
在 tty_struct 中查到 con_write,使用 con_write 向显示器写,这也是上面提到的那个 消费者函数。
在 con_write() 中使用 GETCH(tty->write_q, c) 从缓冲队列中取出字符 c,使用内嵌汇编编写将字符写在显示器上的指令,即写出out指令。
while() 每循环一次在显示器上输出一个字符,直至while() 结束为止。
在这里插入图片描述
在这里插入图片描述
整体流程如下:
在这里插入图片描述

3.Java BIO

JAVA中的网络IO模型,这三种都是为了做套接字编程,他们有不同的架构
学了BIO,NIO和AIO简单的看了下
BIO主要是一种1:1的架构,一个server一个client,当存在多个client时,可以使用多个线程实现链接
下图是BIO多线程结构图

在这里插入图片描述
单服务器-单客户端 + 多发

服务器:

public class BIOServer {
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(8099);
            System.out.println("server is running at 8099");
            Socket client = server.accept();
            InputStream inputStream = client.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            String temp = "";
            while((temp =br.readLine()) != null){
                System.out.println(temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端:

public class BIOClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1",8099);
            OutputStream outputStream = socket.getOutputStream();
            PrintStream ps = new PrintStream(outputStream);
            Scanner sc = new Scanner(System.in);
            while(true){
                String msg = sc.nextLine();
                ps.println(msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

单服务器-多线程-多客户端 + 多发

服务器:

public class BIOServer {
    public static void main(String[] args) {
        try {
            ServerSocket server = new ServerSocket(8099);
            System.out.println("server is running at 8099");
            while(true){
                Socket client = server.accept();

                new BIOServerThread(client).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务器线程:

public class BIOServerThread extends Thread {
    Socket socket;
    public BIOServerThread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        //拿到这个socket并不断监听
        try {
            InputStream inputStream = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            String msg = "";
            while((msg = br.readLine()) != null){
                System.out.println(msg);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

客户端:
不变

public class BIOClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1",8099);
            OutputStream outputStream = socket.getOutputStream();
            PrintStream ps = new PrintStream(outputStream);
            Scanner sc = new Scanner(System.in);
            while(true){
                String msg = sc.nextLine();
                ps.println(msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值