Leetcode46:全排列(ⅠⅡ)Java

Leetcode46:全排列Ⅰ(java)

题目描述

在这里插入图片描述

思路图解

1.递归回溯法
首先介绍“回溯”算法的应用。“回溯”算法也叫“回溯搜索”算法,主要用于在一个庞大的空间里搜索我们所需要的问题的解。我们每天使用的“搜索引擎”就是帮助我们在庞大的互联网上搜索我们需要的信息。“搜索”引擎的“搜索”和“回溯搜索”算法的“搜索”意思是一样的。

“回溯”指的是“状态重置”,可以理解为“回到过去”、“恢复现场”,是在编码的过程中,是为了节约空间而使用的一种技巧。而回溯其实是“深度优先遍历”特有的一种现象。之所以是“深度优先遍历”,是因为我们要解决的问题通常是在一棵树上完成的,在这棵树上搜索需要的答案,一般使用深度优先遍历。

“全排列”就是一个非常经典的“回溯”算法的应用。我们知道,N 个数字的全排列一共有 N!N!N! 这么多个。

大家可以尝试一下在纸上写 3 个数字、4 个数字、5 个数字的全排列,相信不难找到这样的方法。

以数组 [1, 2, 3] 的全排列为例。

我们先写以 1 开头的全排列,它们是:[1, 2, 3], [1, 3, 2];
再写以 2 开头的全排列,它们是:[2, 1, 3], [2, 3, 1];
最后写以 3 开头的全排列,它们是:[3, 1, 2], [3, 2, 1]。

我们只需要按顺序枚举每一位可能出现的情况,已经选择的数字在接下来要确定的数字中不能出现。按照这种策略选取就能够做到不重不漏,把可能的全排列都枚举出来。

在枚举第一位的时候,有 3 种情况。
在枚举第二位的时候,前面已经出现过的数字就不能再被选取了;
在枚举第三位的时候,前面 2 个已经选择过的数字就不能再被选取了。

这样的思路,我们可以用一个树形结构表示。看到这里的朋友,建议自己先尝试画一下“全排列”问题的树形结构。

使用编程的方法得到全排列,就是在这样的一个树形结构中进行编程,具体来说,就是执行一次深度优先遍历,从树的根结点到叶子结点形成的路径就是一个全排列。

在这里插入图片描述举个例子
从 [1, 2, 3] 到 [1, 3, 2] ,深度优先遍历是这样做的,从 [1, 2, 3] 回到 [1, 2] 的时候,需要撤销刚刚已经选择的数 3,因为在这一层只有一个数 3 我们已经尝试过了,因此程序回到上一层,需要撤销对 2 的选择,好让后面的程序知道,选择 3 了以后还能够选择 2。

这种在遍历的过程中,从深层结点回到浅层结点的过程中所做的操作就叫“回溯”。


2.迭代法
在这里插入图片描述

代码实现

package LeetCode;

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

public class Permute
{

	public static void main(String[] args)
	{
		int nums[]={1,2,3};
		
		System.out.println(permute2(nums));

	}
	//方法一:递归回溯法
	public static List<List<Integer>> permute(int[] nums)
	{
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		if (nums == null || nums.length == 0)
			return res;
		helper(res, new ArrayList<Integer>(), nums);
		return res;

	}

	public static void helper(List<List<Integer>> res, List<Integer> list,
			int[] nums)
	{
		if (list.size() == nums.length)
		{
			//  ArrayList是一个引用,记录的是指向位置,如果对应位置上的数据被修改,结果就不是想要的了。
			res.add(new ArrayList<Integer>(list));
			// 如果是这样,无法拷贝integers里面的值
			// res.add(list);
			return;
		}
		for (int i = 0; i < nums.length; i++)
		{
			if (list.contains(nums[i]))
				continue;
			list.add(nums[i]);
			helper(res, list, nums);
			list.remove(list.size() - 1);

		}
	}

