[关闭]
@LIUHUAN 2018-03-15T12:17:02.000000Z 字数 7788 阅读 1304

递归与回溯算法

algorithm


0.递归算法介绍

1.阶乘的计算

  1. n! = n*(n-1)*(n-2)...(1)

上面是阶乘的定义:

  1. (1).让我求f(n),->>>>
  2. (2).我就直接假设我已经能够有这个能力了f(n)定义出来 ->>>>
  3. (3).f(n) 什么时候能够直接得到问题的答案,也就是递归的出口 --->>>>> 这个就是递归调用函数的需要return的地方
  4. (4).组合一下更小规模情况下f(n-1)和f(n)的关系,阶乘函数是n*f(n-1)的关系 ->>>>
  1. public static void main(String []args) {
  2. int n = 5;
  3. System.out.println("5! = " + factorial(n));
  4. }
  5. public static int factorial(int n) {
  6. /* (1).最为基本的情况,也就是说,这个问题已经不能再小了,我已经知道怎么解决了。所有的递归都需要有明确的返回,
  7. * (2).只有在最基本的情况下返回,递归才不会无线循环下去,因为递归是调用自己的。
  8. */
  9. if (n <= 0 )
  10. return 1;
  11. else {
  12. /* (1).一个规模小的问题,和当前的问题的关系是什么
  13. * (2).这里阶乘的计算是 n*f(n-1)的关系
  14. * (3).如果是求和,那么是 n + f(n-1)的关系
  15. */
  16. return n * factorial(n-1);
  17. }
  18. }
  1. n*f(n-1) ->>>>> n + f(n-1)即可

1.1 举例说明递归时如何调用的

  1. class TreeNode {
  2. int v;
  3. TreeNode left,right;
  4. }
  5. public static void preOrder(TreeNode root) {
  6. if (root == null){ // 一颗空树,最基本的情况,什么时候不需要在递归了
  7. return ;
  8. }
  9. visited(root); // 1: 访问节点
  10. preOrder(root.left);//2 访问左子树节点
  11. preOrder(root.right);//3 访问右子树的节点
  12. }
  13. public static void visited(TreeNode node) {
  14. System.out.println(node.v);
  15. }

1.3 树遍历的扩展

  1. public class Solution{
  2. public static nodeCount = 0;
  3. public static void main(String[] args){
  4. TreeNode root = new TreeNode();
  5. preOrder(root);//从根节点开始遍历
  6. }
  7. public static void preOrder(TreeNode root) {
  8. if (root == null){ // 一颗空树,最基本的情况,什么时候不需要在递归了
  9. return ;
  10. }
  11. visited(root); // 1: 访问节点
  12. preOrder(root.left);//2 访问左子树节点
  13. preOrder(root.right);//3 访问右子树的节点
  14. }
  15. public static void visited(TreeNode node) {
  16. nodeCount++;// 统计节点的个数
  17. // 或者nodeCount += node.v + 1;//所有节点的值都+1,求和
  18. System.out.println(node.v);
  19. }
  20. }//end class Solution

2.广度优先搜索算法(Breadth First Search:BFS)

  1. public static void main(String[] args){
  2. Graph G = new Graph();
  3. DFS(G.get(0),G);// 表示从0 节点开始遍历
  4. }
  5. public static void BFS(Node S,Graph G) {
  6. /* 初始化数据结构
  7. * 队列Q
  8. * 是否遍历的标志hashmap
  9. */
  10. Queue<Node> Q = new LinkedList<Node>();
  11. HashMap<Node,Boolean> visitedMap = new HashMap<Node,Boolean>();
  12. /** 初始化标志
  13. */
  14. Q.add(S);
  15. visitedMap.put(S, true); // S 已经认为是在队列当中,说明已经要被访问了,所以这么标志
  16. while( !Q.isEmpty()) { // (0).队列非空
  17. Node q = Q.poll(); // (1).出队列
  18. visited(q); // (2).这个就是遍历函数,或者要通过遍历来实现的目标的函数,,也可以时其他的过程处理,bi'r比如计数,求和等
  19. /* (3).把它的相邻节点,有个求相邻节点的函数
  20. * 且未遍历到的标志为遍历并放入队列Q
  21. */
  22. ArrayList<Node> adjNodeList = G.getAdjectList(q); // 获取相邻的节点
  23. for(int i = 0; i < adjNodeList.size() ; ++i) {
  24. /**没有被访问过,放到队列当中*/
  25. if (visitedMap.containsKey(adjNodeList.get(i)) == false) { // 没有被访问过
  26. Q.add(adjNodeList.get(i)); //加入到队列中
  27. visitedMap.put(adjNodeList.get(i),true);// 表示已经访问了
  28. }
  29. }//end for
  30. }// end while
  31. }
  32. public static void visited(Node s) {
  33. System.out.println(s); // 这里只是简单的打印一下,可进行性统计、计数等操作
  34. }

