算法与数据结构(1)

161 阅读7分钟

a # 认识复杂度,对数器,二分法与异或 程序=算法+数据结构

评估算法的优劣

  • 时间复杂度(流程决定)
  • 额外空间复杂度(流程决定)
  • 常数项时间(实现细节决定) 最细节的,一般省略

常数时间的操作

1656245616(1).png

非常数时间的操作

如查找链表

时间复杂度

常数时间的操作次数的最高阶量级 1656503692(1).png 1656503764(1).png

1657248597(1).png

额外空间复杂度

1656506621(1).png

最优解

b8c6057966e9c7980bc29e9390bcf88.png

对数器

1657248862(1).png

1657250654(1).png

二分法

1657253358(1).png 位运算比除运算快 同理

1657259651(1).png

1657259764(1).png

局部最小(无序情况下 使用二分)

只要可以构建出一种可以排掉一边的逻辑,就可以二分法

认识异或运算

72a48ebf86b17180229303a300dba46.png

1657262439(1).png

1657262592(1).png

java中进制转换

int a =2;
Integer.toString(a,2) //将a转为2进制

题目

  • 不用额外变量交换两个变量的值

1657262994(1).png

  • 数组中有一个数出现了奇数次, 其他的出现了偶数次,把他找出来 (使用异或) 8a0268902242052618171915db7c98f.png
  • 把int类型的数 提取出最右侧的1来

1657264470(1).png

1657264551(1).png

1657264650(1).png

1657265358(1).png

链表结构,栈,队列,递归行为,哈希表和有序表

1657506810(1).png

栈 队列

  • 双向链表实现栈 队列
  • 数组实现 队列是实现:循环数组

1657526138(1).png

  • 主栈弹出 min栈也跟着弹出 4a6a0d18633cfb9339ba8786c75d52c.png

1657590193(1).png

  • 题一 一个栈导入另一个栈就ok了 1657591177(1).png

  • 题二 把Data队列放入help,最后一个值返回用户,然后把help再放入Data中,如此循环往复 1657590275(1).png

递归

  • 对于某一类递归行为,时间复杂度可以直接写出来
    子问题的规模是一致的,这个子问题调用了a次 O(Nd)是子问题中的规模 df832ce131e97df477137c6402b4a02.png

1657595182(1).png

1657595788(1).png 确定复杂度公式 直接记!!! master公式

1657596689(1).png

哈希表

HasMap的特点

  • hasMap中(包装类,基础类型)按值传递 既按值比较,不比较内存
  • 非基础类型按引用传递 1657604179(1).png

1657605459(1).png

有序表

TreeMap 接口时间复杂度 O(logN)

1657606205(1).png 1657606051(1).png

归并排序

1657677037(1).png

  • 递归版
  • 时间复杂度:T(N)=2T(N/2)+O(N) ===>O(NlogN) 1657680194(1).png
  • 非递归版

1657682042(1).png 时间复杂度 1657682812(1).png 选择 冒泡 插入排序浪费了比较行为(前一次的比较不会影响后续的比较行为 ) 所以是O(N^2)

题目

1657685306(1).png

  • 如果每个数都向后找,则时间复杂度为O(N的平方)
  • 所以用归并排序优化
  • 左边比它小,等价于右边比他大

c342b18acd6d28c25067fc55438e265.png

image.png

快速排序

partition 小于----大于

0318fab57a95900f790f8522dc745ad.png

荷兰国旗问题 小于---等于----大于

image.png

快排1.0 (partition) 复杂度 o(N2)

3ff93e24e08704b6753ce8b068813e8.png

快排2.0 (荷兰国旗问题) o(N2)

7821f5e0d64ddb14ca38820bdc86503.png

快排 3.0 每次随机选一个数,放到最右侧进行partition

  • 每次partition后训练,左右两边的数的个数越均衡,效果越好 ebd99c258f4fc39a4efa6ea4a130e02.png

图片.png 在随机行为下 最差情况是概率时间 综合期望是 O(N*logn)

1657938415(1).png

使用数组表示堆,并求其节点

6cd9490e7c4d123de5b1653ee5feb2e.png c550ad6e10fbb646bfa07ccb101583a.png

大根堆

63756093d3b8c45e1e2b721e4ae4793.png

构建大根堆

  • 先放入末尾,之后再和其父亲节点比大小,大于父节点就交换 代价:O(logn) 761a2c56745b19c213bc2baed0e792a.png
  • 取出堆中最大值,并且保存堆的特性 1657949960(1).png 54d21d360e3522e94a67656349e9354.png

小根堆

同理

堆排序

  • 额外空间复杂度o(1) 时间复杂度O(N*logN)

