关于树的深度优先搜索算法描述错误的是
A : 按照某个条件往前试探搜索,如果前进中遭遇失败, 则退回头另选通路继续搜索,直到找到条件目标为止
B: 先访问该节点所有的子节点, 遍历完毕后选取它未访问过的子节点重复上述过程,直到找到条件目标为止
C: 假设树的顶点树为V,则算法的空间复杂度为O(V)
D: 深度优先算法非常适合使用递归来实现
对于C选项来说,我不是太理解,因为DFS,空间复杂度就是树递归的深度, 那么就是logH,除非是树退化为链表的情况才是空间复杂度为O(V)啊
,因为DFS,空间复杂度就是树递归的深度, 那么就是logH,除非是树退化为链表的情况才是空间复杂度为O(V)啊
将10阶对称矩阵压缩存储到一维数组A中,则数组A的长度最少是多少
比如5阶的为5 * 5 / 2 + 对角线的另一半 (5 / 2) = 12.5 + 2.5 = 15个长度
所以10阶的为10 * 10 / 2 + 10 / 2 = 50 + 5 = 55
需要频繁的插入和删除操作使用数组,队列,链表,栈这四种数据结构中的 链表最合适,如果只考虑只在最后一个位置频繁删除和插入那么栈,链表都可以
已知循环队列存储在一维数组A[0…n-1]中,且队列非空时front和rear分别指向队头和队尾元素, 若初始队列为空,且要求第1个进入队列的元素存储在A[0]处,则初始时front和rear的值分别是0和n - 1
循环队列需要留出一个空间,方便判断队列是否为满(循环队列解决假溢出);
常用的队列实现:
当队列为空时,front == rear;其中rear指向队尾元素的下一个位置,即下一个元素入队的位置
入队操作:先赋值,再rear = (rear+1)%MAXSIZE;此时rear仍然指向队尾元素的下一个位置
但是题目中!!!有一句!!!front和rear分别指向队头元素和队尾元素!!!
rear指向队尾元素!!!
把常用的队列实现方法来了个886
那么,在这道题中,判断队满和队空我是一时半会想不下去了。。。(front == (rear + 1)%MAXSIZE 既可以是满,也可以是空啊= =,除非说还要留一个空间出来)
但是!!!
这道题说,初始时队列为空,无论如何还是front == (rear + 1)%MAXSIZE.
简单的说就是,队列是空的,rear指向队尾元素,front指向队头元素,入队了,(rear +1)%n,这个时候A[0]既是队头元素,也是队尾元素,rear = front = 0;所以是rear = n-1
在一个长度为n的顺序表中删除第i元素 ,要移动n - i个元素, 如果要在第i个元素前插入一个元素,要后移 n - i + 1
这里注意第i个元素不包含第0个元素,因为我们想到的是数组从0下标开始,实际计算的时候 第0个下标在第1个元素的位置
二叉树的先序遍历为EFHIGJK 和中序遍历 HFIEJKG, 则二叉树为
E
F G
H I J
K
第二次考试题
解码方程
dp[i]为str[0…i]的译码方法总数
分情况讨论
1若s[i] = ‘0’ ,那么若s[i-1] = '1’或者’2’则pd[i] = dp[i-2],否则为0
2若s[i-1] = ‘1’,则dp[i] = dp[i-1] + dp[i-2]
解释:s[i-1]和s[i]分开译码,为dp[i-1],合并译码为dp[i-2]
3若s[i-1] = ‘2’ and ‘1’ <=s[i] <=“6”,则dp[i] = dp[i-1] + dp[i-2]
因为dp[i]只和前两项有关,可以单变量单体dp[]数组
将空间复杂度从O(n)降低到O(1)
Decode Ways
1.算法思路
动态规划求解
这道题的分类非常重要
若s[i]=‘0’,则
若s[i-1]='1’或‘2’,则dp[i]=dp[i-2]
否则不存在,无解
若s[i]='1’则dp[i]=dp[i-1]+dp[i-2]
因为这样会增加两种译码方法
一个是合在一起,也就是dp[i-2]
一个是分开译码,就是dp[i-1]
若s[i-1]=‘2’,并且s[i]在1~6之间,则同上dp[i]=dp[i-1]+dp[i-2]
因为dp[i]只和前两项有关,则我们使用单个变量代替创建整个dp数组就可以把空间复杂度从O(N)变为O(1)
func numDecodings(_ s: String) -> Int {
let len = s.count
if len == 0 || s[0] == "0" {return 0}
var pre = 1, cur = 1
for i in 1..<len { // 注意:// 这样写是错误的,越界 for i in 2..<len + 1 {
let temp = cur
if s[i] == "0" {
if s[i-1] == "1" || s[i-1] == "2" {
cur = pre
}else{
return 0
}
}else if s[i-1] == "1" || (s[i-1] == "2" && s[i] >= "1" && s[i] <= "6") {
cur = cur + pre
}
pre = temp
}
return cur
}
public int numDecodings(String s) {
int n = s.length();
if (n == 0 || s.charAt(0) == '0') return 0;
int[] dp = new int [n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i < n + 1; ++i) {
if (s.charAt(i - 2) == '1' || s.charAt(i - 2) == '2' && s.charAt(i - 1) <= '6')
dp[i] += dp[i - 2];
if (s.charAt(i - 1) != '0')
dp[i] += dp[i - 1];
}
return dp[n];
}
class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
// 先将 wordList 放到哈希表里,便于判断某个单词是否在 wordList 里
Set<String> wordSet = new HashSet<>(wordList);
if (wordSet.size() == 0 || !wordSet.contains(endWord)) {
return 0;
}
// 标准写法,总的 visited 数组
Set<String> visited = new HashSet<>();
// 分别用左边和右边扩散的哈希表代替单向 BFS 里的队列
Set<String> beginVisited = new HashSet<>();
beginVisited.add(beginWord);
Set<String> endVisited = new HashSet<>();
endVisited.add(endWord);
int len = beginWord.length();
int step = 1;
while (!beginVisited.isEmpty() && !endVisited.isEmpty()) {
// 打开以方便调试
// System.out.println("beginVisited => " + beginVisited);
// System.out.println(" endVisited => " + endVisited + "\n");
// 优先选择小的哈希表进行扩散,考虑到的情况更少
if (beginVisited.size() > endVisited.size()) {
Set<String> temp = beginVisited;
beginVisited = endVisited;
endVisited = temp;
}
// 逻辑到这里,保证 beginVisited 是相对较小的集合
// nextLevelVisited 在扩散完成以后,会成为新的 beginVisited
Set<String> nextLevelVisited = new HashSet<>();
for (String word : beginVisited) {
char[] charArray = word.toCharArray();
for (int i = 0; i < len; i++) {
char originChar = charArray[i];
for (char c = 'a'; c <= 'z'; c++) {
if (originChar == c) {
continue;
}
charArray[i] = c;
String nextWord = String.valueOf(charArray);
if (wordSet.contains(nextWord)) {
if (endVisited.contains(nextWord)) {
return step + 1;
}
if (!visited.contains(nextWord)) {
nextLevelVisited.add(nextWord);
visited.add(nextWord);
}
}
}
// 恢复,下次再用
charArray[i] = originChar;
}
}
// 这一行代表表示从 begin 这一侧向外扩散了一层
beginVisited = nextLevelVisited;
step++;
}
return 0;
}
public static void main(String[] args) {
List<String> wordList = new ArrayList<>();
String[] words = {"hot", "dot", "dog", "lot", "log", "cog"};
Collections.addAll(wordList, words);
Solution solution = new Solution();
String beginWord = "hit";
String endWord = "cog";
int ladderLength = solution.ladderLength(beginWord, endWord, wordList);
System.out.println(String.format("从 %s 到 %s 的最短转换序列的长度:%d。", beginWord, endWord, ladderLength));
}
}
简答题
下列叙述中正确的是
A.线性链表中的各元素在存储空间中的位置必须是连续的
B.线性链表中的表头元素一定存储在其他元素的前面
C.线性链表中的各元素在存储空间中的位置不一定是连续的,但表头元素一定存储在其他元素的前面
D.线性链表中的各元素在存储空间中的位置不一定是连续的,且各元素的存储顺序也是任意的
正确答案:D
解析:性表的链式存储结构中,各数据结点的存储序号不连续,且各结点在存储空间中的位置关系与逻辑关系也不一致。性链表中,各数据元素之间的前后件关系是由各结点的指针域来指示的。所以,选项D正确。将 A B C D 四个元素次序进栈(中间可能有出栈操作,例如 A 进栈后出栈,B 再进栈),则可能的出栈系列是
C A D B
B D C A
C D A B
D A B C
B为什么对。 A先压进去,再压入B,然后把B弹出来,所以B最先出栈。 然后把C压入栈,D再压入栈,根据先进先出原则依次弹出,所以剩下的三个数字出栈顺序为D,C,A。 所以总顺序为B,D,C,A
红黑树性质
1.每个结点要么是红的要么是黑的。
2.根结点是黑的。
3.每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
4.如果一个结点是红的,那么它的两个儿子都是黑的。
5.对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。布隆过滤器的定义和特性
一个很长的二进制向量和一系列随机映射函数.布隆过滤器可以用于检索一个元素是否在一个集合中
优点是空间效率和查询时间都远远超过一般的算法(模糊查询)
缺点是有一定的误识别率和删除困难在最坏的情况下,下列排序方法中时间复杂度最小的是: 对长度为n的线性表排序,冒泡排序、快速排序、直接插入排序的时间复杂度均为O(n^2),堆排序时间复杂度为O(nlogn),复杂度最小。