2.1 广度优先搜索的举例

  1. class TreeNode {
  2. int v;
  3. TreeNode left,right;
  4. /**
  5. (1).获取这个节点相邻节点,也就是从这个节点可以访问到的节点,左子树节点和右子树节点
  6. (2).虽然这个节点的父节点从实际画出来的结构看也能访问到
  7. (3).但是定义数据结构的时候,并没有看到有子节点访问父节点的路径
  8. (4).如果TreeNode 里面存储parent节点表示父节点,那么是可以访问到的,还要看数据结构怎么设计
  9. */
  10. public ArrayList<TreeNode> getAdjectList(){
  11. ArrayList<TreeNode> adj = new ArrayList<TreeNode>();
  12. if(this.left != null ) {
  13. adj.add(this.left);
  14. }
  15. if(this.right != null ) {
  16. adj.add(this.right);
  17. }
  18. }
  19. }
  20. public static void BFS(TreeNode root) {
  21. /* 初始化数据结构
  22. * 队列Q
  23. * 是否遍历的标志hashmap
  24. */
  25. Queue<Node> Q = new LinkedList<Node>();
  26. HashMap<Node,Boolean> visitedMap = new HashMap<Node,Boolean>();
  27. /** 初始化标志
  28. */
  29. Q.add(root);
  30. visitedMap.put(root, true); // root 已经认为是在队列当中,说明已经要被访问了,所以这么标志
  31. while( !Q.isEmpty()) { // (0).队列非空
  32. TreeNode q = Q.poll(); // (1).出队列
  33. visited(q); // (2).这个就是遍历函数,或者要通过遍历来实现的目标的函数,,也可以时其他的过程处理,比如计数,求和等
  34. /* (3).把它的相邻节点,有个求相邻节点的函数
  35. * 且未遍历到的标志为遍历并放入队列Q
  36. */
  37. ArrayList<TreeNode> adjNodeList = q.getAdjectList(); // 获取相邻的节点
  38. for(int i = 0; i < adjNodeList.size() ; ++i) {
  39. /**没有被访问过,放到队列当中*/
  40. if (visitedMap.containsKey(adjNodeList.get(i)) == false) { // 没有被访问过
  41. Q.add(adjNodeList.get(i)); //加入到队列中
  42. visitedMap.put(adjNodeList.get(i),true);// 表示已经访问了
  43. }
  44. }//end for
  45. }// end while
  46. }
  47. public static void visited(TreeNode s) {
  48. System.out.println(s); // 这里只是简单的打印一下,可进行性统计、计数等操作
  49. }

2.3 相关二叉树的层次遍历(BFS)的练习题

