排序算法和二叉树

master
kn5886348135 3 years ago
parent 7e2d22e6af
commit b59e7b4c4b

@ -32,3 +32,21 @@ private static void printCompleteBinaryStr(int num){
Integer.MIN_VALUE取反加一后仍然为Integer.MIN_VALUE这不是bug。
Integer.MAX_VALUE加1后溢出为Integer.MIN_VALUEInteger.MIN_VALUE减一则溢出为Integer.MAX_VALUE。
异或运算满足交换律和结合律
a^b=b^a
(a^b)^c=a^(b^c)
a、b交换
a=a^b
b=a^b
a=a^b
给定数组arr只有一个数出现了奇数次其它数都是偶数次找到并打印这个数。
偶数次异或为0奇数次异或为本身遍历每一个元素并异或最后结果就是奇数次的数字。
拿到一个数二进制最右侧的1
a&(~a+1)
给定数组arr只有一种数出现了K次其他数都出现M次。
假设a出现k次b出现m次
将所有数字的二进制位相加则t[i] % m就是a的二进制在i位置次数要么是0要么是k

@ -92,3 +92,41 @@ private static void insertSort(int[] arr){
}
}
```
不基于比较的排序也叫桶排序。对数据样本有要求但是特定场景可以做到O(N)时间复杂度基于比较的排序最好能做到O(N*logN)
计数排序 需要指定上下限
基数排序 非负、能用十进制理解的样本
排序的稳定性
稳定排序 冒泡、插入、归并
不稳定排序 选择、快排、堆排序、希尔
|序号|名称 |时间复杂度 |额外空间复杂度|稳定性|
|:----- |:----- |:-----|:-----|:-----|:-----| :-----| ----: | :----: |
|1|选择排序 |O(N^2^) |O(1) |无|
|2|冒泡排序 |O(N^2^) |O(1) |有|
|3|插入排序 |O(N^2^) |O(1) |有|
|4|归并排序 |O(N$\times$ $\log$N) |O(N) |有|
|5|随机快排 |O(N$\times$ $\log$N) |O($\log$N) |无|
|6|堆排序 |O(N$\times$ $\log$N) |O(1) |无|
|7|计数排序 |O(N) |O(M) |有|
|8|基数排序 |O(N) |O(N) |有|
|9|希尔排序 | | ||
* 不基于比较的排序,对样本数据有严格要求,不容易改写
* 基于比较的排序,只要规定好两个样本怎么比较大小就可以直接复用
* 基于比较的排序时间复杂度的极限是O(N$\times$ $\log$N)
* 时间复杂度O(N$\times$ $\log$N)、额外空间复杂度低于O(N)、且稳定的基于比较的排序是不存在的
* 为了绝对的速度选择快排、为了省空间选堆排序、为了稳定性选归并
工程上的改进
* 考虑稳定性Arrays.sort基础类型使用快排有比较器使用归并排序。
* 充分利用O(N$\times$ $\log$N)和O(N^2^)排序各自的优势O(N^2^)排序的常数项小,在数据样本不大(比如小于60)的时候是比O(N$\times$ $\log$N)要快的。
单链表回文
L1、L2、L3、L4、L5、L6、L7、R1、R2、R3、R4、R5、R6、R7变成L1、R7、L2、R6、L3、R5、L4、R4、L5、R3、L6、R2、L7、R1
单链表找中间节点
单链表荷兰国旗问题
给定表头将节点数据按照小于、等于、大于K连接

@ -116,3 +116,7 @@ private static boolean validateArr(int[] arr) {
return true;
}
```
小和问题、逆序对、大于两倍问题
区间和leetcode327 可以用有序表、归并解决
荷兰国旗问题