631b7accbbeb36e8db42ce1a81b82c0.png

1657952992(1).png

建堆的复杂度

1658285549(1).png


heapInsert和heapify

heapInsert和父节点比 heapify和子节点比

系统通过的堆vs手写的堆

1657953254(1).png

  • **答:选择大小为K的小根堆进行堆排序,弹出一个进入一个 **
  • 复杂度O(n*logK) 1657953925(1).png

比较器

bbfc6ad56f0bb6b24b5b2ea5b6bbe8b.png

自定义排序

9022d1a93cdbf5c05fec3a618fb2111.png

84f5d690e110c3e31d7bbcb0646e6c4.png

特殊标准

d5067d3fa6230b7812a42d8d20989a0.png

手动写堆用来,用来满足一下需求

633006d1670c654741748e20e8490b5.png da2e4c464f6749e3296882f48b01a44.png

2ea1c4c4f9fef5218beedd7d8e53e08.png

3c65cea330dc75bde362243b1446785.png

47933e9bc4b950dc3db4d11980139ea.png

e1ba39efea5fd3707ec9ca9670bae08.png

8292cd7013b4a0713e0b6f4118f0319.png

bd4fd8b0792b2e5fc2eaebcc61fc10a.png

trie 桶排序 排序总结

前缀树

  • 解决前缀查询相关的问题 1658287324(1).png
  • 有路就走,没路就新建 比如加入gab,整条路都会是新的 5aa4114f86d7cd4ea4e14d3119ce6aa.png

前缀树的代码实现

  • 节点数据结构 1658542674(1).png
  • 插入 6c3319f54a4230d10231932d021c406.png
  • 查找带前缀的字符串的个数 1658544406(1).png
  • 删除字符串节点 1658545577(1).png
  • 方法二,使用hash表

1658547857(1).png

不基于比较的排序 桶排序(一种思想的统称)

1658562071(1).png

  • 计数排序 1658556245(1).png
  • 基数排序 前提:非负,十进制 O(N) 1658557252(1).png

1658557403(1).png

1658557461(1).png

1658557586(1).png

1658557649(1).png

基数排序代码实现

O(N)因为样本有规定量不大 实际上是O(Nlog已10为底的最大值的对数) Nlog已10为底的最大值的对数=====最大数有几位 dac582a465b4009d11a11654e139c41.png 597a76cc11ce3cc8ca14915b0232634.png

  • 不再用桶来逐个放入, 而是直接算出桶里面应该有多少个元素, 这样就直接知道排序后下标的位置了.也就不需要桶了. 284f88653b71992f081970e4730f9c2.png

1658628384727.png

排序算法的稳定性

  • 算法面对相等时的态度决定他是否稳定 1658628436221.png

稳定性的用途

1658629506126.png

1658631054314.png

20cd771e3d96e6c4cd01bc9bd0ba244.png

1658631275296.png

c54ff34ebeb36455b198da6dd3fe225.png

1658631588348.png

1658632344845.png

链表相关面试题

288a37eaa5c19a939749c31135dbcfa.png

快慢指针

重点即中间的点,偶数有两个中点 1661080524004.png

6f3b2f68e804bbc13f8d00dcf918b35.png

b7cbf6d23f16959018cbb4d330a9ce7.png

6af6978fda9b8378589ff6248dd06ee.png

方法二

34367704430a62c6c6746870deb5613.png

回文 adcda 栈

1661087304264.png

1661084811511.png

使用快慢指针优化

49875ceac6a98c491a6081c3ed2b63e.png

不使用额外空间

fe98633014941b6470ed76d1412cc78.png

e8f578dcfb5a3cb6c3b78b88a19f64c.png

5036a51ed2f9fced17895ceaaede50c.png

partition

c84a343addfa8ddae6e8bb48c05e624.png

方法一 荷兰国旗

1661087553963.png

方法二 使用6个指针

e61279ad9ee37b903e503164b121161.png

43c419bcde264c38c511f1937861a94.png

934a9f9c9358a851cf371502fdadc3a.png

d908028005d54110aade73e828e192a.png

常见题

076198dded2d61b3971a401c504ce3b.png

常用方法

题目中的链表结构 解决方法: hash表
7817782095b1efbffed4cf1b1123b6e.png **使用map存储原节点和被复制节点之后,拿出一对节点进行next和rand的复制,之后循环将所以节点全部复制完成。

1661763145071.png

特殊方法

这种方法代替了hash表 1661763629022.png 1661763953005.png 之后把大链表上把新节点分离出来

1e143b70fbc2d322f2a1c381119556e.png

image.png

常见题

a5a93513caa7ed5a526cce1751f3122.png

链表结构

1661765102479.png

