BAT面试算法基础学习笔记

排序

归并排序, 改变有序区间(从1开始)

快速排序 {}3

堆排序 取出最大值存入数组

希尔排序 以距离值 跳跳比

桶排序

计数排序
身高

基数排序 个位数 序列 十位数 百位数

时间O(N*N)
冒泡 选择

O(N*logN)
快速

空间复杂度
O(1)
插入 选择 冒泡 堆排序 希尔排序

但是如果使用的是递归实现的,空间复杂度将不再是O(1),而是O(logN) 函数栈

OlgN-O(N)
快速

43335 快速不稳定
5115 希尔排序不稳定

快速排序:常量系数低

如果没有空间复杂度的限制,用哈希表实现
哈希表实现,时间复杂度为O(N),空间复杂度为O(N)

O(1)
先排序

合并有序数组

从后往前拷贝,避免原有的数据被覆盖,比较和拷贝

荷兰国旗问题

看当前数
|

{} 1 1 0 0 2 1 1 0 {}
0区 2区

查找矩阵中的有序数

数组右上角开始

需要排序的最短子数组的长度

【1,5,4,3,2,6,7】
返回4 【5,4,3,2】
方法:从左往右记录遍历过的最大值位置,会发生一种情况,当前的元素比右边的数大,记录这种情况的最右边的位置
在从右往左,同理
最后最小和最大的位置区间就是要进行排序的区间长度。

数组中的最大差值

例如某数组排序之后为
1 2 3 4 7 8 9

最大差值来自于4和7,所以返回3

思想来自于桶排序

遍历数组

最小 最大 N区间 元素个数
N+1 为最大值
构造空桶
空桶区间 的最大差值即为最大差值

字符串

  1. 广泛性
    1.1 字符串数组 查找 排序
    1.2 字符串对象

  2. 需要掌握的概率

    • 回文
    • 子串(连续)
    • 子序列(不连续)
    • 前缀树
    • 后缀树和后缀数组
    • 匹配
    • 字典序
  3. 需要掌握的操作

    • 与数组有关的操作:增删改查
    • 字符串替换
    • 字符串的旋转

字符串题目的常见类型

  1. 规则判断

    • 判断字符串符合整数规则
    • 判断字符串符合浮点数规则
    • 判断字符串符合回文数规则 TRUE or false
  2. 数字运算

    int 和long 类型表达整数范围有限,所以经常用字符串实现大整数

    与大整数相关的加减乘除操作,需要模拟笔算的过程

  3. 与数组有关的排序有关类型

    • 排序
  4. 字符计数

    • 哈希表
    • 固定长度的数组
      C/C++ 字符256 java 65536

    • 滑动窗口问题、寻找无重复字符串子串问题、
      计算变位词问题

  5. 动态规划问题

    • 最大子串长度问题
    • 最大回文子序列
    • 最长公共子串
    • 最长公共子序列
  6. 搜索类型

    • 宽度优先搜索
    • 深度优先搜索
  7. 高级算法与数据结构解决的问题

    • Manacher算法解决最长回文子串问题
    • KMP 算法解决字符串匹配问题
    • 前缀树结构
    • 后缀树和后缀数组

比较两棵树,T1中包含T2的子树,返回TRUE,否则返回false

方法1,遍历二叉树+判断匹配 O(N*M)

方法2:二叉树序列化+ KMP算法 O(M+N)

实际为KMP比较字符串

变形词
str1=“123” str2=”231” 返回TRUE

str1=“123” str2=”2331” 返回False

方法:使用哈希表 str1 str2 判断记录是否一致
可以借用数组

旋转词

  1. 判断str1 与str2 是否相等
  2. 如果相等,生成str1+str1的大字符串
  3. KMP算法判断是否含有str2

str1=“1 2 3 4”
str1+str1=“ 1 2 3 4 1 2 3 4”

穷举了所有的旋转词

单词间逆序

  1. 实现将字符串逆序的函数f
  2. 利用f将字符串所有字符逆序
    “pig loves dog”->”god sevol gip“
  3. 找到逆序后的字符串中每一个单词的区域,利用f将每一个单词的区域逆序
    “god sevol gip”->”dog loves pig”

一个字符串str,和一个整数i,i代表str中的位置,将str[0..i]移动到右侧,str[i+1..N-1] 移到左侧

str=”ABCDE” ,i=2. 将str调整为“DEABC”
要求,时间复杂度为O(N),额外空间复杂度为O(1)

原地调整
分为两步
1. 将str[0..i] 部分的字符逆序
A B C D E

