冒泡排序
原理:比较两个相邻的元素,将值大的元素交换至右端。
思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
for (int e = arr.length - 1; e > 0; e--) {
for (int i = 0; i < e; i++) {
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
}
}
}
使用python实现,每一趟的长度都应该减少1,第一趟时要对比所有的数据,从arr[0]到arr[-1]进行对比,最后一次的arr[j+1]就是arr[-1],以后依次减少对比的长度。
for i in range(len(arr))[::-1]:
for j in range(i):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j + 1], arr[j]
插入排序
直接插入排序(Straight Insertion Sort)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
从后面的无序表中拿出一个数,先与最后有序表最后一个元素进行对比,如果arr[j] > arr[j + 1],就进行插入,否则不进行操作。
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
python实现,每次有序长度加1或者说每次都需要从无序列表中拿一个数据,进行插入对比是在有序里面进行的,第一次开始的时arr[j+1]就是最新加入的数据
for i in range(1, len(arr)):
for j in range( i-1)[::-1]:
if arr[j] > arr[j+1]:
arr[j] , arr[j+1] = arr[j+1] , arr[j]
选择排序
原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。
给定数组:int[] arr={里面n个数据};第1趟排序,在待排序数据arr[1]~ arr[n]中选出最小的数据,将它与arrr[1]交换;第2趟,在待排序数据arr[2]~ arr[n]中选出最小的数据,将它与r[2]交换;以此类推,第i趟在待排序数据arr[i]~ arr[n]中选出最小的数据,将它与r[i]交换,直到全部排序完成。
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
二分法使用递归查找最大值
使用堆的压栈和出栈理解递归函数,每次调用一次自己,就会把一个getmax(arr, L, R)的信息压入栈,这时会有多个getmax(arr, L, R)函数被压入栈,每个getmax(arr, L, R)有自己的独立信息,当L == R时,就会执行出栈操作,从上往下进行出栈操作。
getmax:是通过二分法取得左侧和右侧的最大值,之后进最大值之间的比较。
getmax(int [] arr, int L, int R){
if (L == R){
return arr[L];
}
int mid = (L + R) / 2;
int maxLeft = getmax(arr, L, mid);
int maxRight = getmax(arr, mid, R);
return Math.max(maxLeft, maxRight)
}
并归排序
使用递归进行二分法进行排序,把左侧和右侧单独排序,之后通过外派进行进行排序。外排就是使用两个指针,新建一个新数组,一个指向左侧第一个,一个指向右侧第一个,把两个指针的小值填入新数组,填入的指针加一。
解释:void mergeSort(int[] arr, int l, int r)就是二分法的递归函数把左侧和右侧进行排序
使用merge(int[] arr, int l, int m, int r)进行外派合并,每次压栈只对arr的[l, r]进行操作,其他地方不变。
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
mergeSort(arr, 0, arr.length - 1);
}
public static void mergeSort(int[] arr, int l, int r) {
if (l == r) {
return;
}
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
public static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
while (p1 <= m && p2 <= r) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}
荷兰国旗问题
输入一个数组arr和一个数字p,把小于p的数放左边,大于p的数据放右边,相等的放中间。
思路:设置一个less指针和more指针。less指向arr[0],more指向arr[-1],l从0-length滑动。如果arr[l] < p就 交换arr[less+1]和arr[l], 之后i = i +1,如果arr[l] > p就交换arr[more-1]和arr[l],重新判断i位置,相等时直接 i = i +1.
less + 1, more - 1 是arr[l] == p区间的位置
public static int[] partition(int[] arr, int l, int r, int p) {
int less = l - 1;
int more = r + 1;
while (l < more) {
if (arr[l] < p) {
swap(arr, ++less, l++);
} else if (arr[l] > p) {
swap(arr, --more, l);
} else {
l++;
}
}
return new int[] { less + 1, more - 1 };
}
int[] res = partition(test, 0, test.length - 1, 1);
python 实现荷兰国旗问题,这里与四个指针,left,right时原始的数组开始和结尾指针,less和more是在arr中小于和大于num的边界,less和more中间的值就是等于num的值。在排序过程中,less左右边的值必为小于num的值,在more右的值必为大于num的值,指针left每次都加1,但如果遇到大于num时,会把当前的值和一个未知大小的值arr[more-1]进行交换,所以需要重新判断一次arr[left]。
arr = [1,4,1,5,3,2,3,6]
def partition(arr, left, right, num):
less = left - 1
more = right + 1
while (left < more):
if arr[left] < num:
less += 1
arr[less],arr[left] = arr[left],arr[less]
left += 1
elif arr[left] > num:
more -= 1
arr[more],arr[left] = arr[left],arr[more]
else:
left += 1
print(arr)
return less + 1, more - 1
partition(arr,0, len(arr) - 1, 3)
快排
使用递归划分子集,给定一个arr,首先arr取中的最后一个数arr[-1]。把arr[l] < arr[-1]的数放左边,arr[l] > arr[-1]的数放右边,arr[l] == arr[-1]的数放中间。得到arr[l] == arr[-1]的边界p,p[0] - 1就是arr[l] < arr[-1]的数表,p[1] + 1就是arr[l] > arr[-1]的数表,继续进递归,中间arr[l] == arr[-1]的不需要动,递归操作的是l-r, 递归返回的条件就是arr[-1]就是整数组的最大值,这时l和p[0]相等,p[1]和r相等,或者p[0]==p[1]相等。
随机快排,由于数组本身就是有序数据,这时复杂度为最大,随机选取一个数放在arr[-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); // 由于最后一个数不参与划分直接,需要把r放入等于区间
return new int[] { less + 1, more };
}
通过把每个子数组的进行二分,把小于arr[-1]放左边,把大于arr[-1]的放右边,当数组中被切分的[left, right]最大值被放到最左边时,就可进行返回了。
import random
arr = [1,4,1,6,3,2,3,5,1]
def partition(arr, left, right):
less = left - 1
more = right
num = arr[right]
while (left < more):
if arr[left] < num:
less += 1
arr[less],arr[left] = arr[left],arr[less]
left += 1
elif arr[left] > num:
more -= 1
arr[more],arr[left] = arr[left],arr[more]
else:
left += 1
arr[more],arr[right] = arr[right],arr[more]
return arr,[less + 1, more]
def quiksort(arr, left, right):
if left < right:
rand = left + int(random.random() *(right - left + 1))
arr[rand],arr[right] = arr[right],arr[rand]
arr,equl = partition(arr,left, right, )
arr = quiksort(arr, left, equl[0]-1)
arr = quiksort(arr, equl[1]+1, right)
return arr
print(quiksort(arr,0, len(arr) - 1))
堆结构
把一个数组建一个大根堆结构。
i 左节点:2 * i + 1
i 右节点: 2 * i + 1
父结构: (i - 1) / 2
所有节点都不能越界。
完全二叉树:先补左节点,再补右节点
大根堆:完全二叉树,任何一个子树的最大值为子树头部。
小根堆:完全二叉树,任何一个子树的最小值为子树头部。对数组arr,每次添加一个数到堆里。
heapInsert(int[] arr, int index) 第index个数加到堆的操作:找到index的父节点,对比其中的最大值,如果arr[index] > arr[(index - 1) / 2],第index个数比父节点大,进行交换,之后与父父节点对比,直到index=0或者arr[index] <= arr[(index - 1) / 2].
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
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;
}
}
- 如果堆的值发生改变:
需要改变堆的结构,把小值进行下沉。
在一个数组中index的值发生改变,arr中size为堆的长度。
找到index的两个子节点中最大值largest,比较largest和index的小,如果largest <= index返回,否则交换swap(arr, largest, index),进入下一个子节点的比较
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;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
- 怎么进行减堆操作:
1.删掉最后值,2.弹出第一个值,把最后一个值放到第一个值,调整堆结构
对一个动态的数组建立一个堆,注意:数组的长度不固定,通过添加值的同时就可以保持数据的有序性。
通过建立一个大根堆和一个小根堆,两个堆列的size不能超过1,如果某一个num <= big[0]进行入大根堆,否则进入小根堆,size相差超过1,弹出头部,这时大根堆的头部为中位数。
堆排序
基于大根堆进行排序,因为每个大根堆的头部最大值是整个堆的最大值,通过把头部与尾部进行交换,size-1,进行对调整,循环调整。
首先建堆,把数组arr堆成堆arr,通过交换头部与尾部,再调整堆结构。
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int size = arr.length;
swap(arr, 0, --size);
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
栈结构
- 先入后出的一种数据结构,有push,pop,peek操作。
push:往栈结构中加入数据
pop:从栈结构中弹出数据,只弹出最新的数据,弹出的数据不属于栈结构
peek:返回栈的最后一个数据,并不删除数据
- 固定数组的栈实现:
public ArrayStack(int initSize) {
if (initSize < 0) {
throw new IllegalArgumentException("The init size is less than 0");
}
arr = new Integer[initSize];
size = 0;
}
public Integer peek() {
if (size == 0) {
return null;
}
return arr[size - 1];
}
public void push(int obj) {
if (size == arr.length) {
throw new ArrayIndexOutOfBoundsException("The queue is full");
}
arr[size++] = obj;
}
public Integer pop() {
if (size == 0) {
throw new ArrayIndexOutOfBoundsException("The queue is empty");
}
return arr[--size];
}
}
- 队列结构,先入先出。
使用数组设计一个固定长度的队列结构,队列有固定长度的size,设计两个指针first,last。
last:最新添加的数据的位置
first:弹出的数据的位置
push:往队列结构中加入数据
poll:从队列头中弹出数据,只弹出最早入队列的数据,弹出的数据不属于结构
peek:返回队列的第一个数据,并不删除数据
- 固定数组实现队列
public static class ArrayQueue {
private Integer[] arr;
private Integer size;
private Integer first;
private Integer last;
public ArrayQueue(int initSize) {
if (initSize < 0) {
throw new IllegalArgumentException("The init size is less than 0");
}
arr = new Integer[initSize];
size = 0;
first = 0;
last = 0;
}
public Integer peek() {
if (size == 0) {
return null;
}
return arr[first];
}
public void push(int obj) {
if (size == arr.length) {
throw new ArrayIndexOutOfBoundsException("The queue is full");
}
size++;
arr[last] = obj;
last = last == arr.length - 1 ? 0 : last + 1;
}
public Integer poll() {
if (size == 0) {
throw new ArrayIndexOutOfBoundsException("The queue is empty");
}
size--;
int tmp = first;
first = first == arr.length - 1 ? 0 : first + 1;
return arr[tmp];
}
}
例子:实现一个特殊的栈,在实现栈的结构,返回栈中的最小值
使用两个栈实现:
Data:正常的数据入栈
Min:第一次入栈数据和Data一样,之后,比较Min的栈顶与新数据的大小,把最小值放入min中,Min和Data的长度一样,Min和Data同步弹出,Min的栈顶就是Data中的最小值。例子:使用队列替换栈结构
队列是先进先出结构,栈是先进后出结构。
方法:使用时两个队列Queue1和Queue2,正常入栈放到Queue1,当要出栈,就把Queue1最后一个之前的数据放入Queue2,把最后一个数据弹出,弹出之后,新数据直接放到Queue2,这样交替进行,swap指向队列的指针。
public static class TwoQueuesStack {
private Queue queue;
private Queue help;
public TwoQueuesStack() {
queue = new LinkedList();
help = new LinkedList();
}
public void push(int pushInt) {
queue.add(pushInt);
}
public int peek() {
if (queue.isEmpty()) {
throw new RuntimeException("Stack is empty!");
}
while (queue.size() != 1) {
help.add(queue.poll());
}
int res = queue.poll();
help.add(res);
swap();
return res;
}
public int pop() {
if (queue.isEmpty()) {
throw new RuntimeException("Stack is empty!");
}
while (queue.size() > 1) {
help.add(queue.poll());
}
int res = queue.poll();
swap();
return res;
}
private void swap() {
Queue tmp = help;
help = queue;
queue = tmp;
}
}
- 例子:使用栈替换队列结构
队列是先进先出结构,栈是先进后出结构。
同样使用两个栈结构Stack1和Stack2,使用时往Stack1添加数据,第一次弹数据时,把Stack1数据全部放入Stack2,从Stack2弹出数据,之后把添加数据放入Stack1,Stack2一直弹数据,直到Stack2为空时再把Stack1倒入Stack2,所以Stack1一直是添加数据栈,Stack2存储的是最先入队的数据。
public static class TwoStacksQueue {
private Stack stackPush;
private Stack stackPop;
public TwoStacksQueue() {
stackPush = new Stack();
stackPop = new Stack();
}
public void push(int pushInt) {
stackPush.push(pushInt);
}
public int poll() {
if (stackPop.empty() && stackPush.empty()) {
throw new RuntimeException("Queue is empty!");
} else if (stackPop.empty()) {
while (!stackPush.empty()) {
stackPop.push(stackPush.pop());
}
}
return stackPop.pop();
}
public int peek() {
if (stackPop.empty() && stackPush.empty()) {
throw new RuntimeException("Queue is empty!");
} else if (stackPop.empty()) {
while (!stackPush.empty()) {
stackPop.push(stackPush.pop());
}
}
return stackPop.peek();
}
}
二叉树结构
有左右两个子节点left, right,同时函数有数据data
实现二叉树结构:
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
二叉树遍历一颗树每个节点会访问三次。
-
- 先序遍历
先打印节点,再打印左子树,之后为右子树
- 先序遍历
-
- 中序遍历
先打印左子树,再打印节点,之后为右子树
- 中序遍历
-
- 后序遍历
先打印左子树,在打印为右子树,之后打印节点
递归函数:
- 后序遍历
public static void preOrderRecur(Node head) {
if (head == null) {
return;
}
System.out.print(head.value + " ");
preOrderRecur(head.left);
preOrderRecur(head.right);
}
public static void inOrderRecur(Node head) {
if (head == null) {
return;
}
inOrderRecur(head.left);
System.out.print(head.value + " ");
inOrderRecur(head.right);
}
public static void posOrderRecur(Node head) {
if (head == null) {
return;
}
posOrderRecur(head.left);
posOrderRecur(head.right);
System.out.print(head.value + " ");
}
解递归函数:
- 前序遍历,可以通过栈实现,先把头部压栈,先弹打印当前节点,先压右节点,再压左节点,如果无子节点,进行弹出,弹出左节点进行打印,之后右节点。
public static void preOrderUnRecur(Node head) {
System.out.print("pre-order: ");
if (head != null) {
Stack stack = new Stack();
stack.add(head);
while (!stack.isEmpty()) {
head = stack.pop();
System.out.print(head.value + " ");
if (head.right != null) {
stack.push(head.right);
}
if (head.left != null) {
stack.push(head.left);
}
}
}
System.out.println();
}
2.中序遍历把每个节点的左节点压栈,直到遇到空,进行出栈进行弹出打印,找到同一节点的右节点,进行左节点压栈,循环。。。。
public static void inOrderUnRecur(Node head) {
System.out.print("in-order: ");
if (head != null) {
Stack stack = new Stack();
while (!stack.isEmpty() || head != null) {
if (head != null) {
stack.push(head);
head = head.left;
} else {
head = stack.pop();
System.out.print(head.value + " ");
head = head.right;
}
}
}
System.out.println();
}
- 后序遍历
使用前序遍历的逆序,使用两个栈,一个栈存头部,一个栈存打印节点,先压左节点,不进行打印而是进行压栈,再压右节点,不进行打印而是进行压栈。完全遍历之后进行打印。
public static void posOrderUnRecur1(Node head) {
System.out.print("pos-order: ");
if (head != null) {
Stack s1 = new Stack();
Stack s2 = new Stack();
s1.push(head);
while (!s1.isEmpty()) {
head = s1.pop();
s2.push(head);
if (head.left != null) {
s1.push(head.left);
}
if (head.right != null) {
s1.push(head.right);
}
}
while (!s2.isEmpty()) {
System.out.print(s2.pop().value + " ");
}
}
System.out.println();
}
- 树的序列化
前序的序列化
对每个子节点,先返回左节点,在返回右节点,如果遇到null,以"#!"代替
public static String serialByPre(Node head) {
if (head == null) {
return "#!";
}
String res = head.value + "!";
res += serialByPre(head.left);
res += serialByPre(head.right);
return res;
}
- 判断一个二叉树是否是平衡二叉树
平衡二叉树,左右两个边的高度相差不超过1。
- 左树平衡,右树平衡
- 左树的高度,右树的高度
递归函数每次返回数据结构是一样的,通过先判度是否平衡,再判断右树高度。
public static boolean isBalance(Node head) {
boolean[] res = new boolean[1];
res[0] = true;
getHeight(head, 1, res);
return res[0];
}
public static int getHeight(Node head, int level, boolean[] res) {
if (head == null) {
return level;
}
int lH = getHeight(head.left, level + 1, res);
if (!res[0]) {
return level;
}
int rH = getHeight(head.right, level + 1, res);
if (!res[0]) {
return level;
}
if (Math.abs(lH - rH) > 1) {
res[0] = false;
}
return Math.max(lH, rH);
}
- 搜索二叉树
对一颗树,左子节点的值比右子节点大。在中序遍历中,通过判断先前节点==当前节点。
- 判断完全二叉树
- 判断有右节点,无节点,为false
- 同一层中间层有缺节点
public static boolean isCBT(Node head) {
if (head == null) {
return true;
}
Queue queue = new LinkedList();
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)) {
return false;
}
if (l != null) {
queue.offer(l);
}
if (r != null) {
queue.offer(r);
} else {
leaf = true;
}
}
return true;
}
- 一知一颗完全二叉树,求其节点的个数
曼儿叉树节点个数L,h(高度):L = 2^(h) - 1
hash 函数
- hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另一段固定长度的数据h,作为这段数据的特征(指纹)。
- input ==无穷
- output == 有限
- input same == out same
- input not same == out same , hash碰撞
- input 无穷 == output 均匀分布
hash函数的输出值,每个位置上的值均是独立的。
- hash 表
一个矩阵中只有0,1两种值,每个位置都可以和自己的上下左右,四个位置相连,如果有一片1连在一起,这个部分叫做一个岛,求矩阵中有多少个岛。
0 | 1 | 1 | 0 | 1 | 0 |
---|---|---|---|---|---|
0 | 1 | 1 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 1 | 0 |
使用感染函数,把已经感染的值1的上下左右变成2,岛的数量加1,继续继续重新遍历,跳过0和2,循环遍历。
public static int countIslands(int[][] m) {
if (m == null || m[0] == null) {
return 0;
}
int N = m.length;
int M = m[0].length;
int res = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (m[i][j] == 1) {
res++;
infect(m, i, j, N, M);
}
}
}
return res;
}
public static void infect(int[][] m, int i, int j, int N, int M) {
if (i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1) {
return;
}
m[i][j] = 2;
infect(m, i + 1, j, N, M);
infect(m, i - 1, j, N, M);
infect(m, i, j + 1, N, M);
infect(m, i, j - 1, N, M);
}
集合
- 查找两个数是不是同一个集合:
通过进行集合的合并,给一个[1,2,3,4],每个数是自己的集合,[[1,], [2,], [3,], [4,]],如下,当把[1]和[2]进行合并时,把[2] 挂到[1]的集合上。
前缀树
在进行建树的过程中,把字母依次在树的路径上进行标记,如果某一条路径不存在,就建立一条路径。
使用前缀树进行字符的查找,构建方法insert, delete,search进前缀树的构建。
insert:是在数据树中插入数据,delete:删除数据,search:在树查找字符。
insert:沿着路径进行查找,当路径不存在时构建路径,如果以及存在path++
delete:删除路径上的数据,如果path==0,可以直接删除之后的结果,如果path!=0,需要删除对应数据。
search:通过word进行数据查找。
public static class TrieNode {
public int path;
public int end;
public TrieNode[] nexts;
public TrieNode() {
path = 0;
end = 0;
nexts = new TrieNode[26];
}
}
public static class Trie {
private TrieNode root;
public Trie() {
root = new TrieNode();
}
public void insert(String word) {
if (word == null) {
return;
}
char[] chs = word.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
node.nexts[index] = new TrieNode();
}
node = node.nexts[index];
node.path++;
}
node.end++;
}
public void delete(String word) {
if (search(word) != 0) {
char[] chs = word.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (--node.nexts[index].path == 0) {
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
public int search(String word) {
if (word == null) {
return 0;
}
char[] chs = word.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.end;
}
public int prefixNumber(String pre) {
if (pre == null) {
return 0;
}
char[] chs = pre.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.path;
}
}
贪心算法的排序
if str1 + str2 < str2 + str1,str2 > str1
切分金条
子节点合并在一起的是叶节点的和,怎么加最经济。
最大收益
有初始资金==w=10 ,有一组项目[[10,2],[12,3],[7,5],[,5,3],[13,10]]
[10, 2]:10为本金,2为利润
家里两个堆:
小根堆:按本金进行排放,最小自放在最上面,都是现在无法完成的项目,没钱
大根堆:按利润进行排放,最大的放上面,都是可以做的项目。
从大根堆开始做项目,每次做最上的一个项目,做完之后把小根堆可以做的项目添加到大根堆上,直到k次或者大根堆已经没有项目可做结束。
public static class MinCostComparator implements Comparator {
@Override
public int compare(Node o1, Node o2) {
return o1.c - o2.c;
}
}
public static class MaxProfitComparator implements Comparator {
@Override
public int compare(Node o1, Node o2) {
return o2.p - o1.p;
}
}
public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
Node[] nodes = new Node[Profits.length];
for (int i = 0; i < Profits.length; i++) {
nodes[i] = new Node(Profits[i], Capital[i]);
}
PriorityQueue minCostQ = new PriorityQueue<>(new MinCostComparator());
PriorityQueue maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
for (int i = 0; i < nodes.length; i++) {
minCostQ.add(nodes[i]);
}
for (int i = 0; i < k; i++) {
while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
maxProfitQ.add(minCostQ.poll());
}
if (maxProfitQ.isEmpty()) {
return W;
}
W += maxProfitQ.poll().p;
}
return W;
}
动态规划
n!
同过解依赖问题n! = n * (n-1)! = n*(n-1)...1!
通过
'abc'
打印‘abc’,打印所有字符组合。有三个位置,要a,不要a,要b,不要b,要c,不要c。2x2x2=8种
public static void printAllSubsquence(String str) {
char[] chs = str.toCharArray();
process(chs, 0);
}
public static void process(char[] chs, int i) {
if (i == chs.length) {
System.out.println(String.valueOf(chs));
return;
}
process(chs, i + 1);
char tmp = chs[i];
chs[i] = 0;
process(chs, i + 1);
chs[i] = tmp;
}
- 母牛每年生一只母牛,新出生的母牛成长三年后也能每年生一只 母牛,假设不会死。求N年后,母牛的数。
n-1年和n-3年的牛综合满足第n年的类型,需要列出所有的牛的总数,写出所有的初始项。
F(n) = F(n-1) + F(n-3)
public static int cowNumber1(int n) {
if (n < 1) {
return 0;
}
if (n == 1 || n == 2 || n == 3) {
return n;
}
return cowNumber1(n - 1) + cowNumber1(n - 3);
}
- 给你一个二维数组,二维数组中的每个数都是正数,要求从左上 角走到右下角,每一步只能向右或者向下。沿途经过的数字要累 加起来。返回最小的路径和
public static int process1(int[][] matrix, int i, int j) {
int res = matrix[i][j];
if (i == 0 && j == 0) {
return res;
}
if (i == 0 && j != 0) {
return res + process1(matrix, i, j - 1);
}
if (i != 0 && j == 0) {
return res + process1(matrix, i - 1, j);
}
return res + Math.min(process1(matrix, i, j - 1), process1(matrix, i - 1, j));
}
通过构建dp表,因为从(i,j)点到(n,n)的值固定,所以通过构建dp树可以进行dp通过解所有的dp表就可以解出最小值.
3 | 1 | 0 | 2 |
---|---|---|---|
4 | 3 | 2 | 1 |
5 | 2 | 1 | 0 |
bp表:
(i,j) | (i,j+1) | * | 3 |
---|---|---|---|
(i+1,j) | * | * | 1 |
8 | 3 | 1 | 0 |
通过化解递归,使用动态规划:
public static int minPath2(int[][] m) {
if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
return 0;
}
int row = m.length;
int col = m[0].length;
int[][] dp = new int[row][col];
dp[0][0] = m[0][0];
for (int i = 1; i < row; i++) {
dp[i][0] = dp[i - 1][0] + m[i][0];
}
for (int j = 1; j < col; j++) {
dp[0][j] = dp[0][j - 1] + m[0][j];
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + m[i][j];
}
}
return dp[row - 1][col - 1];
}
- 给你一个数组arr,和一个整数aim。如果可以任意选择arr中的 数字,能不能累加得到aim,返回true或者false2
在数组中,一个数字,有要和不要,如果要就加上这个数组,如果得到aim,返回true。
public static boolean money1(int[] arr, int aim) {
return process1(arr, 0, 0, aim);
}
public static boolean process1(int[] arr, int i, int sum, int aim) {
if (sum == aim) {
return true;
}
// sum != aim
if (i == arr.length) {
return false;
}
return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
}