Java面试--面试中常考的递归问题

面试中常考的递归问题

一、链表创建

定义函数 CreateLinkedList,让其返回值为 Node(链表头),每次必须缩小问题规模且必须为1;则将 1 拆掉,把 2、3、4、5 添入到

CreateLinkedList,再把 1 和 2、3、4、5 连接起来即可;

如何接:把 1 的 next 和 2、3、4、5 的 head 接起来就完成了链表的创建;

代码实现:

Node.java[创建节点]

package interview.common;

public class Node<T> {
  private final T value;	//用户传入不能再做改变,设置final
  private Node<T> next;

  public Node(T value) {
    this.value = value;
    this.next = null;
  }

  public T getValue() {
    return value;
  }

  public Node<T> getNext() {
    return next;
  }

  public void setNext(Node<T> next) {
    this.next = next;
  }

  public static <T> void printLinkedList(Node<T> head) {
    while(head != null) {
      System.out.print(head.getValue());
      System.out.print(" ");
      head = head.getNext();
    }
    System.out.println();
  }
}

LinkedListCreator.java

package interview.recursion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import interview.common.Node;

public class LinkedListCreator {

  /**
   * Creates a linked list.
   *
   * @param data the data to create the list
   * @return head of the linked list. The returned linked list
   * ends with last node with getNext() == null.
   */
  public <T> Node<T> createLinkedList(List<T> data) {	//定义函数,参数为List<T>
    if (data.isEmpty()) {	//链表为空时【特殊】
      return null;
    }

    Node<T> firstNode = new Node<>(data.get(0));	//将 node 第0个元素拿出来新建 node 
    firstNode.setNext(
        createLinkedList(data.subList(1, data.size())));	
		//用头结点接收去掉第0个元素剩下的所有元素【把 1 的 next 和 2、3、4、5 的 head 接起来就完成了链表的创建】
    return firstNode;
  }

  public Node<Integer> createLargeLinkedList(int size) {
    Node<Integer> prev = null;
    Node<Integer> head = null;
    for (int i = 1; i <= size; i++) {
      Node<Integer> node = new Node<>(i);
      if (prev != null) {
        prev.setNext(node);
      } else {
        head = node;
      }
      prev = node;
    }
    return head;
  }

  public static void main(String[] args) {
    LinkedListCreator creator = new LinkedListCreator();

    Node.printLinkedList(
        creator.createLinkedList(new ArrayList<>()));
    Node.printLinkedList(
        creator.createLinkedList(Arrays.asList(1)));
    Node.printLinkedList(
        creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5)));
  }
}

输出:

二、链表反转

由上图到下图

思路:

1.将问题的规模减小,减小的量为 1,先将 2、3、4、5 这个链表反转了

2.假设可以正确反转,得图

3.假设 2 开头的链表已经进行了正确的反转,要使得 1 这个链表也要能真确的反转,将 2 的 Next 也指向 1;

4.  1 的 Next  指向 Null;至此,链表反转完成;

  代码实现:

LinkedListReverser.java

package interview.recursion;

import java.util.ArrayList;
import java.util.Arrays;

import interview.common.Node;

public class LinkedListReverser {

  /**
   * Reverses a linked list.
   *
   * @param head the linked list to reverse
   * @return head of the reversed linked list
   */
  public <T> Node<T> reverseLinkedList(Node<T> head) {
    // size == 0 or size == 1
    if (head == null || head.getNext() == null) {		//链表为空或1
      return head;
    }

    Node<T> newHead = reverseLinkedList(head.getNext());	//(假设)将head.getNext()开头的LinkedList 反转
    head.getNext().setNext(head);	//将第二个 head.next 由指向 null 变为指向自己
    head.setNext(null);	//将自己的head.next指向null
    return newHead;	//返回指向 5 的节点
  }

  public static void main(String[] args) {	//测试代码
    LinkedListCreator creator = new LinkedListCreator();
    LinkedListReverser reverser = new LinkedListReverser();

    Node.printLinkedList(reverser.reverseLinkedList(
        creator.createLinkedList(new ArrayList<>())));

    Node.printLinkedList(reverser.reverseLinkedList(
        creator.createLinkedList(Arrays.asList(1))));

    Node.printLinkedList(reverser.reverseLinkedList(
        creator.createLinkedList(Arrays.asList(1, 2, 3, 4, 5))));

    System.out.println("Testing large data. Expect exceptions.");
    reverser.reverseLinkedList(
        creator.createLargeLinkedList(1000000));
    System.out.println("done");
  }
}

输出:

三、列出所有的组合

缩小规模:1.从 [1,2,3,4] 中取出 2 个元素进行组合;2.对第一个元素有两个选择:要它或者不要它;

选 1:从[2,3,4] 中选择 1 个元素;

不选1:从[2,3,4] 中选择 2个元素;完成递推关系

代码:Combinations.java

package interview.recursion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Combinations {

  /**
   * Generates all combinations and output them,
   * selecting n elements from data.
   */
  public void combinations(
      List<Integer> selected, List<Integer> data, int n) {
    if (n == 0) {//从集合中选择0个元素
      // output all selected elements
      for (Integer i : selected) {	
        System.out.print(i);
        System.out.print(" ");
      }
      System.out.println();
      return;
    }

    if (data.isEmpty()) {	//从空集中选择0个元素【1种】
      return;
    }

    // select element 0[选择第0号元素]
    selected.add(data.get(0));	//添加第0个元素
    combinations(selected, data.subList(1, data.size()), n - 1);

    // un-select element 0[不选择第0号元素]
    selected.remove(selected.size() - 1);	//删掉最后一个元素
    combinations(selected, data.subList(1, data.size()), n);
  }

  public static void main(String[] args) {
    Combinations comb = new Combinations();

    System.out.println("Testing normal data.");
    comb.combinations(
        new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 2);
    System.out.println("==========");

    System.out.println("Testing empty source data.");
    comb.combinations(
        new ArrayList<>(), new ArrayList<>(), 2);	//空集里选出2个元素
    System.out.println("==========");
    comb.combinations(
        new ArrayList<>(), new ArrayList<>(), 0);//空集里选出0个元素
    System.out.println("==========");

    System.out.println("Selecting 1 and 0 elements.");
    comb.combinations(
        new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 1);	//从(1, 2, 3, 4)中选出1个元素
    System.out.println("==========");
    comb.combinations(
        new ArrayList<>(), Arrays.asList(1, 2, 3, 4), 0);
    System.out.println("==========");

   
  }
}

输出:

三、递归的缺点

1.函数调用开销

2.每一次调用节点都会调用栈、超出栈的范围,会发生 Stack Overflow;

猜你喜欢

转载自blog.csdn.net/jianghao233/article/details/82777920