1. 查找最大相同子串
描述:
先假设长度小的字符串为最大相同子串,在长字符串匹配是够能够匹配到;若存在,则返回小字符串;不存在,则将小字符串长度减 1,再匹配,依次类推。
public String getMaxSameStr(String str1, String str2){
if(str1 != null && str2 != null){
String maxStr = str1.length() >= str2.length() ? str1 : str2;
String minStr = str1.length() < str2.length() ? str1 : str2;
int len = minStr.length();
for(int i = 0; i < len; i++){
for(int x = 0, y = len - i; y <= len; x++, y++){
String subStr = minStr.substring(x, y);
if(maxStr.contains(subStr)){
return subStr;
}
}
}
}
return null;
}
2. 0-1背包问题1
描述:
一个背包最多可以放8公斤的物品,放入如下物品使得背包价值最大
物品1:6公斤 ---- 价值48元
物品2:1公斤 ---- 价值07元
物品3:5公斤 ---- 价值40元
物品4:2公斤 ---- 价值12元
物品5:1公斤 ---- 价值08元
思路:
--------- 0 1 2 3 4 5 6 7 8
6 -48 0 0 0 0 0 0 48 48 48
1 - 7 0 7 7 7 7 7 48 55 55
5 -40 0 7 7 7 7 40 48 55 55
2 -12 0 7 12 19 19 40 48 55 60
1 - 8 0 8 15 20 27 40 48 56 63
public static void main(String[] args) {
// 定义一个数组a[i][j] = a[i] i表示当前物品的序号选上,j表示这个位置能得到的最大值
// 选或者不选动态规划
Scanner scn = new Scanner(System.in);
int[] w = new int[6];// 表示每件物品的重量
int[] v = new int[6];// 表示每件物品的价值
for (int i = 1; i < 6; i++) {
w[i] = scn.nextInt();// 输入重量
v[i] = scn.nextInt();// 输入价值
}
int[][] temp = new int[6][9]; // 8表示背包最多能放8公斤的重量
for (int j = 0; j < 9; j++) { // 初始化每一行
temp[0][j] = 0;
}
for (int i = 1; i < 6; i++) { // 背包的重量为0的时候,最大价值肯定是0
temp[i][0] = 0;
}
for (int i = 1; i < 6; i++) { // 从第一个物品开始选,记录我选了前面出现的物品,背包重量从1-8的能选上的最大的值
for (int j = 1; j < 9; j++) { // 当i循环到最后一层5的时候,也就是得到了,我5件物品都选上的时候的最大的值
if (w[i] <= j) { // 重量比这个状态小,那么就能放。 那么就只是放与不放,看是放重量大,还是不放重量大
temp[i][j] = Math.max(temp[i - 1][j], temp[i - 1][j - w[i]] + v[i]);
} else {
temp[i][j] = temp[i - 1][j];// 第i件物品不能放
}
}
}
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 9; j++) {
System.out.printf("%-5d", temp[i][j]);
}
System.out.println();
}
}
3. 0-1背包问题2
描述:
1.
* 5座金矿,每座金矿的储量不同,需要参与挖矿的工人数也不同。
* 参与挖矿的总工人数是10,每座金矿要么全挖,要么不挖,不能
* 派一半人挖一半矿。求应选择挖哪几座金矿,才能得到尽可能多的黄金,(求能挖的最多黄金)。
* 金矿数据:1:500金/5人; 2:400/5; 3:300/4; 4:200/3; 5:350/3
* 动态规划的过程如下:
* 重量(工人数): 0 1 2 3 4 5 6 7 8 9 10
*
* 物品1(金矿1) 0 0 0 0 0 500 500 500 500 500 500
* 物品2(金矿2) 0 0 0 0 0 500 500 500 500 500 900
* 物品3(金矿3) 0 0 0 0 300 500 500 500 500 800 900
* 物品4(金矿4) 0 0 0 200 300 500 500 500 700 800 900
* 物品5(金矿5) 0 0 0 350 350 500 550 650 850 850 900
public static void main(String[] args) {
int weight = 10; // 工人数量(就是背包问题中的背包重量)
int count = 5; // 金矿数量(物品数量)
int[] weights = { 5, 5, 4, 3, 3 }; // 每个金矿需要的人手(每件物品的重量)
int[] costs = { 500, 400, 300, 200, 350 }; // 每个金矿的价值(每件物品的价值)
System.out.println(dp(weight, count, weights, costs));
System.out.println(dp2(weight, count, weights, costs));
}
// 01背包问题模板
// 打表,自底向上的求解问题,从小问题开始着手。时间复杂度 O(weight * count), 空间复杂度 O(weight);
public static int dp(int weight, int count, int[] weights, int[] costs) {
int[] preLine = new int[weight + 1];
int[] curLine = new int[weight + 1];
// 先处理第一行
for (int i = 0; i <= weight; i++)
if (weights[0] <= i)
preLine[i] = costs[0];
// 基于上一行处理当前行,然后迭代,pre变cur
for (int i = 1; i < count; i++) {
for (int j = 0; j <= weight; j++)
if (weights[i] <= j)
curLine[j] = Math.max(preLine[j], costs[i] + preLine[j - weights[i]]);
else
curLine[j] = preLine[j];
preLine = curLine.clone();
}
return curLine[curLine.length - 1];
}
// 精简后的代码
public static int dp2(int weight, int count, int[] weights, int[] costs) {
int[] preLine = new int[weight + 1];
int[] curLine = new int[weight + 1];
for (int i = 0; i < count; i++) {
for (int j = 0; j <= weight; j++)
if (weights[i] <= j)
curLine[j] = Math.max(preLine[j], costs[i] + preLine[j - weights[i]]);
preLine = curLine.clone();
}
return curLine[curLine.length - 1];
}
4. 手写 LinkedList
1. 创建节点类
public class DoubleNode {
DoubleNode previous;//上一个节点
DoubleNode next;//下一个节点
Object element;//元素数据
public DoubleNode getPrevious() {
return previous;
}
public void setPrevious(DoubleNode previous) {
this.previous = previous;
}
public DoubleNode getNext() {
return next;
}
public void setNext(DoubleNode next) {
this.next = next;
}
public Object getElement() {
return element;
}
public void setElement(Object element) {
this.element = element;
}
@Override
public String toString() {
return "Node [previous=" + previous + ", next=" + next + ", element=" + element + "]";
}
public DoubleNode(DoubleNode previous, DoubleNode next, Object element) {
super();
this.previous = previous;
this.next = next;
this.element = element;
}
public DoubleNode(Object element) {
super();
this.element = element;
}
public DoubleNode() {
super();
}
}
2. 实现基本方法
public class DoubleLinkedList<T> {
private DoubleNode first;//第一个节点(指针)
private DoubleNode last; //最后一个节点
private int size; //大小
//索引合法检查、
private void check(int index) {
if(index < 0 || index > size - 1) {
throw new RuntimeException("索引不合法:" + index);
}
}
//根据索引返回节点
private DoubleNode getNode(int index) {
check(index);
DoubleNode temp = null;
if(index <= (size >> 1)) { //优化,i < size/2 从头开始找
temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next; //将temp指向下一个节点
}
}else {
temp = last;
for(int i = size -1; i > index; i--) {//优化,i > size/2 从尾开始找
temp = temp.previous; //将temp指向上一个节点
}
}
return temp;
}
//重写toString
public String toString() {
StringBuilder sb = new StringBuilder("[");
DoubleNode temp = first;//将first赋给临时变量
while(temp != null) {
sb.append(temp.element + ",");//打印元素的element
temp = temp.next;//将临时变量指向下一个元素
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
//add方法:往链表后面追加元素
public void add(T t) {
//创建一个新节点
DoubleNode node = new DoubleNode(t);
if(first == null) {//第一个元素:第一个元素和最后一个元素都是他本身;上一个节点和下一个节点都为空
first = node;
last = node;
node.previous = null;
node.next = null;
}else {
node.previous = last;//当前(新节点)节点的上一个节点为最后一个元素
node.next = null;//新节点的下一个节点为空
last.next = node;//将最后一个元素的下一个节点指向新元素
last = node;//并且将新元素设置为最后一个节点
}
size++;
}
//add方法:在指定位置插入元素
public void add(int index, T t) {
check(index);
DoubleNode newNode = new DoubleNode(t);//要插入的新节点
DoubleNode temp = getNode(index);//要插入的位置的节点
if(temp != null) {
DoubleNode up = temp.previous;//得到要插入元素的上一个元素
if(up != null) {
up.next = newNode;//将要插入元素的上一个元素的下一个节点指向新元素
newNode.previous = up;
}
newNode.next = temp;
temp.previous = newNode;//将要插入元素的下一个元素的上一个节点指向新元素
}
if(index == 0) {//在第一个位置插入
first = newNode ;
}
if(index == size -1) {//在最后一个位置插入
last = newNode;
}
size++;
}
//get方法
@SuppressWarnings("unchecked")
public T get(int index) {
check(index);
DoubleNode temp = getNode(index);
return temp != null ? (T)temp.element : null;
}
//remove方法
public void remove(int index) {
check(index);
DoubleNode temp = getNode(index);//获得要删除的节点
if(temp != null) {
DoubleNode up = temp.previous;//得到要删除元素的上一个元素
DoubleNode down = temp.next; //得到要删除元素的下一个元素
if(up != null) {
up.next = down;//将要删除元素的上一个元素的下一个节点指向要删除元素的下一个元素
}
if(down != null) {
down.previous = up;//将要删除元素的下一个元素的上一个节点指向要删除元素的上一个元素
}
if(index == 0) {
first = down;//要删除第一个元素,将指针指向第二个元素
}
if(index == size - 1) {
last = up;//要删除最后一个元素,将指针指向倒数第二个元素
}
size--;
}
}
public int size() {
return size;
}
// 测试
public static void main(String[] args) {
DoubleLinkedList<String> sll = new DoubleLinkedList<>();
sll.add("A");
sll.add("B");
sll.add("C");
sll.add("D");
System.out.println(sll);
sll.add(3,"YYY");
System.out.println(sll);
System.out.println(sll.size);
}
}
5. 手写 Map
1. 创建 Map 的节点类
/**
1. Map节点类:
2. hash:哈希值
3. key:键
4. value:值
5. next:下一节点
6. */
public class MapNode<K, V> {
public int hash;//哈希值
public K key;
public V value;
public MapNode next;
public MapNode() {
super();
}
}
2. 实现基本方法
public class MyHashMap<K, V> {
MapNode<K,V>[] table;// 存放Map节点的数组
int size;// 大小
public MyHashMap() {
table = new MapNode[16];// 初始化数组,默认长度为16
}
// 计算哈希值的方法
public int myHash(int hashCode, int len) {
return hashCode % (len - 1); // hashCode & (len - 1)//效率比较高
}
public String toString() {
StringBuilder sb = new StringBuilder("{");
for (int i = 0; i < table.length; i++) {// 遍历数组
MapNode<K,V> temp = table[i];
while (temp != null) {// 遍历链表
sb.append(temp.key + ":" + temp.value + ",");
temp = temp.next;// 指向下一个节点,继续遍历
}
}
sb.setCharAt(sb.length() - 1, '}');
return sb.toString();
}
public void put(K key, V value) {
// 创建一个节点
MapNode<K,V> node = new MapNode<K,V>();
// 赋值
node.key = key;
node.value = value;
// 计算哈希值
node.hash = myHash(key.hashCode(), table.length);
MapNode<K,V> temp = table[node.hash];// 得到节点哈希值对应的数组索引
MapNode<K,V> last = null;// 正在遍历的最后一个元素
boolean b = false;
if (temp == null) {// 为空说明第一次在该位置存放
table[node.hash] = node;// 直接放进去
size++;
} else {
// 不为空,遍历该索引处的链表,key重复则替换对应的value,否则在链表末尾添加进去
while (temp != null) {
// key重复,覆盖
if (temp.key.equals(key)) {
temp.value = value;
b = true;// 有重复,将b置为true
break;// 跳出循环
} else {// 不重复,
last = temp;// 将last记录一下
temp = temp.next;// 继续找下一个节点,直到temp空
}
}
if (!b) {// 没有重复,在末尾添加
last.next = node;
size++;
}
}
}
public V get(K key) {
// 根据key计算哈希值
int hash = myHash(key.hashCode(), table.length);
V value = null;
// 根据哈希值查找key
if (table[hash] != null) {
MapNode<K,V> temp = table[hash];// 取到哈希值对应的第一个节点
while (temp != null) {// 一次往下遍历
if (temp.key.equals(key)) {// 找到了对应的key
value = temp.value;// 返回其值
break;
}else {// 没找到,继续往下找
temp = temp.next;
}
}
}
return (V) value;
}
public int size() {
return size;
}
// 测试
public static void main(String[] args) {
MyHashMap<Integer, String> shm = new MyHashMap<Integer, String>();
shm.put(1, "AAA");
shm.put(2, "BBB");
shm.put(3, "CCC");
shm.put(4, "DDD");
shm.put(4, "DDD");
System.out.println(shm);
System.out.println(shm.size);
System.out.println(shm.get(4));
}
}
6. 实现一个二叉树
1. 创建节点类
public class Node {
public Object value;//值
public Node leftChild;//左子树
public Node rightChild;//右子树
public Node() {
}
public Node(Object value) {
super();
this.value = value;
}
public Node(Object value, Node leftChild, Node rightChild) {
super();
this.value = value;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
@Override
public String toString() {
return "Node [value=" + value + ", leftChild=" + leftChild + ", rightChild=" + rightChild + "]";
}
}
2. 创建树类,实现其基本方法
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
public class Tree {
private Node root;//根节点
public Tree() {
}
public Tree(Node root) {
super();
this.root = root;
}
public boolean isEmpty() {
return root == null;
}
public int size() {
System.out.print("节点个数:");
return this.size(root);
}
private int size(Node root) {
if(root == null) {
return 0;
}else {
//获取左子树节点个数
int nl = this.size(root.leftChild);
//获取右子树节点个数
int nr = this.size(root.rightChild);
//返回两者之和 + 1
return nl + nr + 1;
}
}
public int getHeight() {//递归实现
return this.getHeight(root);
}
private int getHeight(Node root) {
if(root == null) {
return 0;
}else {
// 获取左子树高度
int nl = this.getHeight(root.leftChild);
// 获取右子树高度
int nr = this.getHeight(root.rightChild);
// 返回其中较大一个 + 1
return nl > nr ? (nl + 1) : (nr + 1);
}
}
public Node findKey(int value) {
return this.findKey(value, root);
}
private Node findKey(Object value,Node root) {
if(root == null){//递归结束条件1:结点为空,可能是整个树的根节点,也可能是递归调用中叶子节点中左孩子和右孩子
return null;
}else if(root != null && root.value == value){//递归的结束条件2:找到了
return root;
}else {//递归体
Node node1 = this.findKey(value, root.leftChild);
Node node2 = this.findKey(value, root.rightChild);
if(node1 != null && node1.value == value){
return node1;
}else if(node2 != null && node2.value == value){
return node2;
}else{
return null;
}
}
}
public void preOrderTraverse() {//先序遍历
if(root != null) {
//输出根节点的值
System.out.print(root.value + " ");
//创建一个以左子树为根的树
Tree leftTree = new Tree(root.leftChild);
leftTree.preOrderTraverse();
//创建一个以右子树为根的树
Tree rightTree = new Tree(root.rightChild);
rightTree.preOrderTraverse();
}
}
public void inOrderTraverse() {//中序遍历
System.out.println();
System.out.print("中序遍历:");
this.inOrderTraverse(root);
}
private void inOrderTraverse(Node root) {//中序遍历
if(root != null) {
//遍历左子树
this.inOrderTraverse(root.leftChild);
//输出根节点
System.out.print(root.value + " ");
//遍历右子树
this.inOrderTraverse(root.rightChild);
}
}
public void postOrderTraverse() {//后续遍历
System.out.println();
System.out.print("后序遍历:");
this.postOrderTraverse(root);
}
public void postOrderTraverse(Node root) {//后续遍历
if(root != null) {
//遍历左子树
this.postOrderTraverse(root.leftChild);
//遍历右子树
this.postOrderTraverse(root.rightChild);
//输出根节点
System.out.print(root.value + " ");
}
}
public void inOrderByStack() {
System.out.println("中序非递归遍历:");
// 创建栈
Deque<Node> stack = new LinkedList<Node>();
Node current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
stack.push(current);
current = current.leftChild;
}
if (!stack.isEmpty()) {
current = stack.pop();
System.out.print(current.value + " ");
current = current.rightChild;
}
}
System.out.println();
}
//层次遍历
public void levelOrderByStack() {
System.out.println("按照层次遍历二叉树");
if(root == null)
return;
Queue<Node> queue = new LinkedList<Node>() ;//队列
queue.add(root);
while(queue.size() != 0){
for(int i = 0; i < queue.size(); i++){
Node temp = queue.poll();
System.out.print(temp.value+" ");
if(temp.leftChild != null)
queue.add(temp.leftChild);
if(temp.rightChild != null)
queue.add(temp.rightChild);
}
}
System.out.println();
}
}
3. 测试类
public class Test {
public static void main(String[] args) {
//1、创建一个二叉树
Node node5 = new Node(5, null, null);//值, 左子树, 右子树
Node node3 = new Node(3, null, null);
Node node7 = new Node(7, null, null);
Node node4 = new Node(4, null, node5);
Node node6 = new Node(6, null, node7);
Node node2 = new Node(2, node3, node6);
Node node1 = new Node(1, node4, node2);
Tree tree = new Tree(node1);//指定树的根节点
//2、先序遍历(递归)
System.out.print("先序遍历:");
tree.preOrderTraverse();
//3、中序遍历(递归)
tree.inOrderTraverse();
//4、后序遍历(递归)
tree.postOrderTraverse();
//5、树的高度
System.out.println(tree.getHeight());
//6、树的节点个数
System.out.println(tree.size());
//7、层次遍历(非递归)
tree.levelOrderByStack();
//8、中序遍历(非递归)
tree.inOrderByStack();
}
}
7. 将一个二维数组旋转 90 度
public class Array90 {
public static void rotate(int[][] arr){
int n = arr.length;
for (int i = 0; i < n/2; i++) {
for (int j = i; j < n-1-i; j++){
int temp = arr[i][j];
arr[i][j] = arr[n-1-j][i];
arr[n-1-j][i] = arr[n-1-i][n-1-j];
arr[n-1-i][n-1-j] = arr[j][n-1-i];
arr[j][n-1-i] = temp;
}
}
}
public static void main(String[] args){
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
for(int i = 0; i < arr.length; i++) {
for(int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
}
System.out.println();
rotate(arr);
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
}
}
}
8. 前K个最大的数
描述:
求一个数组中前 K 个最大的数
public class ChildMaxSum {
public static int findKthLargest(int[] nums, int k) {
if (k < 1 || nums == null) {
return 0;
}
return getKth(nums.length - k +1, nums, 0, nums.length - 1);
}
public static int getKth(int k, int[] nums, int start, int end) {
int pivot = nums[end];
int left = start;
int right = end;
while (true) {
while(nums[left] < pivot && left < right) {
left++;
}
while(nums[right] >= pivot && right > left) {
right--;
}
if(left == right) {
break;
}
swap(nums, left, right);
}
swap(nums, left, end);
if (k == left + 1) {
return pivot;
} else if (k < left + 1) {
return getKth(k, nums, start, left - 1);
} else {
return getKth(k, nums, left + 1, end);
}
}
//A交换数组中两个位置的数
public static void swap(int[] nums, int n1, int n2){
int tmp = nums[n1];
nums[n1] = nums[n2];
nums[n2] = tmp;
}
public static void main(String[] args) {
int[] arr = {4,3,7,54,32,2,34,321,42,32,65,5,4};
System.out.println(findKthLargest(arr, 8));
}
}