	//方法二
	public static List<List<Integer>> permute2(int[] nums)
	{
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		if (nums == null || nums.length == 0)
			return res;
		helper2(res, 0, nums);
		return res;
	}

	public static void helper2(List<List<Integer>> res, int first, int[] nums)
	{
		if (first==nums.length)
		{
			List<Integer> list=new ArrayList<Integer>();
			for(int num:nums) {
				list.add(num);
			}
			res.add(new ArrayList<Integer>(list));
			return;
		}
		for (int i = first; i < nums.length; i++)
		{
			swap(nums, first, i);
			helper2(res, first+1, nums);
			swap(nums, first, i);//换回去
		}
	}
	public static void swap(int[]nums,int l,int r)
	{
		int temp=nums[l];
		nums[l]=nums[r];
		nums[r]=temp;
	}
}

运行结果

在这里插入图片描述


Leetcode47:全排列Ⅱ(java)

题目描述

在这里插入图片描述

思路图解

在一定会产生重复结果集的地方剪枝
例题:对于数组 [1, 1’, 1’’, 2],回溯的过程如下:
在这里插入图片描述得到的全排列是:[[1, 1’, 1’’, 2], [1, 1’, 2, 1’’], [1, 2, 1’, 1’’], [2, 1, 1’, 1’’]]。特点是:1、1’、1’’ 出现的顺序只能是 1、1’、1’’

代码实现

package LeetCode;

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

public class Permute2
{

	public static void main(String[] args)
	{
		int nums[] =
		{
				1, 1, 1, 3
		};
		System.out.println("方案一:" + permuteUnique(nums));
		System.out.println("方案二:" + permuteUnique2(nums));
	}

	public static List<List<Integer>> permuteUnique(int[] nums)
	{
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		if (nums == null || nums.length == 0)
		{
			return res;
		}
		// 修改 1:排序(升序或者降序都可以),为了剪枝方便
		Arrays.sort(nums);
		helper(res, new ArrayList<Integer>(), nums, new boolean[nums.length]);
		return res;
	}

	public static void helper(List<List<Integer>> res, List<Integer> list,
			int[] nums, boolean[] used)
	{
		if (list.size() == nums.length)
		{
			res.add(new ArrayList<Integer>(list));
			return;
		}
		for (int i = 0; i < nums.length; i++)
		{
			// 修改 2:在 used[i - 1] 刚刚被撤销的时候剪枝,说明接下来会被选择,搜索一定会重复,故"剪枝"
			if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1])
				continue;
			used[i] = true;
			list.add(nums[i]);
			helper(res, list, nums, used);
			// 回溯,撤销选择
			used[i] = false;
			list.remove(list.size() - 1);
		}
	}

	//方法二:
	public static List<List<Integer>> permuteUnique2(int[] nums)
	{
		List<List<Integer>> res = new ArrayList<List<Integer>>();
		if (nums == null || nums.length == 0)
		{
			return res;
		}
		// 修改 1:排序(升序或者降序都可以),为了剪枝方便
		Arrays.sort(nums);
		helper2(res, nums, 0);
		return res;
	}

	public static void helper2(List<List<Integer>> res, int[] nums, int start)
	{
		if (start == nums.length)
		{
			List<Integer> list = new ArrayList<Integer>();
			for (int num : nums)
			{
				list.add(num);
			}
			res.add(list);
			return;
		}
		for (int i = start; i < nums.length; i++)
		{
			if (isUsed(nums, start, i))
				continue;
			swap(nums, start, i);
			helper2(res, nums, start + 1);
			swap(nums, start, i);

		}
	}

	public static void swap(int[] nums, int i, int j)
	{
		int temp = nums[i];
		nums[i] = nums[j];
		nums[j] = temp;
	}

	public static boolean isUsed(int[] nums, int i, int j)
	{
		for (int x = i; x < j; x++)
		{
			if (nums[x] == nums[j])
				return true;
		}
		return false;
	}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章