前言
数据结构和算法是计算机科学的基石,它们为我们提供了处理和组织数据的方法和工具。通过学习数据结构,您将能够理解如何存储和操作不同类型的数据,如何优化内存使用和访问效率,以及如何设计高效的算法来解决各种计算问题。掌握这些基本概念将使您能够构建更快、更强大的应用程序,并在面对复杂任务时迎刃而解。
软件技术基础
数据结构
- 数据结构是带有结构的数据元素的集合,被定义为(D,R),其中D是数据元素的集合,R是定义在D上关系的有限集合。
- 数据项是数据的最小单位。
- 数据的逻辑结构是各数据间的逻辑结构,是用户按使用需要建立的。
- 要求同一逻辑结构的所有元素具有相同的特性,这意味着不仅数据元素包含的数据项的个数要相同,而且对应数据项类型要一致。
- 树形结构中的元素存在一对多的关系,线性结构一对一,图结构多对多关系。
- 逻辑上可分为线性和非线性。
- 数据元素之间的关系有两种不同的表示方法:顺序映像和非顺序映像。
- 程序 = 算法 + 数据结构
1、简述:数据、数据元素、数据结构、数据对象
数据:指所有能够输入到计算机中并被计算机程序处理的符号集合
数据元素:数据元素是数据的基本单位。数据集合中的一个实体,是计算机程序中加工处理的基本单位
数据结构:相互之间存在一种或多种关系的数据元素的集合,即包括数据元素的集合或数据元素之间的集合
数据对象:具有相同性质的数据元素的集合,为数据的一个子集
2、逻辑结构与存储结构的什么关系?数据的逻辑结构和存储结构各有哪几种?
逻辑结构反映数据间的逻辑关系。集合结构、线性结构、树结构、图结构,其中树和图结构全称为非线性结构。
存储结构是逻辑结构的存储实现。顺序存储结构、链式存储结构、索引存储结构、散列存储结构。
算法分析
- 两个重要方面是——时间复杂度和空间复杂度
- 目的:分析算法的效率以求改进
3、简述算法的定义及其重要特征
算法是一系列解决问题的清晰指令。
特征:输入、输出、有穷性、确切性、可行性
栈
- 假定利用数组 a[n] 顺序存储一个栈,用 top 表示栈顶指针,top==-1 表示栈空,并已知栈未满,当元素 x 进栈时所执行的操作为a[++top]=x
- 链栈执行 Pop 操作,并将出栈的元素存在 x 中应该执行
x=top->data; top=top->next
- n个不同元素依次进栈,若想要知道有多少种不同的出栈序列,可通过卡塔兰数得到。这里我给出一些示例:
F(0) = 1 F(1) = 1 F(2) = 2 F(3) = 5 F(4) = 14 F(5) = 42 F(6) = 132 F(7) = 429
4、简述栈的特点及输入输出的操作主要过程
特点:
(1)先进后出(LIFO),最后入栈的元素最先出栈
(2)只能在栈顶进行插入和删除操作,即只能对栈顶元素进行访问
(3)栈中的元素在插入和删除时,不需要移动其他元素,只需要调整栈顶指针即可
过程:
(1)入栈,将一个元素添加到栈顶
(2)出栈,从栈顶移除一个元素
(3)获取栈顶元素,返回栈顶的元素值,但不修改栈的状态
(4)判断栈是否为空
(5)判断栈是否为满
队列
- 假设以数组 A[m]存放循环队列的元素,其头尾指针分别为 front 和 rear,则当前队列中的元素个数为(rear-front+m)%m。
5、简述队列的特点及输入输出的操作主要过程
特点:
(1)先进先出(FIFO),最先入队的元素首先出队
(2)只能在队尾进行插入操作(入队),在队头进行删除操作(出队),元素的访问限制在队头和队尾
(3)队列中的元素在插入和删除时,不需要移动其他元素,只需要调整队头和队尾指针即可
过程:
(1)入队(Enqueue):将一个元素添加到队尾
(2)出队(Dequeue):从队头删除一个元素
(3)获取队头元素,但不修改队列的状态
(4)判断队列是否为空
(5)判断队列是否为满
6、循环队列的优点是什么?如何判别它的空和满?
它可以克服顺序队列的“假上溢”现象,能够使存储队列的向量空间得到充分利用。
一是采用计数器来判断,空时,计数器为0,满时,计数器为maxsize。 二是另设一个布尔变量以匹别队列的空和满。 三是少用一个元素的空间,约定入队前,测试头尾指针是否会重合,若重合则认为队满。
注:栈和列表具有相同的逻辑结构
链表
- 单链表中,增加一个头结点的目的是为了方便运算的实现。
- 对于一个头指针为head的带头结点的单链表,判定该表为空表的条件是
head->next==NULL
- 在一个单链表中,已知q所指结点是p所指结点的前驱结点,若在q和p之间插入结点s,则执行
q->next=s;s->next=p;
- 链表是一种顺序存取的存储结构。
线性结构
- 线性表由同类数据元素组成,每个元素必须属于同一个数据对象。
- 线性表采用顺序存储表示时,必须占用一片连续的存储单元。
- 长为n的顺序存储的线性表中,删除第i个元素,需要从前向后移n-i个元素。
- 顺序表中,只要知道基地址和结点大小,就可以在相同时间内求出任一结点的存储结构。
- 线性表的顺序存储结构是一种随机存取的存储结构。
- 若线性表最常用的操作是存取第 i 个元素及其前驱和后继元素的值,为了提高效率,应釆
用顺序表的存储方式。
7、何时选用顺序表?何时选用链表作为线性表的存储结构为宜?
当需要频繁插入,删除元素时,且不关心随机访问的效率时,可以选择链表作为线性表的存储结构;当需要机访问元素的效率时,且元素数量较小,可选择顺序表作为线性表的存储结构。
树
- 树最适合用来表示元素之间具有层次关系的数据。
- 设哈夫曼树中的叶子结点总是为 n,则总结点数为2n-1
- 根据二叉树的定义可知二叉树共有5种不同的基本形态。
- 对二叉排序树按中序遍历可以得到由小到大的有序序列。
- 二叉树的先序遍历中,任意结点均处在其子结点之前。
- 哈夫曼树是带权路径长度最短的树,路径上权值较大的结点离根较近。
- 完全二叉树中,若一个结点没有左孩子,则它必是树叶。
先序遍历 根、左、右
中序遍历 左、根、右
后序遍历 左、右、根
8、简述树和二叉树之间的区别与联系?
区别:
(1)结构不同:树是由若干个节点组成的集合,这些节点通过边连接在一起。每个节点可以有多个子节点。二叉树是一种特殊的树结构,每个节点最多只能有两个子节点,分别称为左子节点和右子节点。
(2)子节点数量:树中的节点可以有任意多个子节点,而二叉树中的节点最多只能有两个子节点。
(3)子树的位置:在树中,子节点的位置没有特定的顺序。在二叉树中,左子节点和右子节点的位置是固定的,左子节点位于父节点的左侧,右子节点位于父节点的右侧。
联系:
(1)二叉树可以看作是树的一种特殊情况,即每个节点最多只有两个子节点,可以将树转化为二叉树来进行一些特定的操作。
(2)树和二叉树都可以用递归方式遍历节点。
图
- 有 N 个结点的无向图,该图至少应有 N-1 条边才能确保是一个连通图
- 有向图中有 n 个顶点,则该有向图对应的邻接表中有 n 个表头结点
- 有向图中所有顶点入度之和与所有顶点出度之和的比是1
- 任何一个带权的无向连通图的最小生成树有一棵或多棵
- 在边稀疏(e<<n(n-1)/2)的情况下,用邻接表表示图比用邻接矩阵节省存储空间
- 一个具有 N 个顶点的无向图最多有 N(N-1)/2 条边
- prim算法按照圈中的1,2,3...排列连线,kruskal算法按照权重的从小到大来连线
9、什么是AOV网络?
AOV网是一种有向无回路的图型模型,活动被表示为顶点,依赖关系则由有向表表示,而边表示活动之间的先后关系。
注:拓扑排序,遵循的是一种先后关系,举个例子:进入大学,首先就是学高等数学、C语言等,有了这个基础才能去学数据结构。
查找
顺序表的长为 n,用顺序查找法,则其每个元素的平均查找长度是(n+1)/2。
折半查找,mid=(low+hight) / 2,遇到偶数是向下去整。
在哈希函数除余法 H(key)=key%m 中,一般来讲,m 应取素数。
哈希(散列)函数选择的两条标准是简单和均匀。
进行二分查找的表必须是顺序存储的有序表。
内部排序
- 对待排序的元素序列进行划分,将其分为左右两个子序列,再对两个子序列施加同样的排序操作,知道子序列为空或只剩下一个元素为止。这样的排序方法是快速排序。
- 使用python代码来查看排序算法每一步的运行。
class pysort():
def insertion_sort(self, arr):
"""
直接插入排序
:param arr:
:return:
"""
n = len(arr)
for i in range(1, n):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
print(arr)
def quick_sort(self, arr, low, high):
"""
快速排序
:param arr:
:param low:
:param high:
:return:
"""
if low < high:
pivot_index = self.partition(arr, low, high)
print(arr)
self.quick_sort(arr, low, pivot_index - 1)
self.quick_sort(arr, pivot_index + 1, high)
def partition(self, arr, low, high):
"""
:param arr:
:param low:
:param high:
:return:
"""
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
def bubble_sort(self, arr):
"""
冒泡排序
:param arr:
:return:
"""
n = len(arr)
for i in range(n - 1):
for j in range(n - 1 - i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
print(arr)
def selection_sort(self, arr):
"""
直接选择排序
:param arr:
:return:
"""
n = len(arr)
for i in range(n - 1):
min_index = i
for j in range(i + 1, n):
if arr[j] < arr[min_index]:
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i]
print(arr)
if __name__=="__main__":
a=[46,79,56,38,40,84]
Sort=pysort()
Sort.quick_sort(a,0,5)
操作系统
- 进程控制一般有操作系统内核来实现
- 操作系统中的缓冲区常采用的数据结构是队列
- 进程从运行状态进入就绪状态的可能原因是时间片用完
10、操作系统有哪些基本特征?
并发性、共享性、随机性、虚拟性
11、什么是进程?进程与程序的区别是什么?
进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位。
- 进程是动态的,程序是静态的
- 进程具有自己的生命周期,具有创立、运行、停止、结束等不同的运行阶段和状态
- 进程除了和程序相关以外,还和数据相关
- 进程可以包含多个程序
- 程序可以对应多个进程,程序每执行一次,就是一个进程
12、请描述进程的几种基本状态及进程变化图。
三种基本状态:运行状态、阻塞状态和就绪状态
13、常用进程调度算法有哪些?请分别描述。
先来先服务调度算法,按照进程到达的顺序进行调度,先到达的进程先执行,直到完成或阻塞;
时间片轮转调度算法,每个进程被分配一个固定的时间片,当时间片用完后,进程被暂停,让其他进程执行,被暂停的进程排到就绪队列的末尾;
优先级调度算法,为每个进程分配一个优先级,根据优先级来决定进程的调度顺序,优先级高的进程先执行。
14、何为死锁?产生死锁的原因有哪些?
计算机系统中,两个或多个进程无限期的等待,永远不会发生事件的状态。
原因:系统中的资源不足,进程推进的速度不合理
软件工程
15、软件的生命周期分为哪几个阶段?每个阶段的任务是什么?
- 问题的定义及规划,主要确定软件的开发目标及其可行性
- 需求分析,对软件需要实现的各个功能进行详细分析
- 软件设计,对整个软件系统进行设计,如系统框架设计、数据库设计等
- 程序编码,写出正确的容易理解,容易维护的程序模块
- 软件测试,通过各种类型的测试使软件达到预定的要求
- 运行维护,通过各种必要的维护活动使系统持久的满足用户的需求
16、简述软件设计的原则有哪些?
(1)软件对于分析模型应该是可跟踪的
(2)设计结构应该尽可能的模拟实际问题
(3)设计应该表现出一致性
(4)不要把设计当作写代码
(5)在创建设计的时就应该能够多评估质量
(6)评审设计可以减少语义性的错误
(7)设计应该模块化
17、什么是软件测试?软件测试的方法和步骤有哪些?
软件测试是一个为了寻找软件错误而运行软件的过程,一个成功的软件测试是指找到了迄今为止尚未发现错误的测试。
方法:白盒测试、黑盒测试、灰盒测试
步骤:单元测试、集成测试、系统测试和验证测试4步