没有合适的资源?快使用搜索试试~ 我知道了~
剑指offer剑指offer
资源推荐
资源详情
资源评论
快速排序: 快排的常数项很低, 因为每一步partition过程操作数量很低, 即它的常数项很
低, 所以快速排序在工程上是最被经常使用的排序算法.
空间复杂度: 在整个函数中, 整个函数没返回前所需要的空间叫空间复杂度, 某一部分子过程
的变量空间在整个函数返回前已经释放了, 就不用算作整个的空间复杂度了.
二叉树题目的套路: (
这些题目有着统一的名字
:
叫做树形
DP) ---> 树形DP都是利用后
序遍历?
一系列二叉树的问题 (求整棵树的******…), 统一的处理逻辑就是: 假设以每一个节点为头
的这棵树, 它的最大搜索二叉子树是什么
如果我们求出来了以每一个节点为头的最大二叉搜索子树, 答案一定在其中.
相当于, 一开始进入第一层递归时就已经拿到了下一层的节点的黑盒(看成已知数), 然后根
据这个黑盒进行拆数据并整理成新数据, 并把新数据整理成本层节点的黑盒, 最后返回至最外
层中.
//这种动态规划比在数组中、和矩阵中做动态规划更为方便: 因为遍历顺序就决定了先遍历子
树, 再遍历父树, 所以它的计算顺序都高度固定了.
//判断完全二叉树:
//Deque是一个双端链表, 双向队列。正常的队列是从头进, 从尾出; 而Deque是头尾都能进
出。Deque和Queue是应用中的叫法, 底层都可以用双端链表来实现. 同理, 优先级队列和堆
也是一个东西. 用数组可以实现一个堆, 用二叉树也可以实现堆(code很难写, 需要处理的边
界特别多). 但二叉树实现堆的好处是不会浪费空间, 没有扩容代价(但数组实现堆就有
了). 此问题在《程序员代码面试指南》中有涉及到.
public class Solution {
public static boolean isCBT(Node head) {
if (head == null) {
return true;
}
Queue<Node> queue = new LinkedList<Node>();
boolean leaf = false;
Node l = null;
Node r = null;
queue.offer(head);
while (!queue.isEmpty()) {
head = queue.poll();
l = head.left;
r = head.right;
if ((leaf && (l != null || r != null)) || (l == null && r != null)) { //这一个if
语句,两种判断标准就都包含了进去。 合二为一了
return false;
}
if (l != null) {
queue.offer(l); //宽度优先遍历,加入左节点
}
if (r != null) {
queue.offer(r); //宽度优先遍历,加入右节点
} else { //即r == null,(即包含了l != null && r == null和l == null && r == null两
种情况), 所以为了简便, 直接写else(即r == null)即可。
leaf = true;
}
}
return true;
}
}
//完全二叉树节点的个数
//先遍历整棵树的左边界, 得到整棵树的高度; 然后, 每次拿到一个节点, 只判断它的 右子
树的 左边界 到没到 整棵树的 最后一层.
//设置一个全局变量 ”h”, 代表整棵树的高度.
//时间复杂度分析: 递归遍历中, 每一层遍历只会遍历左右节点中的其中一个, 即O(logN);
遍历每一层的一个节点(左或右)时, 会再遍历这个节点的右子树的左边界, 还是O(logN)。
因此整个算法的时间复杂度为O(log
2
N)。非常低的时间复杂度。时间复杂度远低于O(N)!!!
public class Solution {
public static int nodeNum(Node head) {
if (head == null) {
return 0;
}
return bs(head, 1, mostLeftLevel(head, 1));
}
public static int bs(Node node, int level, int h) {
if (level == h) { //递归结束点, 即当前节点的高度已经到达了最后一层
return 1; //
注意
,
即使是别的写法,这里必须要直接
return 1;
否则会继续向下
计算一次
,
导致结果错误
!
}
if (mostLeftLevel(node.right, level + 1) == h) { //每一次递归都是判断右子树的
左边界的层数是否等于(到达了)整棵树的高度
return (1 << (h - level)) + bs(node.right, level + 1, h); //即2 ^ (h - l) - 1
+ 1.
} else {
return (1 << (h - level - 1)) + bs(node.left, level + 1, h);
}
}
public static int mostLeftLevel(Node node, int level) {
while (node != null) {
level++;
node = node.left;
}
return level - 1;
}
}
//堆排序: (不需要额外空间, 所以空间复杂度是O(1))
//堆中最重要的两个过程: heapInsert和heapify分别对应着某一个位置往上走和某一个位
置往下走的过程.
public class Solution {
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i); //给定一个数组, 依次把i位置上的数字加进来, 让它 0 ~ i 之间形成
大根堆.
}
int size = arr.length;
swap(arr, 0, --size);
while (size > 0) {
heapify(arr, 0, size); //堆排序过程: heapInsert, 换 ---> 减heapSize ---> heapify, 调
swap(arr, 0, --size);
}
}
public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) { //这一行: 兼顾了i位置的元素可以往前跳; 以
及跳到了0位置时(即arr[0] == arr[(0 - 1) / 2]).两种情况下都是对的!
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
//每加入一个新节点时, 新节点的调整代价 (即将数组重新调整为逻辑上的大根堆结构)为log(i - 1),
则所有的节点(0 ~ N) 的节点的调整代价就为log(1) + log(2) + log(3) + ... + log(N-1) = O(N),
//即建立一个大根堆的时间复杂度是O(N)!! 一定要记好!
//任何 “一次” 从堆中 加入或者弹出 操作的调整操作都是log(N)的. 这是一个时间复杂度非
常低的操作 !!!
//所以, 再次地, 堆结构是一个非常重要的结构! (其原理就是利用每次调整只需要调整 “一个堆上的
高度” 的数据, 其他数据都不需要动.) 所以非常好用
}
}
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while (left < size) {
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//1.
先选出两个孩子中较大值的那一个下标
largest = arr[largest] > arr[index] ? largest : index;//2. 再比较左右两个孩子之间和我
的值哪个大, 哪个就作为largest的下标
if (largest == index) {
break;
//减堆 操作: 如果想弹出堆顶元素, 只要把堆的最后一个数占领堆顶的位置, 然后”把 标记越界
(即heapSize的值 -1) 用的变量 -1”, 然后从数组0位置开始经历一个heapify过程, 这样就又恢复成大 (小)根堆
了.
}
//能发生这一步的潜台词是: 某个孩子比我大, 那个孩子的位置是largest
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
//桶排序相关:
(1).桶排序 (计数排序)(注: 仅适用于0~200范围内的数!)
public class Solution {
public static void bucketSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int[] bucket = new int[max + 1];
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++; //注意, bucket桶里装的是数组中每个元素出现的次数
}
int len = 0;
for (int i = 0; i < bucket.length; i++) {
while (bucket[i]-- > 0) {
arr[len++] = i; //注意!! bucket桶里装的是数组中每个元素出现的次数
}
}
}
}
(2). 数组中最大相邻数差值问题
public class Solution {
public static int maxGap(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
int len = arr.length;
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
min = Math.min(min, arr[i]);
max = Math.max(max, arr[i]);
}
if (max == min) {
return 0;
}
int[] mins = new int[len + 1];
int[] maxs = new int[len + 1];
boolean[] flag = new boolean[len + 1];
int bid = 0;
for (int i = 0; i < len; i++) {
bid = bucket(arr[i], len, min, max);
mins[bid] = flag[bid] ? Math.min(mins[bid], arr[i]) : arr[i];
maxs[bid] = flag[bid] ? Math.max(maxs[bid], arr[i]) : arr[i];
flag[bid] = true;
}
int i = 1;
int lastMax = maxs[0];
int res = 0;
for (i = 1; i <= len; i++) {
if (flag[i] == true) {
res = Math.max(res, mins[i] - lastMax);
lastMax = maxs[i];
}
}
return res;
}
剩余62页未读,继续阅读
资源评论
qq_40849626
- 粉丝: 6
- 资源: 15
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- yolo目标检测项目实验
- downloadFile-1.hc
- Centos7.9环境下离线安装开源版Nginx(亲测版)
- C++课程设计:基于Qt的航班信息管理系统
- ADS7822UVerilog驱动,前面传的有点问题
- 基于python的高性能爬虫程序,使用了多线程+缓存+xpath实现的,这里以彼-岸图库为例,实现,仅用于学习交流
- 中分辨率成像光谱仪(MODIS)烧毁面积产品信息MODIS-C6-BA-User-Guide-1.2.pdf
- Screenshot_20240427_172613_com.huawei.browser.jpg
- 关于学习Python的相关资源网站链接及相关介绍.docx
- (HAL库)基于STM32F103C8T6的温控PID系统[Dht11、ESP8266、无线透传、L298N……]
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功