1.二叉树
二叉树常用套路
树形dp套路
树形dp套路使用前提: 如果题目求解目标是S规则,则求解流程可以定成以每一个节点为头节点的子树在S规则下 的每一个答案,并且最终答案一定在其中
树形dp套路第一步: 以某个节点X为头节点的子树中,分析答案有哪些可能性,并且这种分析是以X的左子 树、X的右子树和X整棵树的角度来考虑可能性的
树形dp套路第二步: 根据第一步的可能性分析,列出所有需要的信息
树形dp套路第三步: 合并第二步的信息,对左树和右树提出同样的要求,并写出信息结构
树形dp套路第四步: 设计递归函数,递归函数是处理以X为头节点的情况下的答案。 包括设计递归的basecase,默认直接得到左树和右树的所有信息,以及把可能性做整 合,并且要返回第三步的信息结构这四个小步骤
★要有大局观的思维,把头节点设为x,然后再想想左子树和左子树要达到什么才能满足条件,再判断左右子树递归后需要返回什么给你
①判断是否是平衡二叉树
首先:设头节点为x,要想该树为平衡二叉树,左子树必须是平衡二叉树,右子树也必须是平衡二叉树,且左子树的高度-右子树的高度不能大于1
然后:我们想想那这样需要什么才能形成递归,头节点需要知道左右节点什么
a)知道左右子树是否为平衡二叉树
b)知道左右子树的高度
public class Code06_IsBalancedTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
//主函数
public static boolean isBalanced(Node head) {
return process(head).isBalanced;
}
//创建用于获取递归数据
public static class ReturnType {
public boolean isBalanced;
public int height;
public ReturnType(boolean isB, int hei) {
isBalanced = isB;
height = hei;
}
}
//递归方法
public static ReturnType process(Node x) {
if (x == null) {
return new ReturnType(true, 0);
}
//获取左子树的数据
ReturnType leftData = process(x.left);
//获取右子树的数据
ReturnType rightData = process(x.right);
//获取高度差
int height = Math.max(leftData.height, rightData.height);
//判断左右子树是否为平衡二叉树
boolean isBalanced = leftData.isBalanced && rightData.isBalanced
&& Math.abs(leftData.height - rightData.height) < 2;
//放回递归数据
return new ReturnType(isBalanced, height);
}
}
public static void process(Node node, LinkedList<Node> inOrderList) {
if (node == null) {
return;
}
process(node.left, inOrderList);
inOrderList.add(node);
process(node.right, inOrderList);
}
同种套路使用于搜索二叉树
首先:左子树必须的搜索二叉树,右子树也必须是搜索二叉树,左子树的最大值不大于头节点的值,右子树的最小值不小于头节点的值
然后:我们需要知道左右子树是否也为搜索二叉树,且我们需要知道左子树的最大值,右子树的最小值,我们需要取并集,故需要知道最大和最小值。
2.K算法
① 从最小的边开始查找,该边加上后判断有没有形成环状,没有则要这条边,有则不要这条边
②继续向下一条最小边进行查找,循环上步骤
难点:使用并查集来判断是否形成环状
3.P算法:
4.堆排序:
1,先让整个数组都变成大根堆结构,建立堆的过程:
1)从上到下的方法,时间复杂度为O(N*logN)
2)从下到上的方法,时间复杂度为O(N)
2,把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调 整堆,一直周而复始,时间复杂度为O(N*logN)
3,堆的大小减小成0之后,排序完成
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
//变成大根堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
//定义边界
int size = arr.length;
//将最大值和最小值进行交换
//size减少则,最大值已经定好了
swap(arr, 0, --size);
while (size > 0) {
//循环将头节点放到下面
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
public static void heapInsert(int[] arr, int index) {
//当前节点大于父节点,则一直向上进行交换
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) /2);
index = (index - 1)/2 ;
}
}
//通过顶端的数下降形成新的大根堆
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while (left < size) {
//left + 1为右节点,判断左右节点获取最大
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
//与其父节点进行比较大小
largest = arr[largest] > arr[index] ? largest : index;
//如果最大已经是index则不用再执行了
if (largest == index) {
break;
}
//否则交换该节点和其父节点的位置
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;
}
难点:把数组变为大根堆
荷兰国旗问题(快排partition的子问题):
思路:问题主要是将数分为三个部分,即等于1,大于1和小于1所以我们应该对此进行判断,我们对应创建两个区域,一个是左边小于1的区域,一个是右边大于1的区域
则主要步骤就慢慢出来了,对每一个数进行遍历,定义三个指针,两个分别指向最左和最右的位置,另一个指向当前数,遍历时进行数值判断,若该数
①小于1,则最左指针指向的数与该数进行交换,并且当前指针继续右移,左指针右移
②等于1,则继续右移即可
③大于1,则当前最右指针指向的数与该数进行交互,当前指针不需要右移动(因为是从头遍历,之前右指针所指向的数还未进行判断),右指针左移
public class Solution {
public void sortColors(int[] nums) {
int len = nums.length;
if (len < 2) {
return;
}
int zero = 0;
int two = len;
int i = 0;
while (i < two) {
if (nums[i] == 0) {
swap(nums, i, zero);
zero++;
i++;
} else if (nums[i] == 1) {
i++;
} else {
two--;
swap(nums, i, two);
}
}
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
5.快排:
1)在数组范围中,等概率随机选一个数作为划分值,然后把数组通过荷兰国旗问题分 成三个部分: 左侧划分值
2)对左侧范围和右侧范围,递归执行
3)时间复杂度为O(N*logN)
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int l, int r) {
if (l < r) {
//获取随机数
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0] - 1);
quickSort(arr, p[1] + 1, r);
}
}
public static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
while (l < more) {
if (arr[l] < arr[r]) {
swap(arr, ++less, l++);
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
} else {
l++;
}
}
swap(arr, more, r);
return new int[] { less + 1, more };
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
6.图的广搜
public static void bfs(Node node) {
if (node == null) {
return;
}
//广搜都用队列
Queue<Node> queue = new LinkedList<>();
//使用set判断,避免重复加入队列
HashSet<Node> map = new HashSet<>();
queue.add(node);
map.add(node);
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.println(cur.value);
//对当前节点的所有连通节点进行遍历
for (Node next : cur.nexts) {
//如果set里面还没有next则加入
if (!map.contains(next)) {
map.add(next);
queue.add(next);
}
}
}
}
7.图的深搜
public static void dfs(Node node) {
if (node == null) {
return;
}
//深搜使用栈
Stack<Node> stack = new Stack<>();
//同理,避免重复加入队列
HashSet<Node> set = new HashSet<>();
stack.add(node);
set.add(node);
System.out.println(node.value);
while (!stack.isEmpty()) {
Node cur = stack.pop();
//对当前节点的所有下一个节点进行遍历
for (Node next : cur.nexts) {
if (!set.contains(next)) {
//如果没有则把当前节点重新压入栈,再将此下一个节点叶压入
//则下一个遍历时是对next节点进行遍历,重复如下,则能实现深度遍历
stack.push(cur);
stack.push(next);
set.add(next);
System.out.println(next.value);
break;
}
}
}
}