`
lifethinker
  • 浏览: 71204 次
  • 性别: 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 ,这篇文章给我启发,将递归算法转换成非递归算法时需要行画出递归树。

 

分享到:
评论

相关推荐

    计算机软件水平考试软件设计师考试大纲与培训指南(2009版)

     常用的排序算法、查找算法、数值计算、字符串处理、数据压缩算法、递归算法、图的相关算法  算法描述和分析 2.2.2 操作系统知识  操作系统的内核  处理机管理  存储管理  设备管理  文件管理  ...

    2005-2009软件设计师历年真题

     • 排序算法、查找算法、数值计算方法、字符串处理方法、数据压缩算法、递归算法、图的相关算法  • 算法与数据结构的关系、算法效率、算法设计、算法描述(流程图、伪代码、决策表)、算法的复杂性  2.计算机...

    java源码包2

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    计算机二级C语言考试题预测

    (65) 软件设计包括软件的结构、数据接口和过程设计,其中软件的过程设计是指(B) 注:P73 A. 模块间的关系 B. 系统结构部件转换成软件的过程描述 C. 软件层次结构 D. 软件开发过程 (66) 为了避免流程图在描述程序逻辑...

    数据结构(C++)有关练习题

    利用堆栈类,将本题a和b的代码改成非递归的方式。 实验报告要求: 按要求写出完整的实验代码; &lt;br&gt;实验四 综合(课程设计) 内容及步骤: 1、假定一维数组a[n]中的每个元素值均在[0,200]区间内,用C++...

    软件课程设计 试验报告 代码 演示

    钱币兑换问题是个非常简单的题目,完成本题所需的编程技巧并不多,但却巩固了我在算法设计与分析课上所学到的很多知识。特别是对于贪心算法,不但让我对其有了更进一步的了解,而且使我能够更好的掌握在分析问题时...

    C#开发实例大全(基础卷).软件开发技术联盟(带详细书签) PDF 下载

    实例032 递归算法的经典面试题 39 实例033 制作一个数字猜猜看小游戏 40 实例034 使用goto语句在数组中搜索指定图书 42 第3章 字符串处理技术 44 3.1 字符及字符串转换 45 实例035 将字母全部转换为大写或小写 45 ...

    计算机二级公共基础知识

    算法基本设计方法:列举法、归纳法、递推、递归、减半递推技术、回溯法。 (5)指令系统 所谓指令系统指的是一个计算机系统能执行的所有指令的集合。 (2)数据结构研究的3个方面 ① 数据集合中各数据元素之间所固有...

    java源码包---java 源码 大量 实例

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    编译原理(第2版)课件

    4.1.1 词法分析程序与语法分析程序的接口方式 4.1.2 词法分析程序的输出 4.1.3 将词法分析工作分离的考虑 4.2 单词的描述工具 4.2.1 正规文法 4.2.2 正规式 4.2.3 正规文法和正规式的等性 4.3 有穷自动机 4.3.1 确定...

    java源码包3

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    java源码包4

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密 Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。 设定字符串为“张三,你好,我是李四”...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密 Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。 设定字符串为“张三,你好,我是李四”...

    JAVA上百实例源码以及开源项目源代码

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    JAVA上百实例源码以及开源项目

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

Global site tag (gtag.js) - Google Analytics