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;
	}

}

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