如何判断链表是否有环,并且返回第一个入环节点

  • 方法一 直接使用set集合把遍历的点放入之后比对。
  • 方法二 快慢指针
    第一步:f走两步 s走一步 当s/f相遇的时候,f回到原点,s不动,之后f和s一次都走一步,再次相遇时,就是入环节点。 1661770647539.png

image.png

判断无环链表相交点

方法一 set集合 方法二 如果两个链表相交,则结尾一定为同一个点,表1从头节点到尾节点如果为100步 表二从头到尾长度如果为80 则表1先走20步之后两个表一起走并且不停的比对,第一个相同的点就是相交点

1661772170328_1D62483D-EB86-498a-BFCA-1D5DB75523C3.png

1661772422190_F75D6B74-BADB-485a-8949-FA770B11276F.png

image.png

题目剩余的情况

  • 两个链表都有环
  • 相交后入环(loop1==loop2) 找第一个相交点:先得出入环点相同,之后去掉入环点之后的节点 按照判断无环链表相交点的方法求
  • 相交但入环的点不是一个

image.png

情况1和情况三(loop1!=loop2)

image.png 情况1:链表遍历之后loop1和再次遇到自己缺没有遇到loop2 情况3:遍历过程中再次遇到loop1自己之前遇到loop2

1661773238025_E58E4D86-B799-406f-8DAB-9792E71DA9D6.png

题目主方法

image.png

面试题

image.png

答:如果想删掉一个节点必须要头节点

二叉树

093f72206329c0c7e6fd1a45a475658.png

子树

1661842667096.png

遍历

fad58b515a787468d1d0294441a5ec6.png

0f50c65a63373d3f18a82b9565aeb9a.png

1753ebb908160daa9303c27e2484569.png

递归序

1661843841414.png ce2c05dc7ca62602f1df8de233e187c.png

非递归遍历

2e87eba4cb5a4356486e3e4f6205353.png

先序

image.png 2d42ac7ab9d9d4cbaa132388a389704.png

后序

1661848618930.png 1661847507449.png

一个栈搞定

peek()函数返回栈顶的元素,但不弹出该栈顶元素。 h代表上次打印的节点,用来判断左右子树是否处理

1661850637495.png

中序

大方向是先左 后头 再右 fa09aadb5c3e6a07eb54271f40fbc91.png

按层遍历

c61589509fe6c6e54f631f789151b5f.png

5e77281ea7d2ffdaeb925bf3a266910.png

题目:判断二叉树哪一层节点数量最大

使用map

想要发现每一层的开始与结束 1661851682570.png

262e1a10cd415d126669592ccfa1c87.jpg

5edead524b7820d0138e3e19da81425.png

不使用map

并不关心,具体哪一层节点最多

image.png

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

c4fd8faddb950f2bab1c6433895756c.png

9b48f64ce048bcf014db13dee6a39ca.png 到时候根据序列进行还原即可

1618ec0527640180495c2e735a0481e.png

按层序列化

5629a5c8e95d62cd3c8ef1721d85f9a.png

a63137e8e0ede7d8840169ff57d8002.png 反序列化

1661860910864.png

629ea48f14025a7ec4cc53d995e109b.png

树的打印

5c23f570a7de3d6558cc27df4a435a2.png 先右再头再左

1f7102f6f7892b1b423a3d710b14809.jpg

b4806cc198915af6c4e04d1cfaef7ef.png

代码

cf1b96d80320c726f77f427e047f9a3.png

题目

后继节点:简单来讲就是在中序遍历中某节点的下一个节点。 整棵树最右的节点不会有后继 1661924614037.png

最简单解法(复杂度高) o(n)

向上早到头节点,之后中序遍历,再遍历中序序列找到要找节点的后继

低代价方法 o(k)

  • 如果一个节点有右孩子,右子树中最左的节点为其后继
  • 如果一个节点没用右孩子则 5bb445a364c85582b00ec77c79d70ed.png

6dfb53f0057394590353961eb9179a9.png

6b013031c4f366d6175627a18d7ad4e.png

前驱:中序序列的序列的前一个节点

题目

73dd60ab5921e357afcf9f6e07fea08.png ad36da41117ae8c9cc9d42b85fa1e34.jpg 627304fb7a570a1076b794be859c943.png

** 二叉树递归套路 *********

1661931689881.png

45a16cc6b8a497c9ac91b8a32d3043a.png

题目

1661931788774.png

思想

8a19ef4e0044a9b216d8c3f8d0ca9fa.png 需要的信息:左右树的高度,是否平衡?

1890e3fc2f001069eb23f15a7c6480e.png

a252dc436350979b4c08c4a8fb8d229.png

e279afe7de6b825c1b1a681dbe11f16.png