算法十八招
第一招:动态规划算法
动态规划思想:把问题分解为多个阶段,每个阶段执行决策,记录每一个阶段可达的状态集合(去重后),
基于当前阶段的状态集合,通过决策,推导下一个阶段的状态结婚,动态的往前推进
经典背包问题:有一组不同重量不可分割的物品,需要选择一些装入背包,在满足背包最大重量限制的前提下
,背包中物品的最大重量是多少?
1、把问题分为多个阶段,物品是一个一个装入选择是否装入背包中,每一个物品选择是否装入背包作为一个阶段,共有物品个数个阶段
2、每个阶段做出决策,选择是否装入该物品
3、每个阶段做出决策前有一个当前状态集合,做出决策后也记录可达状态集合(去重)
依次步骤动态的往前推进 阶段就是for循环 决策就是for循环里的逻辑 初始化状态集合,并在执行决策后记录可达的状态集合
循环执行完后,即动态推进结束
public class Packet { public int packet(int[] weights, int maximum) { int number = weights.length; boolean[][] states = new boolean[number][maximum+1];//true表示可达状态 states[0][0] = true; if(weights[0] <= maximum) { states[0][weights[0]] = true; } for(int i=1; i<number; i++) { //装进背包 for(int j=0; j<=maximum; j++) { if(states[i-1][j] && j + weights[i] <= maximum) { states[i][j + weights[i]] = true; } } //不装进背包 for(int j=0; j<=maximum; j++) { if(states[i-1][j]) { states[i][j] = true; } } } for(int i=maximum; i>=0; i--) { if(states[number-1][i]) { return i; } } return 0; } public static void main(String[] args) { Packet demo = new Packet(); int[] weight = {2, 2, 4, 6, 3}; // 物品重量 int n = 5; // 物品个数 int w = 9; // 背包承受的最大重量 System.out.println(demo.packet(weight, w)); } }
第二招:贪心算法
贪心算法思想:每次都选择堆期望值贡献最大的数据
经典背包问题:有一组不同重量不可分割的物品,每个物品都有对象的价值,需要选择一些装入背包,在满足背包最大重量限制的前提下,背包中物品的最大价值是多少?
public class Packet { public int packet(int[] weights, int[] values, int maximum) { //构建性价比数组 int number = weights.length; double[] w_v = new double[number]; int[] index = new int[number]; for(int i=0; i<number; i++) { w_v[i] = values[i] / weights[i]; index[i] = i; } double temp = 0; int x = -1; for(int i=0; i<number-1; i++) { for(int j=i+1; j<number; j++) { if(w_v[index[j]] > w_v[index[i]]) { temp = w_v[index[j]]; w_v[index[j]] = w_v[index[i]]; w_v[index[i]] = temp; x = index[i]; index[i] = index[j]; index[j] = x; } } } //将排好序的重量和价值分别存到数组中 int[] w1 = new int[number]; int[] v1 = new int[number]; for(int i=0; i<number; i++) { w1[i] = weights[index[i]]; v1[i] = values[index[i]]; } int maxValue = 0; int sumWeight = 0; for(int i=0; i<number; i++) { if(sumWeight + w1[i] <= maximum) {//表示当前物品装得下 sumWeight += w1[i]; maxValue += v1[i]; } } return maxValue; } public static void main(String[] args) { Packet demo = new Packet(); int[] weights = {35, 30, 60, 50, 40, 10, 25}; // 物品重量 int[] values = {10, 40, 30, 50, 35, 40, 30};// 物品价值 int maximum = 150; // 背包承受的最大重量 System.out.println(demo.packet(weights, values, maximum)); } }
第三招:回溯算法
经典八皇后问题:
//8皇后问题--回溯算法 public class Recall { int[] result = new int[8];//全局或成员变量,下标表示行,值表示queue存储在哪一列 public static void main(String[] args) { Recall recall = new Recall(); recall.cal8queues(0); } public void cal8queues(int row) {//调用方式:cal8queues(0) if(row == 8) {//8个棋子都放置好了,打印结果 printQueues(result); return;//8行棋子都放好了,已经没法再往下递归了,所以就return } for(int column=0; column<8; column++) {//每一行都有8种放法 if(isOk(row, column)) {//有些放法不满足要求 result[row] = column;//第row行的棋子放到了column列 cal8queues(row+1);//考察下一行 } } } private boolean isOk(int row, int column) {//判断row行column列放置是否合适 int leftup = column-1, rightup = column+1; for(int i=row-1; i>=0; i--) {//逐行往上考察每一行 if(result[i] == column) { return false;//第i行的column列有棋子吗? } if(leftup >= 0) {//考察左上对角线:第i行leftup列有棋子吗? if(result[i] == leftup) { return false; } } if(rightup < 8) {//考察右上对角线:第i行rightup列有棋子吗? if(result[i] == rightup) { return false; } } leftup--; rightup++; } return true; } private void printQueues(int[] result) {//打印一个二维矩阵 for(int row=0; row<8; row++) { for(int column=0; column<8; column++) { if(result[row] == column) { System.out.print("Q"); }else { System.out.print("*"); } } System.out.println(); } System.out.println(); } }
第四招:二叉树
【递归 | 非递归】前序、中序、后序遍历
import java.util.Stack; public class BinaryTree { static class Node { public String value;//节点存储的内容 public Node leftNode;//左孩子 public Node rightNode;//右孩子 public Node(String value) { this.value = value; } } public static void main(String[] args) { /** 8 / \ 6 7 / \ / \ 2 3 4 5 / 1 */ Node root = new Node("8"); Node node1 = new Node("1"); Node node2 = new Node("2"); Node node3 = new Node("3"); Node node4 = new Node("4"); Node node5 = new Node("5"); Node node6 = new Node("6"); Node node7 = new Node("7"); root.leftNode = node6; root.rightNode = node7; node6.leftNode = node2; node6.rightNode = node3; node7.leftNode = node4; node7.rightNode = node5; node2.leftNode = node1; BinaryTree demo = new BinaryTree(); demo.npreOrder(root); System.out.println(); demo.ninOrder(root); System.out.println(); demo.npostOrder(root); System.out.println(); demo.preOrder(root); System.out.println(); demo.inOrder(root); System.out.println(); demo.postOrder(root); System.out.println(); } //非递归前序遍历 public void npreOrder(Node node){ Stack<Node> sk=new Stack<Node>(); Node n=node; while(!sk.isEmpty() || n!=null){ if(n!=null){ System.out.print("->"); System.out.print(n.value); sk.push(n); n=n.leftNode; }else{ n=sk.pop();; n=n.rightNode; } } } //非递归的中序遍历 public void ninOrder(Node node){ Stack<Node> s=new Stack<Node>(); Node n = node; while(n != null || !s.isEmpty()){ if(n != null){ s.push(n); n = n.leftNode; }else{ n = s.pop(); System.out.print("->"); System.out.print(n.value); n = n.rightNode; } } } //非递归后序遍历 public void npostOrder(Node node){ Stack<Node> s1=new Stack<Node>();//第一次入栈 Stack<Node> s2=new Stack<Node>();//第二次入栈 Node n=node; while(!s1.isEmpty() || n!=null){ if(n != null){ s1.push(n); s2.push(n); n=n.rightNode; }else{ n=s1.pop(); n=n.leftNode; } } while(!s2.isEmpty()){ System.out.print("->"); System.out.print(s2.pop().value); } } //递归前序遍历 public void preOrder(Node node){ if(node != null){ System.out.print("->" + node.value); preOrder(node.leftNode); preOrder(node.rightNode); } } //中序遍历 public void inOrder(Node node){ if(node!=null){ inOrder(node.leftNode); System.out.print("->" + node.value); inOrder(node.rightNode); } } //后序遍历 public void postOrder(Node node){ if(node!=null){ postOrder(node.leftNode); postOrder(node.rightNode); System.out.print("->" + node.value); } } }
第五招:分治算法
经典归并排序问题:
要求:
对数组进行排序
思路:
把数组对半分,把对半分的数组再次对半分,分到最后子数组长度为1的时候开始按照有序规则,合并两个数组,递归返回,最后合并成一个大的有序数组
//归并排序 public static int[] mergeSort(int[] arr) { if (arr.length < 2) return arr; int[] arr1 = Arrays.copyOfRange(arr, 0, arr.length / 2); int[] arr2 = Arrays.copyOfRange(arr, arr.length / 2, arr.length); arr1 = mergeSort(arr1); arr2 = mergeSort(arr2); return combine(arr1, arr2); } //合并函数 private static int[] combine(int[] arr1, int[] arr2) { int[] arr = new int[arr1.length + arr2.length]; for (int i = 0, j = 0, k = 0; k < arr.length; k++) { int temp; if (arr1.length <= i) temp = arr2[j++]; else if (arr2.length <= j) temp = arr1[i++]; else if (arr1[i] < arr2[j]) temp = arr1[i++]; else temp = arr2[j++]; arr[k] = temp; } return arr; }