数据结构第一天
1.O(n)的大O是什么意思?什么是时间复杂度?
O表示时间复杂度(描述的是上界)
时间复杂度是描述时间随输入规模变化的程度
2.线性存储结构和链式存储结构的优点
- 线性(顺序)存储结构优点:
- 随机访问效率高,可通过下标(如数组下标)直接访问任意位置元素,时间复杂度
O(1) 。
- 存储密度高,元素在内存中连续存放,不需要额外空间存储指针等关联信息(相对链表而言 )。
- 链式存储结构优点:
- 插入、删除操作灵活高效,不需要像顺序存储那样大规模移动元素,只需修改指针指向即可,时间复杂度
O(1)(找到操作位置后 ,查找时间O(n))。
- 动态性好,不需要预先分配固定大小的存储空间,可根据需要动态申请和释放节点,能更高效利用内存,避免顺序存储可能出现的空间浪费或不够用情况。
3.解释一下顺序存储与链式存储
顺序存储: 元素存放在一段连续的内存空间里 eg: 数组
链式存储:数据元素存放在不连续的内存节点中,每个节点除了存储数据本身,还存储指向其他节点的指针(单链表存后继指针,双向链表存前驱和后继指针等 )
eg:链表
4.头指针和头结点的区别?
头指针:是指向链表第一个节点(若链表有头结点,则指向头结点;若没头结点则指向首元节点 )的指针变量,它是链表的重要标识,通过头指针才能遍历整个链表,链表为空时头指针为NULL ,它存储的是链表第一个节点(或头结点)的内存地址 。
头结点:
是在链表首元节点之前额外添加的一个节点(数据域一般无实际意义,或可存链表长度等辅助信息 ),其作用是为了简化链表操作(比如插入、删除首元节点时,不用单独处理头指针改变的特殊情况 ),链表有头结点时,头指针指向头结点,头结点的next指针指向首元节点,链表为空时,头结点可能存在(数据域等无有效数据,头结点的next为NULL )。
5.栈和队列的区别和内存结构
- 区别:
- 操作特性:栈是 “后进先出”(LIFO)结构,只能在栈顶进行插入(进栈)和删除(出栈)操作;队列是 “先进先出”(FIFO)结构,在队尾插入(入队),在队头删除(出队) 。
- 应用场景:栈常用于函数调用栈、表达式求值(如后缀表达式计算 )、括号匹配等;队列常用于进程调度(如操作系统中任务队列 )、广度优先搜索(BFS )等场景 。
- 内存结构:
- 栈(数据结构里的栈,和内存分区的栈不同概念,但存储逻辑有相似 ):顺序存储的栈可基于数组实现,数组一端作为栈底,另一端作为栈顶,通过栈顶指针标记栈顶位置;链式存储的栈用链表实现,一般以链表头为栈顶,插入删除在表头操作 。(头插法)
- 队列:顺序存储的队列常以循环队列形式实现(解决普通顺序队列 “假溢出” 问题 ),用数组存放元素,通过队头、队尾指针管理;链式存储的队列用链表实现,一般队头指针指向链表头(出队端 ),队尾指针指向链表尾(入队端 )。
6.有一个循环队列Q,里面的编号是0到n-1,头尾指针分别是f,p,现在求Q中元素的个数?
元素个数=(p-f+n)%n
7.如何区分循环队列是队空还是队满
假设头指针f,尾指针p,循环队列最多可以放n个元素
队空:f=p
队满:f=(p+1)%n
8.堆、大顶堆、小顶堆实现及应用
堆分为大顶堆和小顶堆。c++里用优先队列来实现默认大根堆
在大顶堆中,父节点的值总是大于或等于子节点的值;
在小顶堆中,父节点的值总是小于或等于子节点的值。
9.如何判断链表里是否有环?
- 原理:
设置两个指针,慢指针 slow 每次移动 1 步,快指针 fast 每次移动 2 步。如果链表中存在环,那么随着指针不断移动,快指针一定会追上慢指针(因为快指针相对慢指针每次多走 1 步,在环里不断 “追赶” );如果链表无环,快指针会先到达链表末尾(遇到 null )。
方法2:哈希法
- 原理:
遍历链表的每个节点,每次访问一个节点时,检查该节点是否在哈希表(或集合 )中存在。若存在,说明之前已经访问过这个节点,链表存在环;若不存在,就把该节点加入哈希表,继续遍历下一个节点。如果遍历到链表末尾(遇到 null ),说明链表无环。(哈希表记录)
10.哈希表的概念、构造方法、哈希有几种类型?哈希冲突的解决办法?
- 哈希表概念:也叫散列表,是根据关键码(键,key )直接访问数据的值(value )的一种数据结构,通过哈希函数(散列函数 )把关键码映射到对应的存储位置(哈希地址 ),从而实现快速的插入、查询、删除等操作,理想情况下时间复杂度接近
O(1) 。
- 构造方法:(哈希函数如何进行构造)
- 直接定址法:取关键字或关键字的某个线性函数值为哈希地址,如
H(key) = key 或H(key) = a * key + b(a、b为常数 ),适合关键字分布连续的情况 。
- 数字分析法:分析关键字各位数字的分布规律,选取分布均匀的若干位作为哈希地址,适用于关键字位数较多且各位数字有一定分布特征的情况 。
- 平方取中法:对关键字平方后,取中间几位作为哈希地址,中间位受关键字各位影响较大,能使哈希地址分布更均匀,常用于关键字不确定的情况 。
- 折叠法:将关键字分割成位数相同的几部分(最后一部分位数可不同 ),然后取这几部分的叠加和(舍去进位 )作为哈希地址,适合关键字较长的情况 。
- 除留余数法:
H(key) = key % p(p是一个小于哈希表长度的质数 ),简单且应用广泛,选好p能有效减少冲突 。
- 哈希(表)类型:从存储结构等角度,有开放寻址法实现的哈希表(遇到冲突时,通过探查其他地址解决,如线性探查、二次探查、双重哈希等 ),还有链地址法(拉链法 )实现的哈希表(每个哈希地址对应一个链表,冲突元素存到对应链表中 )等常见实现类型 。
- 哈希冲突解决办法:
- 开放寻址法:如上述,线性探查是冲突时依次探查下一个地址;二次探查是按
H_i = (H(key) + i²) % m 或H_i = (H(key) - i²) % m(i为探查次数,m为哈希表长度 )等方式找下一个空地址;双重哈希是用多个哈希函数,冲突时换另一个哈希函数计算地址 。
- 链地址法:为每个哈希地址维护一个链表,当关键字哈希到同一地址时,就把对应的元素插入到该链表中,查询时遍历对应链表查找元素 。
- 再哈希法:准备多个不同哈希函数,冲突时换用另一个哈希函数计算地址,直到找到空地址 。
- 建立公共溢出区:把哈希表分为基本表和溢出表,冲突的元素统一存放到溢出表中,查询时先在基本表找,找不到再去溢出表找 。