`
lifethinker
  • 浏览: 72077 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

排列(递归与非递归)算法及接口设计

    博客分类:
  • java
阅读更多

本文我将首先用递归和非递归算法实现对整数数组的全排列,然后通过提炼接口,将这种算法运用到实现对其它数据类型的全排列,对long数组,列表,字符串等。

 

下面代码是用来实现对整数数组的排列的算法,具体原理见这里

	public static List<int[]> perm(int[] a) {
		return perm(a, 0, a.length);
	}
	public static List<int[]> perm(int[] a, int fromIndex, int toIndex) {
		List<int[]> result = new ArrayList<int[]>();
		perm(a, fromIndex, toIndex, result);
		return result;
	}
	
	private static void perm(int[] a, int fromIndex, int toIndex, List<int[]> result) {
		if (toIndex - fromIndex <= 1) {
			result.add(a.clone());
		} else {
			for (int i = fromIndex; i < toIndex; i++) {
				swap(a, fromIndex, i);
				perm(a, fromIndex+1, toIndex, result);
				swap(a, fromIndex, i);
			}
		}
	}
	
	private static void swap(int[] a, int i, int j) {
		int temp = a[i]; a[i] = a[j]; a[j] = temp;
	}
 

这里将所有的结果放在一个List中,这样对小的数据很好,但是对大的数据可能会OutOfMemory,在我的机器上当数据的大小>=10时就会OutOfMemory。我需要将它转换成非递归算法,并返回Iterator,这样保存在内存中的数据极少,而不会OutOfMemory。将该程序转换成非递归算法时,需要先画出递归树,具体该怎么做,这里就不详述了,见代码:

	
	public static Iterator<int[]> permIterator(int[] a, int fromIndex, int toIndex) {
		return new IntArrayPermIterator(a, fromIndex, toIndex);
	}
	public static Iterator<int[]> permIterator(int[] a) {
		return new IntArrayPermIterator(a, 0, a.length);
	}
	public static Iterable<int[]> permIterable(int[] a, int fromIndex, int toIndex) {
		return new IntArrayPermIterator(a, fromIndex, toIndex);
	}
	public static Iterable<int[]> permIterable(int[] a) {
		return new IntArrayPermIterator(a, 0, a.length);
	}
	
	// 将递归翻译成非递归
	private static class IntArrayPermIterator implements Iterator<int[]>, Iterable<int[]> {
		private int[] array;
		private int toIndex;
		private LinkedList<StackFrame> frames = new LinkedList<StackFrame>();
		
		public IntArrayPermIterator(int[] a, int fromIndex, int toIndex) {
			this.array = a;
			this.toIndex = toIndex;
			frames.add(new StackFrame(fromIndex));
		}

		public boolean hasNext() {
			return frames.size() > 0;
		}

		public int[] next() {
			StackFrame frame = frames.getLast();
			swap(array, frame.fromIndex, frame.currentIndex);
			for (int i = frame.fromIndex + 1; i < toIndex; i++) {
				frames.addLast(new StackFrame(i));
			}
			int[] result = array.clone();
			frame = frames.removeLast();
			while (!frames.isEmpty()) {
				frame = frames.getLast();
				swap(array, frame.fromIndex, frame.currentIndex);
				if (frame.currentIndex != toIndex - 1) {
					frame.currentIndex++; break;
				}
				frames.removeLast();
			}
			
			return result;
		}
		
		public void remove() {
			throw new UnsupportedOperationException();
		}

		public Iterator<int[]> iterator() {
			return this;
		}
	}

	
	private static class StackFrame {
		int fromIndex;
		int currentIndex;
		
		public StackFrame(int fromIndex) {
			this.fromIndex = fromIndex;
			this.currentIndex = fromIndex;
		}
		public String toString() {
			return String.format("[%d, %d]", fromIndex, currentIndex);
		}
	}

  IntArrayPermIterator实现Iterator和Iterable接口,实现Iterable接口是为了能够在利用JDK5中增强for循环:

		for (int[] a: permIterable(new int[]{1, 2, 3})) {
			System.out.println(Arrays.toString(a));
		}
 

这样做的问题是,如果要实现对long数组,char数组,List数据,字符串的排列,我几乎需要将它们重写一遍。我想重用这些算法,该怎么办呢?每当这时我都会想到提取接口。让我们观察递归版本的perm(int[] a, int fromIndex, int toIndex, List<int[]> result)方法(非递归版本类似),它用到对数组a的进行操作的方法只有swap方法和a.clone()方法,因此可以将它提取出一个接口叫做Permable,为了不指定toIndex,我添加了一个新方法size()。另外为了不和Object中clone()方法冲突,我使用了copy这个名字。接口如下:

	public interface Permable<T> {
		void swap(int i, int j);
		T copy();
		int size();
	}

 该接口使用了类型参数T,表示返回的数据类型。使用这个接口,递归版本的排列算法为:

	public static <T> List<T> perm(Permable<T> data) {
		return perm(data, 0, data.size());
	}
	
	public static <T> List<T> perm(Permable<T> data, int fromIndex, int toIndex) {
		List<T> result = new ArrayList<T>();
		perm(data, fromIndex, toIndex, result);
		return result;
	}
	
	private static <T> void perm(Permable<T> data, int fromIndex, int toIndex, List<T> result) {
		if (toIndex - fromIndex <= 1) {
			result.add(data.copy());
		} else {
			for (int i = fromIndex; i < toIndex; i++) {
				data.swap(fromIndex, i);
				perm(data, fromIndex+1, toIndex, result);
				data.swap(fromIndex, i);
			}
		}
	}

 要对long数组,字符串,List等进行排序,只需要提供它们自己的Permable接口实现:

	public static class LongArrayPermable implements Permable<long[]> {
		private long[] data;
		public LongArrayPermable(long[] data) {
			this.data = data;
		}
		public void swap(int i, int j) {
			long temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
		public long[] copy() {
			return data.clone();
		}
		public int size() {
			return data.length;
		}
	}

	public static class CharArrayPermable implements Permable<char[]> {
		private char[] data;
		public CharArrayPermable(char[] data) {
			this.data = data;
		}
		public void swap(int i, int j) {
			char temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
		public char[] copy() {
			return data.clone();
		}
		public int size() {
			return data.length;
		}
	}
	
	public static class StringPermable implements Permable<String> {
		private char[] data;
		public StringPermable(String str) {
			this.data = str.toCharArray();
		}
		public StringPermable(char[] data) {
			this.data = data;
		}
		public String copy() {
			return new String(data);
		}
		public int size() {
			return data.length;
		}
		public void swap(int i, int j) {
			char temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
	}

	public static class ObjectArrayPermable implements Permable<Object[]> {
		private Object[] data;
		public ObjectArrayPermable(Object[] data) {
			this.data = data;
		}
		public void swap(int i, int j) {
			Object temp = data[i]; data[i] = data[j]; data[j] = temp;
		}
		public Object[] copy() {
			return data.clone();
		}
		public int size() {
			return data.length;
		}
	}
	
	public static class ListPermable<E> implements Permable<List<E>> {
		private List<E> data;
		private Class<?> implementation;
		
		public ListPermable(List<E> data, Class<?> listImplementation) {
			this.data = data;
			this.implementation = listImplementation;
		}
		public ListPermable(List<E> data) {
			this(data, data.getClass());
		}
		
		@SuppressWarnings("unchecked")
		public List<E> copy() {
			if (ArrayList.class == implementation) {
				return new ArrayList<E>(data);
			} else {
				try {
					List<E> list = (List<E>) implementation.newInstance();
					list.addAll(data);
					return list;
				} catch (InstantiationException e) {
					throw new IllegalStateException(e);
				} catch (IllegalAccessException e) {
					throw new IllegalStateException(e);
				}
			}
		}

		public int size() {
			return data.size();
		}

		public void swap(int i, int j) {
			E temp = data.get(i); data.set(i, data.get(j)); data.set(j, temp);
		}
	}
 

现在要得到字符串,long数组的排列,可以:

		for (long[] a : perm(new LongArrayPermable(new long[]{1, 2, 3}))) {
			System.out.println(Arrays.toString(a));
		}
		for (String a : perm(new StringPermable("abc"))) {
			System.out.println(a);
		}

它该接口用在非递归版本的排列算法上也很简单,只需要做几处简单的替换就可以了,这里就不列举了,见附件。

 

参考文章:

全排列算法原理和实现: http://www.blogjava.net/nokiaguy/archive/2008/05/11/199838.html

如何用栈实现递归与非递归的转换: http://www.chinaunix.net/jh/23/331522.html ,这篇文章给我启发,将递归算法转换成非递归算法时需要行画出递归树。

 

分享到:
评论

相关推荐

    数据结构与算法

    递归算法通常与堆栈操作紧密相关,需要理解递归概念、递归实现与堆栈的关系。 7. 分治法是一种常见的算法设计方法,它将问题分解为相互独立的子问题,然后分别求解这些子问题。分治法是许多算法如快速排序、归并...

    数据结构与算法java版-不是很高清,不过还算全

    数据结构与算法是计算机科学的基础,对于理解和设计高效的软件至关重要。在Java编程语言中,学习数据结构和算法有助于提升程序的性能和可维护性。"数据结构与算法java版"这个资源虽然清晰度不高,但内容较为全面,...

    数据结构与算法(java版)

    数据结构与算法是计算机科学的基础,对于理解和解决复杂问题至关重要。在Java环境下,这些概念的实现使得程序设计更加高效和优化。以下将详细介绍标题和描述中提到的关键知识点: 1. **栈与队列**: - 栈(Stack)...

    《算法基础与在线实践》教材【完整版】超清晰、带目录

    《算法基础与在线实践》是一本深入浅出的教材,旨在帮助读者理解并掌握算法的基础知识,同时结合实际编程环境,提升算法的在线实践能力。该书内容全面,清晰度高,目录结构分明,便于读者查找和学习。作为一本与Java...

    java数据结构于算法(第二版)_书中示例代码

    Java数据结构与算法是计算机科学中的基础且至关重要的部分,它们是解决问题和设计高效程序的核心。本书《Java数据结构与算法(第二版)》显然深入探讨了这些主题,旨在帮助读者提升编程技能和理解复杂问题的解决策略...

    数据结构与算法(JAVA语言版)

    #### Java与面向对象程序设计:构建算法的基础 在《数据结构与算法(JAVA语言版)》这一资源中,Java作为首选编程语言,其强大的面向对象特性为学习数据结构与算法提供了坚实的基础。Java的特性如封装、继承、多态...

    数据结构与算法(Java版)

    2. **基于归纳的递归:** 通过归纳原理来设计递归算法,即先假设问题的一部分已经被解决,然后基于这部分解决方案来构建整个问题的解决方案。 3. **递推关系求解:** - **求解递推关系的常用方法:** 包括特征方程...

    数据结构与算法java中文

    本章节将深入探讨递归的概念、实现原理以及与堆栈的关系,通过基于归纳的递归来理解递归的本质,学习递推关系的求解方法,包括线性齐次递推式、非齐次递推关系的解,以及Master Method的运用。此外,我们还将了解...

Global site tag (gtag.js) - Google Analytics