设一棵完全二叉树共有699个结点,则在该二叉树中的叶子结点数为多少个。
完全二叉树中如果全部节点数为奇数,说明含度为1的节点;设叶子节点数为no,度为2的节点数为n2,则有n0=n2+1;
2*n0 -1 = 699; n0=350
按从1到699编号,最后一个是699号,也就是最后一个叶子节点,设其对应的父节点为n,2n+1=699,n=349,其父节点是最后一个非叶子节点,所以叶子节点数为699-349=350
下列哪两个数据结构,同时具有较高的查找和删除性能?()
有序数组
有序链表
AVL树
Hash表
解析:几种常见的数据结构的操作, 平衡二叉树的查找,插入和删除性能都是O(logN),其中查找和删除性能较好;哈希表的查找、插入和删除性能都是O(1),都是最好的。
下面关于哈希查找的说法中,正确的是( )
哈希函数构造的越复杂越好,因为这样随机性好,冲突小
除留余数法是所有哈希函数中最好的
不存在特别好与坏的哈希函数,要视情况而定
若采用开放定址法处理冲突,则删除元素只需直接将该元素从哈希表中删去即可
答案:c 哈希函数都要视情况而选择,比如填装因子不同,表长变化,在哈希法时的哈希函数都不同。已知一个二叉树前序遍历结果ABCDEFGH和中序遍历结果CDBAGFEH
/*
A
B E
C F H
D G
*/
那么后续为DCBGFHEA
一个数组长度为n。指定数组内的一个元素,把数组内小等于该元素的,放在该元素左边,把数组内大于该元素的,放在该元素右边。这个需要多少次操作