2.4 图的联通量的遍历

  1. /***
  2. * A----B D E
  3. * | | | |
  4. * F----G H I
  5. * | |
  6. * J K L M
  7. * | |
  8. * N O----P----Q
  9. * (x-1,y-1)---(x-1,y)----(x,y-1)
  10. * (x , y-1)---(x , y)----(x,y+1)
  11. * (x+1,y-1)---(x+1,y)----(x+1,y+1)
  12. *
  13. * 11110
  14. 11010
  15. 11000
  16. 00000
  17. answer:1
  18. *
  19. * 11000
  20. 11000
  21. 00100
  22. 00011
  23. * answer:3
  24. */
  25. public class Solution {
  26. public static HashMap<NodeValue,Boolean> visitedMap = new HashMap<NodeValue,Boolean>();
  27. public static void main(String[] args) {
  28. char [][] grid = new char[n][n];
  29. System.out.println(grid);
  30. }
  31. public static int numIslands(char[][] grid) {
  32. // 每一个节点都需要遍历,其实,已经遍历过的节点,会在visitedMap当中标志,不会添加相邻节点到队列当中
  33. int area = 0;
  34. int count = 0;
  35. for(int x =0; x < grid.length;++x) {
  36. for(int y = 0; y < grid[0].length;++y) {
  37. if(visitedMap.containsKey(new NodeValue(x,y)) == false) {
  38. BFS(grid,x,y);
  39. count ++;// 表示找到了一个连通分量
  40. /**
  41. if(area < tarea) {
  42. area = tarea;
  43. }
  44. **/
  45. }
  46. }
  47. }
  48. return count;
  49. }
  50. /**
  51. * grid 可以认为是一个图,startNodex,startNodey 表示开始遍历的节点
  52. * */
  53. public static int BFS(char[][] grid,int startx,int starty) {
  54. int area = 1; //当前只有一个节点
  55. Queue<NodeValue> Q = new LinkedList<NodeValue>();
  56. NodeValue e = new NodeValue(starty, starty);
  57. Q.add(e);
  58. visitedMap.put(e, true);
  59. while(! Q.isEmpty()) {
  60. NodeValue q = Q.poll();
  61. ArrayList<NodeValue> adjlist = getAdjList(grid,q);
  62. for(int i = 0 ;i < adjlist.size(); ++i) {
  63. if(visitedMap.containsKey(adjlist.get(i)) == false) {
  64. Q.add(adjlist.get(i));
  65. visitedMap.put(adjlist.get(i), true);
  66. area += 1;// 说明这个是可以到达的和startx,starty 是相邻的
  67. }
  68. }
  69. }
  70. return area;
  71. }
  72. /**寻找其相邻节点的函数*/
  73. public static ArrayList<NodeValue> getAdjList(char[][] grid,NodeValue e) {
  74. ArrayList<NodeValue> adjlist = new ArrayList<NodeValue>();
  75. int xlen = grid.length;
  76. int ylen = grid[0].length;
  77. if(e.x - 1 >=0 ) { // 上面
  78. if (grid[e.x -1 ][e.y] == '1') {
  79. adjlist.add(new NodeValue(e.x-1,e.y));
  80. }
  81. }
  82. if(e.y - 1 >=0 ) { //左面
  83. if (grid[e.x ][e.y - 1 ] == '1') {
  84. adjlist.add(new NodeValue(e.x,e.y - 1 ));
  85. }
  86. }
  87. if(e.x + 1 < xlen ) { // 下面
  88. if (grid[e.x + 1 ][e.y] == '1') {
  89. adjlist.add(new NodeValue(e.x + 1,e.y));
  90. }
  91. }
  92. if(e.y + 1 < ylen ) { // 右面
  93. if (grid[e.x ][e.y + 1] == '1') {
  94. adjlist.add(new NodeValue(e.x,e.y + 1));
  95. }
  96. }
  97. return adjlist;
  98. }
  99. }
  100. class NodeValue{
  101. public int x, y;
  102. public NodeValue(int x,int y){
  103. this.x = x;
  104. this.y = y;
  105. }
  106. @Override
  107. public boolean equals(Object obj) {
  108. // TODO Auto-generated method stub
  109. if(this == obj)
  110. return true;
  111. if(obj == null)
  112. return false;
  113. NodeValue node = (NodeValue)obj;
  114. return this.x == node.x && this.y == node.y;
  115. }
  116. }

3.深度度优先算法(Depth First Search:DFS)

  1. /**是否已经被遍历的标志,如果被遍历了,是不会在进行遍历的*/
  2. public static HashMap<Node,Boolean> visitedMap= new HashMap<Node,Boolean>();
  3. public static void main(String[] args) {
  4. Graph G = new Graph();
  5. Node start;
  6. /**
  7. //每个节点开始去遍历,按照一个图所有节点之间都是可以到达的化,这种做法是不必要的,因为从任何一个节点都是可以遍历整个图的,
  8. 但是如果这个图中有两个不可达的节点,这个循环就是必要的了。
  9. */
  10. for(int i = 0 ;i < G.size(); ++i)
  11. if (visitedMap.get(G.get(i)) == false)
  12. DFS(G.get(i),G);
  13. }
  14. public static void DFS(Node s,Graph G){
  15. if( visitedMap.get(s) == true){
  16. /**递归的出口一定要有,
  17. * 已经被遍历了,所有不需要再遍历了,那么直接返回吧
  18. */
  19. return ;
  20. }
  21. /**
  22. 没有被遍历、那么遍历一下、设置标志位
  23. */
  24. visit(s);//遍历下
  25. visitedMap.put(s,true);// 标志位已经遍历了
  26. /** 获取相邻节点,并遍历*/
  27. ArrayList<Node> adjList = G.getAdjNodeList(s,G);
  28. for(int i = 0; i < adjList.size(); ++i){
  29. Node newNode = adjList.get(i);
  30. if (visitedMap.contains(newNode) == false) {
  31. DFS(newNode,G);//newNode 作为一个新的节点,从这里又开始向它的相邻节点遍历了
  32. }
  33. }
  34. }
  35. public static void visit(Node s) {
  36. System.out.println(s);// 只是打印
  37. }

4.回溯算法

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注