算法篇
排序
比较的和非比较的排序
归并排序
分治思想的实现
merge过程
static void merge(int[] a, int L, int R) {
int help[] = new int[R - L + 1];//辅组数组
int m = (L + R) >> 1;
int p1 = L;//坑!!!写成了0 每次进来都是右边 指针一
int p2 = m + 1;//指针二
int i = 0;
while (p1 <= m && p2 <= R) {
help[i++] = a[p1] < a[p2] ? a[p1++] : a[p2++];//谁小复制谁
}
while (p1 <= m) {
help[i++] = a[p1++];
}
while (p2 <= R) {
help[i++] = a[p2++];
}
for (i = 0; i < help.length; i++) {
a[L + i] = help[i];//每次的起始位置是L
}
}
static void mergesort(int[] a) {
if (a == null || a.length < 2) {//边界条件
return;
}
mergesort(a, 0, a.length - 1);//进入递归函数
}
static void mergesort(int[] a, int l, int r) {
if (l >= r) {
return;
}
int m = (l + r) >> 1;
mergesort(a, l, m);//左边递归
mergesort(a, m + 1, r);//右边递归
merge(a, l, r);//类似外排合并
}
堆排序
堆结构
1.完全二叉树
2.每个节点子树上都比他的节点小或大
-
2*i+1
image.png
- heapinsert
- heapify
- java 实现 PriorityQueue
heap; - 比较器
static void heapsort(int[] a) {
if (a == null || a.length < 2) {
return;
}
for (int i = 0; i < a.length; i++) { //建堆
heapinsert(a, i);
}
int size = a.length;
swap(a, 0, --size);
while (size > 0) {//开始循环去把一个个数换到后面去
heapify(a, 0, size);
swap(a, 0, --size);
}
}
// heapify 下沉
static void heapify(int a[], int index, int heapsize) {
int left = index * 2 + 1;
while (left < heapsize) {
// 找左右孩子中最大的那个的下标
// int largest = left + 1 <=heapsize && a[left + 1] > a[left] ? left + 1 : left;
int largest = left + 1 < heapsize && a[left + 1] > a[left] ? left + 1 : left; //left 是下标 size 是数量== 情况不成立
// 找左右 index 中的下标
largest = a[index] > a[largest] ? index : largest;//坑!largest 写成了 left
if (largest == index) {//找到了最大 刚刚忘了!
break;
}
swap(a, index, largest);//刚忘了!
index = largest;
left = index * 2 + 1;
}
}
//上浮
static void heapinsert(int a[], int index) {
while (a[index] > a[(index - 1) / 2]) {//照顾了==0 也不进去循环
swap(a, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
快速排序
image.png
static int[] partition(int[] a, int l, int r) {
int less = l - 1;//小于区域
int more = r;//大于区域
while (l < more) { //l在变化 像一个指针 more 是大于区域
if (a[l] < a[r]) {//最后一个数是标准
swap(a, ++less, l++);
} else if (a[l] > a[r]) {
swap(a, --more, l);
} else {
l++;
}
}
swap(a, more, r);
return new int[]{less + 1, more}; //返回交换之后的等于的数组范围;
}
static void quicksort(int[] a) {
if (a == null && a.length < 2) {
return;
}
quicksort(a, 0, a.length - 1);
}
static void quicksort(int[] a, int l, int r) {
if (l < r) {
int[] ret = partition(a, l, r);
quicksort(a, 0, ret[0] - 1);
quicksort(a, ret[1] + 1, r);
}
}
image.png
暴力递归
暴力递归就是尝试
1,把问题转化为规模缩小了的同类问题的子问题
2,有明确的不需要继续进行递归的条件(base case)
3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解
一定要学会怎么去尝试,因为这是动态规划的基础
image.png
import java.net.DatagramPacket;
public class Code06_ConvertToLetterString {
public static int number(String str) {
if (str == null || str.length() == 0) {
return 0;
}
return process(str.toCharArray(), 0);
}
public static int process(char[] chs, int i) {
if (i == chs.length) {
return 1;
}
if (chs[i] == '0') {
return 0;
}
if (chs[i] == '1') {
int res = process(chs, i + 1);
if (i + 1 < chs.length) {
res += process(chs, i + 2);
}
return res;
}
if (chs[i] == '2') {
int res = process(chs, i + 1);
if (i + 1 < chs.length && (chs[i + 1] >= '0' && chs[i + 1] <= '6')) {
res += process(chs, i + 2);
}
return res;
}
return process(chs, i + 1);
}
public static int dpWay(String str) {
if (str.length() == 0 || str == null) {
return 0;
}
char[] chs = str.toCharArray();
int N = str.length();
int[] dp = new int[N + 1];
dp[N] = 1;
for (int i = N - 1; i >= 0; i--) {
if (chs[i] == '0') {
dp[i] = 0;
} else if (chs[i] == '1') {
dp[i] = dp[i + 1];
if (i + 1 < chs.length) {
dp[i] += dp[i + 2];
}
} else if (chs[i] == '2') {
dp[i] = dp[i + 1];
if (i + 1 < chs.length && (chs[i + 1] >= '0' && chs[i + 1] <= '6')) {
dp[i] += dp[i + 2];
}
} else {
dp[i] = dp[i + 1];
}
}
return dp[0];
}
public static void main(String[] args) {
System.out.println(number("11111"));
System.out.println(dpWay("11111"));
}
}
动态规划
动态规划(dynamic programming)是[运筹学]的一个分支,是求解决策过程(decision process)最优化的数学方法。数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
空间换时间 把上一次运行要的结果保存下来
动态规划的维度
空间的复用
//最大背包问题
// weight 数组 和价值数组 bag 包容量 使之价值最大
public class Code07_Knapsack {
public static int maxValue1(int[] weights, int[] values, int bag) {
return process1(weights, values, 0, 0, bag);
}
public static int process1(int[] weights, int[] values, int i, int alreadyweight, int bag) {//bag是剩下的容量
if (alreadyweight >= bag) { //basecase 1
return 0;
}
if (i == weights.length) {// 没有物品了
return 0;
}
int max = Math.max(
process1(weights, values, i + 1, alreadyweight, bag),//不要当前包
values[i] + process1(weights, values, i + 1, alreadyweight + weights[i], bag));//要当前包 只改'变了 alreadweight 的值
System.out.println("max="+max);
return max;
}
public static int maxValue2(int[] c, int[] p, int bag) {
int[][] dp = new int[c.length + 1][bag + 1];//长度是length bag
for (int i = c.length - 1; i >= 0; i--) {
for (int j = bag; j >= 0; j--) {
dp[i][j] = dp[i + 1][j];
if (j + c[i] <= bag) {
dp[i][j] = Math.max(dp[i][j], p[i] + dp[i + 1][j + c[i]]);
}
}
}
return dp[0][0];
}
public static void main(String[] args) {
int[] weights = {3, 2, 4, 7};
int[] values = {5, 6, 3, 19};
int bag = 11;
System.out.println(maxValue1(weights, values, bag));
System.out.println(maxValue2(weights, values, bag));
}
}
树形dp问题
- 可以向左树要信息 可以向右树要信息
- 用黑盒和拆黑盒 ReturnType
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,0).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,int height) {
if (x == null) {
return new ReturnType(true, height);//返回true 和得到的高度
}
ReturnType leftData = process(x.left,height+1);
ReturnType rightData = process(x.right,height+1);
height = Math.max(leftData.height, rightData.height);//黑盒 左右的最大高度
boolean isBalanced = leftData.isBalanced && rightData.isBalanced
&& Math.abs(leftData.height - rightData.height) < 2;// 左右是平衡 并且 左右高度 相差小于2
return new ReturnType(isBalanced, height);
}
滑动窗口和单调栈结构
滑动窗口
image.png
一个数组两个指针 L R
- L往右动
-
R往右动
就是窗口增大还是减小
找到窗口中的最大值/最小值(每次都去遍历 代价太高)
引入结构
双端队列
头------------尾
大------------小
存了一个作为最大值的可能性
image.png
单调栈结构
3 1 2 4 5 0 6
每个位置都找到下左右两侧离他最近的比他小的数
不优化 O(n^2) 的方法
单调栈 O(n)
image.png
套路为先
还有些比较重要的结构和思想
贪心 二叉树 非递归遍历 morris 遍历 哈希函数 有序表 并查集 大数据相关