103-【八股】数据结构与算法
八股
哈希表
实现原理:
哈希函数的作用是将给定的键转换为一个数组的索引。哈希函数接收一个输入并输出一个整数,这个整数在哈希表中对应的索引位置。
哈希函数的设计要确保以下几点:
- 均匀分布:哈希函数应当能够将输入均匀地映射到表中的各个桶位置,减少碰撞的概率。
- 低冲突:哈希函数应尽量减少不同的键映射到同一个位置(即哈希冲突)。
冲突解决方式:
- 链接法:每个桶是一个链表,发生冲突就在该位置尾插冲突元素。
- 开放地址法:开放地址法是一种将所有元素直接存储在哈希表数组中的方法,不使用链表来处理冲突。当发生冲突时,系统会查找哈希表中的下一个空桶位置,并将元素插入到该位置。常见的开放地址法有以下几种:
- 线性探测(Linear Probing):当发生冲突时,检查当前位置的下一个位置,直到找到空位置为止。
- 二次探测(Quadratic Probing):探测时按照二次方规律进行,即 i=(h+f(i))mod TableSizei = (h + f(i)) TableSizei=(h+f(i))modTableSize,其中 f(i)f(i)f(i) 是一个平方数序列。
- 双重哈希(Double Hashing):使用第二个哈希函数来决定下一个探测位置。
堆和栈
栈是一种 先进后出数据结构,通常用于存储局部变量和函数调用信息(如函数的参数、返回地址等)。栈由操作系统管理,程序的执行栈在函数调用时不断压入和弹出。
堆是一块由程序员显式管理的内存区域,用于存储动态分配的内存。程序员通过 new
(C++)或 malloc
(C)等函数申请内存,并在使用完毕后,通过 delete
(C++)或 free
(C)手动释放这部分内存。
特性 | 栈(Stack) | 堆(Heap) |
---|---|---|
内存分配方式 | 自动分配和回收,遵循先进后出(LIFO)规则 | 动态分配,由程序员手动管理 |
内存大小 | 内存有限,通常较小(几 MB) | 内存较大,通常受限于操作系统的总内存大小 |
生命周期 | 随着函数调用的开始和结束自动管理 | 由程序员控制,直到显式释放 |
分配和回收速度 | 快,栈指针的移动 | 慢,涉及内存查找和分配 |
存储内容 | 存储局部变量、函数参数、返回地址等 | 存储动态分配的对象(如 new 或 malloc 创建的) |
内存溢出 | 栈溢出(stack overflow),当栈的空间用完时 | 堆溢出(heap overflow),当堆空间不足时 |
内存管理方式 | 由操作系统自动管理 | 需要手动管理,程序员负责内存的分配和释放 |
内存的访问模式 | 由操作系统管理,分配紧凑且连续(低碎片化) | 内存是分散的,可能会导致碎片化 |
算法题
链表
- 约瑟夫环问题的三种解法讲解 - 力扣(LeetCode):环形链表模拟,有序集合模拟,数学归纳推导
二叉树
- 105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
- 106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
- 889. 根据前序和后序遍历构造二叉树 - 力扣(LeetCode)
哈希
- 594. 最长和谐子序列 - 力扣(LeetCode)
二分查找
- 153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
- 【不用库函数求】69. x 的平方根 - 力扣(LeetCode):也能用牛顿迭代
分治法
- 12.4 汉诺塔问题 - Hello 算法 (hello-algo.com)
智力题
- 将一根木棍分成三段,求这三段构成三角形的概率