学习时间: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();
}
}
}