C B A D E

  1. 将str[i+1~N-1] 部分的字符串
    C B A D E

C B A E D

  1. 将str整体的字符逆序
    C B  A E D
    D E A B C

活用局部逆序函数

 拼接字符串,返回字典序列最小的

strs=[“abc”, “de”];
abcde deabc 返回abcde

strs==[“b”,”ba”]
bab bba 返回bab

O(N*logN)
实质是排序

如果str1+str2

空格替换 “%20”

长度变化的规律,从后往前拷贝

字符串的括号有效匹配

str=() true str(()()) true
str=()) false str=()( false
str=()a() false

  1. 遍历 ‘(’ ‘)’
  2. ‘(’ num++
  3. ‘)’ num–
  4. num<0 false;
    5 num==0 true 否则false
求字符串中,返回str最长无重复字符串的长度

str=“abcd” 返回4
str=”bacb” abc 返回3

哈希表 -> 统计之前每个字符的位置
整型pre->s[i-1]结尾的情况下,无重复的长度
位置A 位置B
c

栈和队列

1、 栈是先进后出

栈结构的基本
1.pop
2.top 访问不弹出
3.push
4.size

双端队列
优先级队列
优先级队列为堆结构,不是线性结构
深度优先遍历DFS 和宽度优先遍历 BFS

实现一个特殊的栈,在实现栈的基本功能的基础上,在实现返回栈中最小的元素的操作getmin.

要求:
1.pop、push 、getMin操作的时间复杂度都是O(1)
2. 设计的栈类型可以使用现成的栈结构

方法一、 StackData StackMin
1 2 1 5 4 3

压入条件是只有当前数小于等于StackMin的栈顶时,放入StackMin

弹出条件 相等StackMin

StackMin 都保存了当前的最小值 时间和空间不同

编写一个类,只能用两个栈结构实现队列,支持队列的基本操作(add,poll,peek)

StackPush StackPop 倒入

两个重要的注意点:
1.一次性全部导入
2. StackPop 压入之前需要为空

实现栈的逆序

// function: 移除栈底元素并返回

public int get(Stack stack){

    int result=stack.pop();
    if(stack.isEmpty()){
        return result;
    }else{
        int last=get(stack);
        stack.push(result);
        return last;    
    }
}

// function2 reverse
/*将栈中元素逆序*/
public void reverse(Stack<Interger> stack){

    if(stack.isEmpty()){
        return;
    }
    int i=get(stack);
    reverse(stack);
    stack.push(i);
}
一个栈中元素类型为整型,现在想将该栈从顶到底按从大到小的排序,只许申请一个栈,除此之外还可以申请新的变量,但不能申请额外的数据结构。如何完成排序?

Stack help

数组窗口滑动,返回最大值

方法:双端队列
4 3 5 4 3 3 6 7

qmax={},
1.如果qmax为空,直接把下标i放入qmax中

给定一个没有重复元素的数组arr,写出生成这个数组的MaxTree 的函数。要求如果数组长度为N,则时间复杂度为O(N)、额外空间复杂度为O(N)。MaxTree概念如下:
  1. MaxTree是一颗二叉树,数组的每一个值对应一个二叉树节点。
  2. 包括MaxTree数在内在其中的每一颗字数子树上,值最大的节点都是树的头。

3 4 5 1 2
左边第一个大的数 右边第一个大的数
3-> null 3->4
4->null 4->5
5->null 5->null
1->5 1->2
2->5 2->null

   5
  / \
 4   2
/     \

3 1

1.该方法可以生成一棵树,而不是森林
2.生成的这一棵树是二叉树,而不是多叉树

任何一个数在单独一侧,孩子的数量都不超过一个
….A…K1…K2
A>K1 且A>K2

利用栈获取左边比他大的第一个数
同理可得右边

链表

1.链表的分类 单链表 双链表
2.有环无环 循环链表

链表问题代码实现的关键点
1. 链表调整函数的返回值类型,根据要求往往是节点类型。
2. 思考哪些指针发生了改变,画图,查看修改的指针。
3. 边界(头节点、尾节点、空节点)

给定一个整数num,如何在节点值有序的环形链表中插入一个节点值为num的节点,并且保证这个环形单链表依然有序。

1、 如果原链表为空
2、 如果链表不为空
3、 转了一圈之后都没有发现插入的位置,此时node插入头节点的前面。
- 如果node节点的值都大于链表中的值,则直接返回head节点
- 如果node节点的值比head节点值小,则应该返回以此节点的链表

给定一个链表中的节点node,但不给定整个链表的头节点。如何在链表中删除node? 请实现这个函数,要求时间复杂度为O(1)。

前面不可见
1->2->3->null

节点删除不正确时的影响: 节点工程依赖,或者节点结构并没有真正的删除,只是节点的值拷贝

给定一个链表的头节点head,再给定一个数num,请把链表调整成节点值小于num的节点都放在链表的左边,值等于num的节点都放在链表的中间,值大于num的节点,都放在链表的右边。

简单做法:
1. 将链表的所有节点放入数组中,然后将数组进行快排划分
2. 把数组联系起来

最优解:
分解成3个链表,在连接起来

给定两个链表,求公共部分

1 3 4 5 ->null
2->3->4->null

给定k,逆序链表

方法一:
k=3
栈逆序

最后一次不足K个时,单独处理

第一组的特殊 各组的连接

方法二:
收集K个元素后逆序该组

删除链表中给定value的节点

7->1->3->1

方法1.构建链表

链表回文结构

链表1->2->3->2->1 是回文结构,返回TRUE
链表1->2->3->1 不是回文结构,返回false

方法一:时间O(n) 空间O(n)
遍历压入栈
弹出和遍历比对

方法二:时间O(n) O(N)/2

fast 每次走两步
slow

方法三:时间O(n) O(1)
找到中间节点,两边依依对比

rand节点 和next节点
判断一个链表是否有环,如果有的话返回链表节点,如果没有返回null,如果链表的长度为N,请做到时间复杂度为O(N),额外空间复杂度为O(1)。

哈希表普通做法
第一个节点重复时,返回即可,否则为空

快慢指针
有环会相遇 相遇时,快慢各走一步,再次相遇,返回
快->null 则无环

相交节点

遍历两个链表的长度,长的先走,再同步走,如果相交,则一定会返回

判断环是否相交,相交返回第一个节点,不相交返回null,如果两个链表的长度分别为N和M,请做到时间复杂度O(N+M),额外空间复杂度O(1)。
  1. 找到入环节点
给定两个单链表的头结点head1和head2,如何判断两个链表是否相交?相交的话返回第一个相交的节点,不相交的话,返回空。

方法:
1. 判断单链表是否有环
2. 如果node1和node2,一个为空,另一个不为空,返回空

二分搜索

  1. 在有序序列中查找一个数,时间复杂度为O(logN)

arr : ——-mid ———m

二分搜索常见的考察点

  1. 循环每次减一半
  2. 判断条件不同 等于X的位置,大于或小于X的位置
  3. 返回内容不同

二分搜索的重要提醒:
mid=(left+right)/2

mid=left+(right-left)/2 //更好的写法防止溢出

无序arr,返回任意一个局部最小的位置,任意相邻值不重复

二分搜索

时间O(logN)
1. arr为空或者长度为0,返回-1,表示不存在
2. 如果arr 长度为1,返回0,
3. 如果arr长度大于1

给定一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置

1 2 3 3 3 3 4 4 4 4 4 4 4 4 4
num=3
res=-1 // 如果没有找到num=3,直接返回-1
res=2; // 下标 数组

有序循环数组arr

arr[L]<=arr[R]

arr[L]>arr[M]

给定一个有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。

res=-1;
arr:
下标: arr[0]>N-1 返回-1
arr[N]<0 返回-1

arr[M]>M
arr[M]

给定一颗完全二叉树的头节点head,返回这棵树的节点个数。如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。

最优解:二分搜索
1. 找到二叉树的最左节点 得到高度
2. 找到右子树的最左子孩子 找完全二叉树

如果更快的求一个整数K的N次方。如果两个整数相乘并得到结果的时间复杂度为O(1),得到整数K的N次方的过程,请实现时间复杂度为O(logN)的方法。

10^75=10^1001011
=10^64*10^8*10^2*10^1

二叉树

二叉树的结构

class Node{
    int value;
    Node left;
    Node right;
    Node(int data){
        this.value=data;
    }
}

先序
中序
后序

栈 ,先压入右孩子
栈, 中序 先压入左孩子,发现为空时,弹栈

非递归的后序实现
方法一:使用两个栈实现
1.申请一个栈,记为s1,然后将头节点压入S1
2. 从S1中弹出的节点记为cur,然后先把cur的左孩子压入s1中,然后把cur1的右孩子压入S1中
3. 在整个过程中,每一个从s1中弹出的节点都放在第二栈s2中。
4. 不断重复步骤2和步骤3,直到s1为空,过程停止。
5、 从s2中一次弹出节点并打印,打印的顺序就是后序遍历的顺序了。

方法二: 使用一个栈

二叉树的按层打印

队列:

打印二叉树
按层,连同行号打印

last:当前行的最右节点
nlast :下一行的最右节点

换行的条件为: nlast==last->next;

二叉树的序列化和反序列化

  1. 二叉树转换为文件的记录的过程,称为序列化
  2. 将文件记录信息转换为二叉树的过程,称为反序列化

结束符!#

反序列化
生成一个数组,values={“12”,”3”,”#”,”#”}

二叉树的子树

平衡二叉树(AVL树)

判断一颗树是否为平衡二叉树

LH-LR 是否大于1

搜索二叉树

搜索二叉树的中序一定是从小到大的。

满二叉树

完全二叉树

最后一层没有满

最优解法:

二叉树节点之间的距离

算术运算符和位运算符

位运算

不安全黑名单100亿个黑名单URL 哈希表或数据库

布隆过滤器精确 代表一个集合
bitarray
k个哈希函数 (优秀且K个哈希函数各自独立)

每一个位置为一个bit
0:白 1: 黑

大小为m,样本数量为n,失误率为p
n=100亿

如何确定bitarray 的大小

布隆过滤器的过程
1. 失误率
2. 确定样本个数n

如何不用额外变量,交换

a=a^b;
b=a^b;
a=a^b;

给定两个32位整数a和b,返回a和b中较大的,但是不能任何比较判断。

方法二:
避免溢出
public static int geMax2(int a,int b){
int c=a-b;
// a的符号,as==1表示a为非负,as==0表示a为负
int as=sign(a);
// b的符号,bs==1表示b为非负,bs==0表示b为负
int cs=sign(c); // a-b 的符号
// 表示a和b是否符号不相同,不相同为1,相同为0
int difab=as^bs;
//表示a和b是否符号相同,相同为1,不相同为0
int sameab=flip(difab);
int returnA=difab*as+sameab*cs;
int returnB=flip(returnA);
return a*returnA+b*returnB;
}

给定一个整型数组arr,其中只有一个数出现了奇数次,其他的数出现了偶数次,请打印这个数。要求时间复杂度为O(N),额外空间复杂度为O(1)。

n与0异或 结果为n
n与n异或结果为0

eo=0; arr=[C,B,D,A,A,B,C]

1.异或运算满足交换律
2. 结合律

给定一个整型数组arr,其中有两个数出现了奇数次,其他的数出现了偶数次,请打印这两 个数。要求时间复杂度为O(N),额外空间复杂度为O(1)。
  1. n与0异或 结果为n
  2. n与n异或结果为0

对Text加密和解密

异或运算可完成简单的加密和解密

排列组合

概率组合

  1. 古典概率计算
  2. 斐波那契数列和卡特兰数
  3. 3.

6X9的方格中,以左上角为起点,右下角为终点,每次只能向下走或者向右走,请问一共有多少种不同的走法。

13
c13 5 c13 8

ABCDEFG 七人站队,要求A必须在B的左边,但不要求相邻,请问共有多少种排法?第二问如果要求A必须在B的左边,并且一定相邻,请问一共有多少种排法?

第一问: 不相邻的时候

7! 一半在A的左边
7!/2

第二问:
6!

六个人排成一排,要求甲与乙不相邻,并且甲与丙不相邻的排法数是多少?

方法一:

6!=720

甲 乙 丙 丁
甲乙看成一个人
5!+5!

乙甲丙 4!X 2=

甲在开头末尾

C3 2 X 3!

C3 2 X 3! X4

10颗相同的糖果,分给3个人,每人至少一颗,问有多少种分法。

0 0 0 | 0 0 0 0 | 0 0 0

10颗糖果中间的位置为9个
所以目标转换为9个空隙中选两个插入隔板
C9 2=36种

10颗不同的球,放到3个不同的桶里,问有多少种分法。

球和桶不同
因为每个球都有3种可能
3^10 =59049

有10颗趟,如果每天至少吃一颗,吃完为止,问有多少种吃法?

一天 1 种
C9 1 两天
三天 C9 2

2^9=512

假设有n对左右括号,请求出合法的排列有多少个?合法是指每一个括号都可以找到预支配对的括号,比如n=1是,()合法的,但是)(为不合法。

(-> 1
) ->-1

n+1个1 和n-1个-1组成的排列

不合法的排列数=n+1

左括号数量为n,右括号数量为n,总排列数为C2n n
c2n n - C2n n+1 = 1/n+1 *C2n n

n个数进出栈的顺序有多少种?假设栈的容量无限大。再补充一个问题,2n个人排队买票,n个人拿5块钱,n个人拿10块钱,票价是5块钱1张,每个人买一张票,售票员手里没有零钱,问有多少种排队方法让售票员可以顺利卖票。

进栈(
出栈)
C2n n - C2n n+1=1/n+1*C2n n
10 5
C2n n - C2n n+1=1/n+1*C2n n

求n个无差别的节点构成的二叉树有多少种不同的结构?

假设n个无差别的结构不同结构数为f(n)
1 1 5

f(0)=1
1/n+1*C2n n

12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?

卡特兰数

0:在第一排
1:在第二排

0 0 0 0 1 1 0 1 0 1 1 1


|

前四个人在第一排
5 6 个人在第二排
。。。

如果前缀1比0个数多的哈,说明必然会出现不合法的情况。

问题变为任意 前缀不能出现1比0多的情况,依然是卡特兰数问题

有n个信封,包含n封信,现在把信拿出来,再装回去,要求每封信不能装回它原来的信封,问有多少种装法

递归:
f(n)
n>2

n - i个信封
情况一: i 封 放入了n个信封中,后续为f(n-2)
情况二:第i封信没放入n个信封中,后续 为f(n-1)

f(n)=(n-1)*[f(n-1))+f(n-2)]

概率问题

  1. 客观题
  2. 组合数学 分子和分母的组合数学

概率的应用

  1. 利用随机来改进著名算法 (快速排序)
  2. 随机数放生器(用给定的随机数)

8只球队,有3个强队,其都是弱队,随机把它们分成4组比赛,每组两个队,问两强相遇的概率是多大?

  1. 首先求出8只球队分成4组比赛的方法数

一组 二组 三组 四组

7 x 5 x 3 x 1 =105

  1. 没有两强相遇的方法数

O O O 3 个强队
O O O O O 5 个弱队

在5只弱队中选出3只与强队配对,剩下的2只自行配对。

C5 3 X A3 3=60;
从5只弱队中 3只强队和3只弱队
选出3只弱队 彼此配对的方法数
的方法数

  1. 求两强不相遇的方法数为:
    (105-60)/ 105 =3/7

三只蚂蚁从正三角形的三个顶点沿着边移动,速度是相同的,问它们碰头的概率是多少?

如果方向并不都相同,则一定会相遇

每只蚂蚁方向数有2种,一共有3只蚂蚁
所以总的情况有2^3=8种

只有完全顺时针和完全逆时针这两种情况下不相遇。

所以相遇的概率为 (8-2)/8=3/4

假设某地区重男轻女,一个家庭如果生出一个女孩就一直生,知道生出男孩就停止生育。假设一胎只出生一个孩子,问时间足够长后,男女比例会变为多少?

假设这一地区共有n个家庭
假设n/2 第一胎就是男孩为,所以只有1个孩子
有n/4 的家庭先生出1女孩,再生1男孩,有2个孩子
有n/8的家庭先生2个女孩,再生1个男孩,有3个孩子

孩子的总数为:
n/2+(n/4)*2+(n/8)*3+(n/16)*4…= 2*n

每个家庭都会有一个男孩,所以2n的孩子中,男孩数为n,所以女孩数也为n.
所以比例依然为1:1

给定一个等概率随机产生1-5的随机函数,除此之外,不能使用任何额外的随机机制,请实现等概率随机产生1~7的随机函数

1、已经有等概率随机产生 :1 2 3 4 5
第二步:第一步-1 0 1 2 3 4 为f()
3. f()*5 0 5 10 15 20
4. f()*5+f() 1 2 3 4…24
5. 重复4,产生0~20
6. 0~20%7 可以产生3个0~6,然后+1 产生0~7的随机函数

给定一个以p概率产生0,以1-p概率产生1的随机函数f(),p是固定的值,但你并不知道是多少,除此之外也不能用任何额外的随机机制,请用f()实现等概率随机产生0和1的随机函数。

f() 产生0 p
产生 1 1-p
产生01和10序列的概率都为pX(1-p)
不断调用f,指导能够产生01或10,序列终止。

如果为01 返回0
如果为10 返回1

给定一个长度为N且没有重复元素的数组arr和一个整数M,实现函数等概率随机打印arr中的M个数。

arr 0 00 0 0 0 0 0
1 2 3 4 5…
0~N-1中得打印一个随机数
之后将打印过的数放到最后,避免重复打印,并且减小了打印的范围。

等概率随机常与最后交换。

机器吐出自然数球

你可能感兴趣的:(【算法分析】)