1、一个有环的链表,如何确认链表有环,环的长度。
LeetCode 142。原题为判断链表是否有环,如果有环找到环的起点。本题修改为求环的长度,基本思路一致,依然为双指针。当快慢指针相遇之后,如果寻找环的起点,则将一个指针从起点开始与另一个指针从相遇点开始以相同速度移动,相遇点即链表起点。如果求环的长度,则将一个指针不动,另一个指针移动到静止指针的位置,移动距离即环的长度。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) int {
slow, fast := head, head
for {
if fast == nil || fast.Next == nil {
return -1
}
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
break
}
}
slow = slow.Next
length := 1
for slow != fast {
slow = slow.Next
length++
}
return length
}
2、设计一个HashMap,你要用什么数据结构? 能用数组能做为存储HashMap的数据结构吗?不能的话,要怎么做?怎么解决hash冲突 你的设计里怎样根据key查找对应的值
-
使用数组+链表或者数组+红黑树等结构
-
可以使用数组作为存储的数据结构
-
可以使用链表、跳表和红黑树等数据结构替代
-
可以使用链地址法,开放地址法(线性探测、二次探测、双重哈希等)
3、MySQL事务的隔离级别有几种,分别阐述。在串行化隔离级别中,怎么加读锁和写锁,这里的锁是什么锁:表锁?行锁?还是其他锁?
1. 四种事务的隔离级别
- 读未提交
- 在“读未提交”隔离级别下,一个事务可以读取其他事务尚未提交的修改数据。这种隔离级别可能导致以下问题:脏读,不可重复读,幻读。
- 读已提交
- 在“读已提交”隔离级别下,一个事务只能读取其他事务已经提交的数据。这种隔离级别消除了“脏读”(Dirty Read)问题,但仍然可能遇到“不可重复读”(Non-repeatable Read)和“幻读”(Phantom Read)问题。
- 可重复读
- 在“可重复读”隔离级别下,一个事务在读取某行数据时,会对该行数据加锁,从而确保在整个事务期间,该行数据不会被其他事务修改。这样可以避免“不可重复读”问题,即确保在同一事务内对同一行数据的多次读取结果是一致的。但依然无法解决幻读问题。
- 可串行化
- 在“可串行化”隔离级别下,数据库系统确保并发事务的执行结果与某种顺序执行这些事务的结果相同。这意味着,从并发事务执行的角度来看,它们是按顺序逐个执行的,即使它们实际上是并发执行的。
4、假设MySQL联合索引为<a,b,c> select * from table_name where c> 10 and a = 10 and b < 10 limit 2000,10 该语句有什么问题,如何优化
联合索引在MySQL中是按照顺序使用的,也就是说,如果你有一个联合索引 (a, b, c),查询必须首先用到 a,然后用到 b,最后用到 c,才能完全利用索引。
优化:
SELECT * FROM table_name WHERE a = 10 AND b < 10 AND c > 10 LIMIT 2000, 10;
SELECT * FROM table_name
WHERE a = 10 AND b < 10
ORDER BY a, b
LIMIT 2000, 10;
5、如何查看cpu占用 top命令中idle字段的解释? 如何查看端口占用? 如何查看文件操作权限? ls -al 中 每行前10个字符的意思 如何更改文件权限
1. top
命令
us (user):用户空间进程的CPU时间百分比,不包括nice值调整的进程。
sy (system):内核空间进程的CPU时间百分比。
ni (nice):用户空间内改变过优先级的进程的CPU时间百分比。
id (idle):CPU空闲时间百分比。此值越高,表示系统空闲时间越多,CPU负载越低。
wa (iowait):CPU等待I/O操作完成的时间百分比。
hi (hardware interrupt):硬件中断时间百分比。
si (software interrupt):软件中断时间百分比。
st (steal time):虚拟机管理程序从虚拟机窃取的CPU时间百分比。
在我的虚拟机中,99.9 id表示当下CPU99.9%的时间是空闲的(因为我刚开机)。
2. 查看端口占用
- 使用
netstat
或者ss
命令
-t
:显示TCP端口
-u
:显示UDP端口
-l
:显示监听状态的套接字
-n
:显示数字形式的地址和端口
fuser
命令可以显示哪些进程正在使用指定的文件、目录或套接字。
比如查看Redis
的端口
3. 文件操作权限
第一个字符表示文件类型:
-
:普通文件d
:目录l
:符号链接c
:字符设备文件b
:块设备文件s
:套接字p
:命名管道
接下来的九个字符分为三组,每组三个字符,表示用户(user)、组(group)和其他人(others)的权限:
-
第一组(
rw-
)表示用户权限(文件所有者):
r
:读取权限w
:写入权限x
:执行权限(进入目录,即cd
命令)
-
第二组(
r--
)表示组权限(与文件所有者同组的用户):
r
:读取权限w
:写入权限x
:执行权限
-
第三组(
r--
)表示其他用户权限(其他所有用户):
r
:读取权限w
:写入权限x
:执行权限
使用
chmod
命令可以修改文件和目录的权限。chmod u+rwx filename # 赋予文件所有者读取、写入和执行权限 chmod u=rx,g=rx,o=r filename # 赋予文件所有者和组读取和执行权限,其他人只读权限 chmod 755 filename # 7:所有者(rwx)5:组(r-x)5:其他人(r-x) # 读(r)权限:值为4 # 写(w)权限:值为2 # 执行(x)权限:值为1
6、介绍一下协程,协程和线程的关系。一个main函数内用go 开启多个协程,现在一个协程panic了,main函数会怎样?为什么?
1. 协程与线程
协程,简单概括就是用户态的轻量级的线程。相比于操作系统级的线程,协程占用更少的资源(如内存和CPU时间),启动和销毁的开销也更小。
特性 | 协程(Goroutine) | 线程(Thread) |
---|---|---|
创建开销 | 较低 | 较高 |
切换开销 | 较低(用户态切换) | 较高(内核态切换) |
内存占用 | 较小(初始栈大小约2KB) | 较大(初始栈大小约1MB) |
调度 | 由Go运行时调度器管理 | 由操作系统内核调度 |
通信方式 | 使用channel,支持安全的协程间通信 | 需要显式使用锁机制,避免竞争条件 |
并发模型 | M(多个协程映射到少量线程上运行) | 1:1(一个线程映射到一个操作系统线程) |
2. 协程panic
单个协程panic的行为:当一个协程发生panic时,如果这个panic没有被捕获并处理(即没有使用recover
),这个panic会导致整个程序崩溃,输出panic的相关信息和栈追踪(stack trace)。
main函数的行为:由于main函数本身也是一个协程(主协程),当任何一个协程未捕获的panic发生时,Go运行时会终止所有协程,包括main函数所在的主协程。因此,整个程序会崩溃并退出。
程序示例如下:
package main
import (
"fmt"
"time"
)
func main() {
go func() {
fmt.Println("Goroutine 1 is running")
time.Sleep(2 * time.Second)
panic("Panic in Goroutine 1") // 这里会发生panic
}()
go func() {
fmt.Println("Goroutine 2 is running")
time.Sleep(4 * time.Second)
fmt.Println("Goroutine 2 completed")
}()
time.Sleep(5 * time.Second)
fmt.Println("Main function completed")
}
7、TCP和UDP协议的区别? TCP中滑动窗口,假设窗口1,2,3已经发送,远端ACK4 ,这时候滑动窗口要向前移动吗?为什么?
1. 区别
特性 | TCP | UDP |
---|---|---|
连接类型 | 面向连接(需要握手建立连接) | 无连接 |
可靠性 | 可靠(提供确认、重传机制) | 不可靠(不提供确认和重传) |
顺序保证 | 保证按顺序接收 | 不保证顺序 |
流量控制 | 有流量控制(滑动窗口机制) | 无流量控制 |
开销 | 头部开销较大(20-60 字节) | 头部开销较小(8 字节) |
速度 | 较慢(由于连接建立和可靠性) | 较快(由于无连接和无可靠性) |
常见用途 | HTTP/HTTPS, SMTP, FTP | 视频流, 在线游戏 |
2. 需要前移滑动窗口
接收到 ACK 4 意味着序号 1、2、3 的数据包都已经成功接收,并且接收方期望下一个数据包的序号是 4。这时,发送方知道序号 1、2、3 的数据包已经确认,可以将窗口向前滑动 3 个位置。
8、Http如何实现有状态连接 ?cookie 和session的区别
1. 实现有状态连接
1. 使用 Cookies
Cookies 是服务器发送给客户端的一小段数据,客户端会在后续请求中带上这些数据。服务器可以通过这些数据识别不同的客户端,并维护会话状态。
2. 使用 URL 参数
在 URL 中添加查询参数来传递状态信息。服务器可以通过这些参数识别客户端会话。
3. 使用 Token 认证
Token是一种安全的认证机制,可以在客户端和服务器之间传递有状态信息。Token 一般在用户登录时生成,并在后续请求中传递给服务器。
4. 使用服务器端会话存储
服务器可以使用内存或数据库(比如Session)来存储会话数据,并通过唯一的会话 ID 识别不同的客户端。会话 ID 可以通过 Cookie 或 URL 参数传递。
2. Cookie与Session区别
存储位置: Cookie 在客户端,Session 在服务器端。
安全性: Session 更加安全,因为数据存储在服务器端,不容易被客户端篡改。
生命周期: Cookie 可以持久化存储,而 Session 通常在浏览器关闭或会话超时后失效。
大小限制: Cookies 有大小限制,而 Sessions 依赖于服务器的存储能力。
9、编程题 字符串加法:实现加法,但是输入和输出的数字都是字符串格式
LeetCode 43。原题为字符串相乘,本题为相加,基本原理相同,但本题模拟的过程要简单很多。
// 本方法为实现正整数加法,如果数字为一整一负,则需要按照减法实现。若有小数,亦需单独处理。
// 实际应用中,最好选用math/big包实现任意精度大数的基本运算
func add(a string, b string) string {
m, n := len(a), len(b)
i, j := m - 1, n - 1
add := 0
res := []rune{}
for i >= 0 || j >= 0 || add > 0 {
x := add
if i >= 0 {
x += int(a[i] - '0')
i--
}
if j >= 0 {
x += int(b[j] - '0')
j--
}
add = x / 10
res = append(res, rune(x % 10 + '0'))
}
k := len(res)
for i := 0; i < k - i - 1; i++ {
res[i], res[k-1-i] = res[k-1-i], res[i]
}
return string(res)
}
最后给大家推荐一个LinuxC/C++高级架构系统教程的学习资源与课程,可以帮助你有方向、更细致地学习C/C++后端开发,具体内容请见 https://xxetb.xetslk.com/s/1o04uB