快手一面【日常实习】
base: 北京
岗位:客户端开发-ios
时间: 2024/9/2 上午 11点
一面已过 ,明天晚上二面
文章目录
操作系统 :
进程和线程的区别
- 定义:
进程:操作系统中一个正在运行的程序实例。 它包含了程序代码和当前活动状态。每个进程都有自己独立的地址空间。
线程:是进程中的一个执行单元。线程共享进程的资源(如内存、文件句柄),但它们有自己的栈、寄存器和程序计数器。
进程是系统进行资源分配和调度的基本单位
线程是操作系统能够进行运算调度的最小单位,其是进程中的一个执行任务(控制单元)
- 资源:
进程:拥有独立的内存空间和系统资源。
线程:同一个进程中的线程共享该进程的资源。
- 开销:
进程:进程之间的切换开销大,因为需要切换独立的内存空间和资源。
线程:线程之间的切换开销小,线程共享同一进程的资源,切换时不需要更换内存空间。
- 通信:
进程:进程间通信相对复杂,需要通过操作系统提供的机制如管道、消息队列、共享内存等来进行。
线程:线程间的通信更为直接,因为它们共享进程的地址空间,可以通过直接访问共享数据来通信。
详解推荐: 面试官:说说什么是进程?什么是线程?区别?
计网:
1. 网络协议有哪些?
网络协议主要分为以下几层,各层具有不同的协议:
应用层: HTTP, HTTPS, FTP, SMTP, DNS, POP3, IMAP
传输层: TCP, UDP
网络层: IP,ICMP, ARP, RARP, OSPF, BGP
数据链路层:Ethernet, PPP, HDLC
物理层:光纤、网线、无线信号等物理媒介
2. TCP和UDP在哪一层?
TCP和UDP都属于传输层协议
3.TCP 和 UDP的区别?
连接性:
TCP:面向连接的协议,在传输数据前需要建立连接(三次握手)。 UDP:无连接协议,发送数据前不需要建立连接。
可靠性:TCP:提供可靠的数据传输,有确认、重传机制,保证数据的完整性和顺序性。 UDP:不保证可靠传输,没有确认、重传机制,数据可能丢失或乱序。
流控制和拥塞控制:
TCP:有流控制和拥塞控制机制,保证网络的稳定性。 UDP:没有流控制和拥塞控制机制,传输速度快,但不保证数据的稳定传输。
速度:TCP:较慢,由于连接建立、确认和重传机制,开销较大。 UDP:较快,适用于实时应用,如视频直播、在线游戏。
4.TCP 为什么可靠?
顺序控制:TCP数据包带有序号,接收方可以按序重组数据包,即使数据包乱序也能正确排序。
确认应答:每次数据包传送后,接收方都需要发送一个确认应答(ACK),未收到确认的包会被重传。
超时重传:如果发送方在一定时间内没有收到确认应答,数据包将被重新发送。
流量控制:TCP使用滑动窗口机制来控制数据流的传输速度,防止网络拥塞。
拥塞控制:TCP采用慢启动、拥塞避免、快重传、快恢复等机制,确保网络不会因为过载而崩溃。
- 三次握手 和 四次挥手 解释一下!
三次握手:
客户端发送SYN:客户端向服务器发送一个SYN(同步)包,请求建立连接。
服务器发送SYN-ACK:服务器收到SYN包后,返回一个SYN-ACK包,表示同意连接,并请求客户端确认。
客户端发送ACK:客户端收到SYN-ACK包后,发送一个ACK包,确认连接建立。 连接正式建立。四次挥手:
客户端发送FIN:客户端向服务器发送一个FIN(终止)包,表示要断开连接。
服务器发送ACK:服务器收到FIN包后,返回一个ACK包,表示收到请求。
服务器发送FIN:服务器在准备好断开连接后,发送一个FIN包,表示可以断开连接。
客户端发送ACK:客户端收到服务器的FIN包后,发送一个ACK包,确认连接断开。 连接正式断开。
详解推荐: TCP 三次握手与四次挥手面试题
5. HTTP 和 HTTPS的区别?
安全性:
HTTP:不安全,数据以明文形式传输,容易被拦截、篡改。 HTTPS:安全,使用SSL/TLS协议对数据进行加密,防止数据被拦截和篡改。
端口:HTTP:使用端口80。 HTTPS:使用端口443。
证书:HTTP:不需要证书。 HTTPS:需要数字证书,由CA机构颁发,保证服务器的真实性。
性能:HTTP:性能稍高,因为不涉及加密解密操作。 HTTPS:性能稍低,因为涉及加密解密操作,但现代硬件的性能已经使这个差距非常小。
语言
面试官: 看你做后端java开发比较多,解释一下 String、StringBuilder以及StringBuffer 的区别
数据结构
排序算法 和 稳定性
常见的排序算法有哪些?哪些是稳定的?
推荐链接: ❤️十大排序算法详解❤️——可能是你看过最全的,完整版代码
也可以多看一些 动图 解释,印象更深刻
常见的排序算法有很多,主要包括:
冒泡排序(Bubble Sort)
选择排序(Selection Sort)
插入排序(Insertion Sort)
归并排序(Merge Sort)
快速排序(Quick Sort)
堆排序(Heap Sort)
希尔排序(Shell Sort)
计数排序(Counting Sort)
桶排序(Bucket Sort)
基数排序(Radix Sort)
稳定与不稳定排序算法
排序算法是否稳定,取决于它在处理相等元素时,是否保持它们在原数组中的相对顺序。稳定的排序算法对于某些场景(如需要进一步操作排序后的结果)非常重要。
稳定的排序算法:
冒泡排序
插入排序
归并排序
计数排序
基数排序
桶排序(在某些实现中)
不稳定的排序算法:
选择排序
快速排序
堆排序
希尔排序
常见排序算法的代码实现及解释
1. 冒泡排序(Bubble Sort)(稳定)
冒泡排序通过多次遍历数组,每次比较相邻的元素并交换顺序,直到所有元素有序。
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
解释: 逐渐将较大的元素“冒泡”到数组的末尾。
时间复杂度:O(n²)
2. 选择排序(Selection Sort)(不稳定)
选择排序每次从未排序的部分中选出最小的元素,并将其放到已排序部分的末尾。
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_idx = i
for j in range(i+1, n):
if arr[j] < arr[min_idx]:
min_idx = j
arr[i], arr[min_idx] = arr[min_idx], arr[i]
return arr
解释:每次选择剩下部分中最小的元素放到合适的位置。 时间复杂度:O(n²)
3. 插入排序(Insertion Sort)(稳定)
插入排序通过逐步将元素插入到已排序部分的合适位置。
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and key < arr[j]:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
return arr
解释:
类似于玩扑克牌时的整理牌顺序,每次插入一个元素。
时间复杂度:O(n²)
4. 归并排序(Merge Sort)(稳定)
归并排序是一种分治算法,先将数组分成两个子数组,分别排序后再合并。
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]
merge_sort(L)
merge_sort(R)
i = j = k = 0
while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
return arr
解释:
通过递归分割数组,并将两个有序数组合并成一个。
时间复杂度:O(n log n)
5. 快速排序(Quick Sort)(不稳定)
快速排序是一种分治算法,选取一个基准点(pivot),将数组分为比基准点小和大的两部分,分别排序。
def quick_sort(arr):
if len(arr) <= 1:
return arr
else:
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
解释:
通过递归地划分数组,最终得到有序数组。
时间复杂度:平均O(n log n),最坏O(n²)
6. 堆排序(Heap Sort)(不稳定)
堆排序利用二叉堆的数据结构,每次从堆顶取出最大元素放到排序部分。
def heapify(arr, n, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and arr[i] < arr[left]:
largest = left
if right < n and arr[largest] < arr[right]:
largest = right
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heap_sort(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0], arr[i]
heapify(arr, i, 0)
return arr
解释:
通过构建最大堆,每次提取堆顶元素来构建有序数组。
时间复杂度:O(n log n)
稳定性和不稳定性解释:
稳定排序:在处理相同值的元素时,不会改变它们在原始序列中的相对顺序。例如,归并排序和冒泡排序是稳定的。
不稳定排序:在排序过程中,可能会改变相同值元素的相对顺序。例如,快速排序和堆排序是不稳定的。
情景题:
看你有机器学习、深度学习的基础,如果我们现在想要做一个,距离优先、价格优先的东西,你觉得你会如何处理 哪些数据,以及如何处理这些数据?
项目介绍
介绍一下,你做项目遇到的难题,怎么解决的
代码题
层序遍历
用二叉树,实现一个层序遍历,简单实现核心功能即可!
#include <iostream>
#include <queue>
using namespace std;
// 定义二叉树节点结构
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 层序遍历函数
void levelOrderTraversal(TreeNode* root) {
if (root == nullptr) return;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
cout << node->val << " ";
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
int main() {
// 构建一个简单的二叉树
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
cout << "层序遍历结果: ";
levelOrderTraversal(root);
return 0;
}
反问环节
转载链接: https://vue3js.cn/interview/linux/thread_process.html#%E4%B8%80%E3%80%81%E8%BF%9B%E7%A8%8B
https://xiaolincoding.com/network/3_tcp/tcp_interview.html#tcp-%E5%9F%BA%E6%9C%AC%E8%AE%A4%E8%AF%86