1.```按层次打印一个二叉树
定义二叉树节点类
class TreeNode: def init(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
定义按层次打印二叉树函数
def printLevelOrder(root): if not root: return
# 广度优先遍历
queue = [(root, 0)]
while queue:
node, level = queue.pop(0)
# 遍历当前层节点
if level % 2 == 0:
print(node.val, end=' ')
else:
print(node.val, end='\t')
# 加入左右子节点到队列
if node.left:
queue.append((node.left, level + 1))
if node.right:
queue.append((node.right, level + 1))
2.给你一个数n(最大为10000),怎么求其阶乘
由于阶乘的计算可能会产生非常大的数,因此在计算较大数值的阶乘时需要特别注意数值溢出的问题。根据阶乘的特性,可以采用分部阶乘的方式降低数值的增长速度,从而避免溢出。例如,对于正整数 a, k,有:
a 的 k 次阶乘等于 a 乘以 a-1 的 k-1 次阶乘,再乘以 a-2 的 k-2 次阶乘,直到 a-k+1 的 0 次阶乘。
因此,可以编写一个函数,根据给定的数值 n 和阶乘的指数 k,逐步计算出 n 的 k 次阶乘:
def power_factorial(a, k): if k == 0: return 1 elif k % 2 == 1: return a * power_factorial(a - 1, k - 1) else: t = power_factorial(a - 1, k // 2) return t * t
3.判断两个单链表是否有交叉
判断两个单链表是否有交叉可以使用快慢指针的方式。快指针每次移动两个节点,慢指针每次移动一个节点。如果快指针追上了慢指针,说明两个链表有交叉。
慢指针指向链表 1 的头节点,快指针指向链表 2 的头节点。然后同时移动指针,直到其中一个指针到达链表末尾。
4.使用C++写一个字符串插入的函数
#include #include
using namespace std;
string insert_char(string str, int index, char c) { // 检查索引是否有效 if (index < 0 || index >= str.length()) { throw out_of_range("Index out of range"); }
// 分配足够的内存来存储插入字符后的字符串
string result = new char[str.length() + 2];
// 将原始字符串复制到结果字符串中
for (int i = 0; i < index; i++) {
result[i] = str[i];
}
// 插入新字符
result[index] = c;
// 将剩余的原始字符复制到结果字符串中
for (int i = index + 1; i < str.length(); i++) {
result[i + 1] = str[i];
}
// 返回插入字符后的字符串
return result;
}
5.关于虚函数底层的实现,你了解多少?
在C++中,虚函数允许在父类和子类之间进行动态绑定,使得在运行时可以根据对象的实际类型来调用正确的成员函数。为了实现这个机制,编译器在每个包含虚函数的类中都生成了一个虚函数表。虚函数表是一个存储了虚函数地址的数组。每个类实例(对象)在内存中都有一个指向相应虚函数表的指针,该指针称为虚函数表指针(vptr)。 当对象被创建时,虚函数表指针被初始化为指向该类的虚函数表。如果一个对象的类具有继承关系,并且子类也定义了虚函数,那么子类会重写继承自父类的虚函数,并且子类的虚函数表会覆盖父类的虚函数表。这确保了通过父类指针或引用调用虚函数时可以正确地定位到子类的实现。 当调用一个虚函数时,编译器会通过对象的虚函数表指针来查找对应的函数地址,然后跳转到那个地址执行相应的代码。这个过程是在运行时动态决定的,因此能够实现多态。 注意,虚函数的使用会带来一些额外的开销,例如额外的内存消耗(用于存储虚函数表指针)和函数调用开销(在运行时查找虚函数地址并调用)。但这些开销通常是可以接受的,特别是在需要实现多态行为的场景中。
6.请实现快排。
```
from typing import List
def quick_sort(lst: List[int]) -> List[int]:
if len(lst) <= 1:
return lst
pivot = lst[0]
less_than_pivot = [x for x in lst[1:] if x <= pivot]
greater_than_pivot = [x for x in lst[1:] if x > pivot]
return quick_sort(less_than_pivot) + [pivot] + quick_sort(greater_than_pivot)
7.请实现堆排序。
import random
# 检查是否是最大堆
def is_max_heap(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
ifarr[i] < arr[i + 1]:
return False
return True
# 建立最大堆
def build_max_heap(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, i)
return arr
# 交换 arr[i] 和 arr[n-1] 并维护最大堆的性质
def heapify(arr, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and arr[left] > arr[largest]:
largest = left
if right < n and arr[right] > arr[largest]:
largest = right
if largest!= i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, largest)
# 飞快排序
def quick_sort(arr):
n = len(arr)
if n <= 1:
return arr
else:
# 选择第一个元素为基准值
pivot = arr[0]
# 将比基准值小的元素放在左边 region0,比基准值大的放在右边 region1
less_than_pivot = [x for x in arr[1:] if x <= pivot]
greater_than_pivot = [x for x in arr[1:] if x > pivot]
# 在 region0 和 region1 分别递归执行 quick_sort
return quick_sort(less_than_pivot) + [pivot] + quick_
7.实现红黑树的删除操作
红黑树是一种自平衡的二叉搜索树,它具有以下特性: 1. 每个节点是红色或黑色。 2. 根节点是黑色。 3. 每个叶节点(NIL节点,空节点)是黑色。 4. 如果一个节点是红色的,则它的子节点必须是黑色的。 5. 从任意节点到其每个叶子的所有路径都包含相同数目的黑色节点。 红黑树的删除操作相对复杂。
下面是红黑树删除操作的伪代码:
1. 如果要删除的节点存在: - 如果该节点有两个非空子节点,找到其后继(右子树的最小节点)或前驱(左子树的最大节点),将其值替换到要删除的节点,并将删除操作转换为删除后继或前驱节点。
- 否则,该节点最多只有一个非空子节点或无子节点。
2. 如果要删除的节点是红色节点,并且没有子节点,直接删除该节点。
3. 如果要删除的节点是黑色节点,并且没有子节点: - 获取该节点的父节点和兄弟节点。 - 如果兄弟节点是红色的,将兄弟节点设为黑色,将父节点设为红色,然后对父节点进行左旋或右旋。 - 如果兄弟节点是黑色的: - 如果兄弟节点的两个子节点都是黑色的,将兄弟节点设为红色,并继续对父节点进行删除操作。 - 如果兄弟节点的一个子节点是红色的,将兄弟节点设为红色,红色子节点设为黑色,然后对兄弟节点进行适当的旋转操作,再继续进行删除操作。
4. 如果要删除的节点是黑色节点,并且有一个红色子节点: - 将红色子节点设为黑色。
5. 如果要删除的节点是黑色节点,并且有两个黑色子节点: - 在删除节点前,将删除节点的位置设为哨兵节点(NIL节点)。 - 将哨兵节点设为双重黑色。
6. 从删除节点的父节点开始,根据红黑树的性质进行调整,包括旋转操作和颜色调整,以维护红黑树的平衡性。 上述是红黑树删除操作的大致步骤,实际的实现会涉及到具体的数据结构和算法细节。由于篇幅的限制,无法给出完整的实现代码。如果你想了解更详细的实现细节,我建议阅读相关的算法书籍或参考开源的红黑树实现
8.我了解TCP(传输控制协议)和UDP(用户数据报协议),它们是互联网通信中常用的两种传输协议。 TCP是一种面向连接的协议,提供可靠的、有序的数据传输和错误检测机制。 TCP在通信双方之间建立一条可靠的连接,并通过序列号和确认机制来保证数据的可靠性。 它还通过流量控制和拥塞控制机制来调整发送速率,以避免网络拥塞。TCP适用于要求可靠传输的应用场景,比如文件传输、电子邮件等。 UDP是一种无连接的协议,提供不可靠的数据传输和简单的错误检测功能。UDP将数据以数据报的形式发送,不保证数据的可靠性和顺序性。 由于UDP没有建立连接和维护状态的开销,因此通信开销较低,传输速度较快 。UDP适用于实时通信、流媒体和音视频传输等应用场景,其中丢失一些数据并不会对应用造成严重影响。
TCP和UDP都是在传输层使用的协议,它们在功能、性能和应用场景上有所区别,需要根据具体的需要来选择合适的协议。