@ -18,7 +18,7 @@ private static int binarySearch(int[] arr, int target) {
int index = -1;
int middle;
while (left <= right) {
middle = (left + right) >> 1;
middle = left + ((right-left) >> 1);
if (arr[middle] == target) {
index = middle;
return index;
@ -42,7 +42,7 @@ private static int binarySearchLeft(int[] arr, int target) {
int index = -1;
int middle;
while (left <= right) {
middle = (left + right) >> 1;
middle = left + ((right-left) >> 1);
if (arr[middle] >= target) {
index = middle;
right = middle - 1;
@ -64,7 +64,7 @@ private static int binarySearchRight(int[] arr, int target) {
int index = -1;
int middle;
while (left <= right) {
middle = (left + right) >> 1;
middle = left + ((right-left) >> 1);
if (arr[middle] <= target) {
index = middle;
left = middle + 1;
@ -99,7 +99,8 @@ public int localMin(int[] arr){
int index = -1;
int middle;
while (left <= right) {
middle = (left + right) >> 1;
// 避免溢出
middle = left + ((right-left) >> 1);
if (arr[middle - 1] < arr[middle]) {
right = middle - 1;
} else if (arr[middle + 1] < arr[middle]) {

@ -35,7 +35,11 @@ private static Node reverDoubleLinkedList(Node head) {
}
```
把给定值都删除
单链表实现队列和栈
双链表实现队列和栈
数组实现队列和栈
### 使用双链表实现双端队列
@ -85,3 +89,16 @@ public class BitMap {
减法delete add(a,add(~b,1))取反加1后为负数。
乘法 a左移b的每一位进制的位数相加。
除法 先取绝对值a右移到刚好大于等于b拿到一个商最后所有商相加补上符号。对系统最小值特殊处理。
只用栈结构实现队列结构
只用队列结构实现栈结构
图的宽度优先搜索,使用队列实现,图的深度优先搜索,实现栈实现
栈和队列互换后可以实现
图的宽度优先搜索,使用栈实现,图的深度优先搜索,实现队列实现
不考虑时间复杂度
任何地柜都可以改成非递归方式实现。将系统压栈改为自定义实现,则可以避免递归。
master公式预估递归的时间复杂度要求子问题数据规模一样。
小和问题

@ -21,18 +21,28 @@
先序遍历的第一个元素是头结点先序遍历剩余部分左边是左节点的先序右边是右节点的先序中序遍历左边是左节点的中序右边是右节点的中序。递归构建二叉树递归函数入参为先序遍历的起止index中序遍历的起止index返回头结点。
### 二叉树按层遍历收集节点 LeetCode107 LeetCode102
#### 二叉树按层遍历收集节点 LeetCode107 LeetCode102
使用LinkedList(有序,快速插入),递归将每一层的节点入队、出队。
### 平衡二叉树 LeetCode110
#### 平衡二叉树 LeetCode110
左子树的高度和右子树的高度相差不超过1则称为平衡二叉树。
### 二叉搜索树 LeetCode98
#### 二叉搜索树 LeetCode98
递归解决
### 路径总和 LeetCode112
#### 路径总和 LeetCode112
### 达标路径总和 LeetCode113
#### 达标路径总和 LeetCode113
X是一棵二叉树的某一个节点A是二叉树先序遍历X的左边部分B是二叉树后序遍历X右边部分AB相交的结果是且仅是X的所有父节点。
1、X的所有父节点在先序遍历的左边X的所有父节点在后序遍历的右边交集一定包含所有父节点
2、X的所有子节点在先序遍历的左边X的所有子节点在后序遍历的左边交集一定不包含所有子节点
3、A不包含所有祖先节点的右兄弟节点B不包含所有祖先节点的左兄弟节点
X的所有祖先节点、X自己、X的子节点、X或者X的父节点作为左树的右兄节点、X或者X的父节点作为右树的左兄节点
判断二叉树是否是平衡二叉树
判断二叉树是否是搜索二叉树

@ -0,0 +1,66 @@
堆 大根堆、小根堆
完全二叉树的概念只有最下面一层,最右边当节点为空
最大线段重合问题
给定很多线段,每个线段都有两个数[start, end],表示线段开始位置和结束位置,左右都是闭区间
规定:
1线段的开始和结束位置一定都是整数值
2线段重合区域的长度必须>=1
返回线段最多重合区域中,包含了几条线段?
1、将所有线段按照start升序排序
2、构造堆保存当前重合所有重合线段的end, heap的size就是当前重合线段的数量
3、遍历所有线段如果出现不重合线段则弹出不重合的线段如果重合则加入end
手动改写堆(非常重要)!
系统提供的堆无法做到的事情:
1已经入堆的元素如果参与排序的指标方法变化系统提供的堆无法做到时间复杂度O(logN)调整都是O(N)的调整!
2系统提供的堆只能弹出堆顶做不到自由删除任何一个堆中的元素或者说无法在时间复杂度O(logN)内完成一定会高于O(logN)
根本原因:无反向索引表
手动改写堆的代码讲解
1建立反向索引表
2建立比较器
3核心在于各种结构相互配合非常容易出错
给定一个整型数组int[] arr和一个布尔类型数组boolean[] op
两个数组一定等长假设长度为Narr[i]表示客户编号op[i]表示客户操作
arr = [ 3 , 3 , 1 , 2, 1, 2, 5…
op = [ T , T, T, T, F, T, F…
依次表示3用户购买了一件商品3用户购买了一件商品1用户购买了一件商品2用户购买了一件商品1用户退货了一件商品2用户购买了一件商品5用户退货了一件商品…
一对arr[i]和op[i]就代表一个事件:
用户号为arr[i]op[i] == T就代表这个用户购买了一件商品
op[i] == F就代表这个用户退货了一件商品
现在你作为电商平台负责人,你想在每一个事件到来的时候,
都给购买次数最多的前K名用户颁奖。
所以每个事件发生后,你都需要一个得奖名单(得奖区)。
得奖系统的规则:
1如果某个用户购买商品数为0但是又发生了退货事件
则认为该事件无效得奖名单和上一个事件发生后一致例子中的5用户
2某用户发生购买商品事件购买商品数+1发生退货事件购买商品数-1
3每次都是最多K个用户得奖K也为传入的参数
如果根据全部规则得奖人数确实不够K个那就以不够的情况输出结果
4得奖系统分为得奖区和候选区任何用户只要购买数>0
一定在这两个区域中的一个
5购买数最大的前K名用户进入得奖区
在最初时如果得奖区没有到达K个用户那么新来的用户直接进入得奖区
6如果购买数不足以进入得奖区的用户进入候选区
7如果候选区购买数最多的用户已经足以进入得奖区
该用户就会替换得奖区中购买数最少的用户(大于才能替换),
如果得奖区中购买数最少的用户有多个,就替换最早进入得奖区的用户
如果候选区中购买数最多的用户有多个,机会会给最早进入候选区的用户
8候选区和得奖区是两套时间
因用户只会在其中一个区域,所以只会有一个区域的时间,另一个没有
从得奖区出来进入候选区的用户,得奖区时间删除,
进入候选区的时间就是当前事件的时间可以理解为arr[i]和op[i]中的i
从候选区出来进入得奖区的用户,候选区时间删除,
进入得奖区的时间就是当前事件的时间可以理解为arr[i]和op[i]中的i
9如果某用户购买数==0不管在哪个区域都离开区域时间删除
离开是指彻底离开,哪个区域也不会找到该用户
如果下次该用户又发生购买行为,产生>0的购买数
会再次根据之前规则回到某个区域中,进入区域的时间重记
请遍历arr数组和op数组遍历每一步输出一个得奖名单
public List<List<Integer>> topK (int[] arr, boolean[] op, int k)

@ -0,0 +1,2 @@
前缀树(prefix tree/trie tree)
前缀树是多叉树,便于处理字符串前缀相关的问题。
Loading…
Cancel
Save