Java实现查找数组中k个元素所有组合的算法

 

给定一个大小为N的数组,例如e={'A','B','C','D','E'} N = 5,我们想找到该数组中k个元素的所有可能组合。例如,如果k = 3,则一种可能的组合是{'A','B','C'}。在这里,我们有三种不同的算法来查找数组的k个组合。

向前-向后算法

在这里,我们有两个数组和两个主要索引ri

  • 数组e是元素数组。
  • 数组指针,该数组指针用于保存所选元素的索引。
  • 索引i指向数组e中的当前选定元素。
  • 指向指针数组中当前位置的索引r
  • 只要ir不超过数组长度,该算法就会通过增加ir来向前移动。
  • 如果r到达指针数组的最后位置,则会打印一个组合。
  • 如果这两个指数达到其指向数组的最后poisition的algorith将通过减少退步[Rr--,并设置用的价值i = pointer[r]+1

groups_forwardbackward

public static void combination(Object[]  elements, int k){
       
       

	// get the length of the array
	// e.g. for {'A','B','C','D'} => N = 4 
	int N = elements.length;
	
	if(k > N){
       
       
		System.out.println("Invalid input, K > N");
		return;
	}
		
	// init combination index array
	int pointers[] = new int[k];
	

	int r = 0; // index for combination array
	int i = 0; // index for elements array
	
	while(r >= 0){
       
       
	
		// forward step if i < (N + (r-K))
		if(i <= (N + (r - k))){
       
       
			pointers[r] = i;
				
			// if combination array is full print and increment i;
			if(r == k-1){
       
       
				print(pointers, elements);
				i++;				
			}
			else{
       
       
				// if combination is not full yet, select next element
				i = pointers[r]+1;
				r++;										
			}				
		}
		
		// backward step
		else{
       
       
			r--;
			if(r >= 0)
				i = pointers[r]+1;
			
		}			
	}
}

移位算法

  • 该算法比第一个算法更直观。
  • 我们实际上将elements数组分为两种类型的元素:可以选择的k个元素和将被忽略的Nk个元素。
  • 在每次迭代中,我们选择Nk个不可忽略的元素。
  • 每次迭代后,我们将忽略元素的位置移动,如下图所示。

groups_shifting

public static void combination(Object[]  e, int k){
       
       

	int[] ignore = new int[e.length-k]; // --> [0][0]
	int[] combination = new int[k]; // --> [][][]
	
	// set initial ignored elements 
	//(last k elements will be ignored)
	for(int w = 0; w < ignore.length; w++)
		ignore[w] = e.length - k +(w+1);
	
	int i = 0, r = 0, g = 0;
	
	boolean terminate = false;
	while(!terminate){
       
          
		
		// selecting N-k non-ignored elements
		while(i < e.length && r < k){
       
       
    			
    		if(i != ignore[g]){
       
       
    			combination[r] = i;
    			r++; i++;
    		}
    		else{
       
       	    			
    			if(g != ignore.length-1)
    				g++;	    			
    			i++;
    		}
    	}
    	print(combination, e);
    	i = 0; r = 0; g = 0;

    	terminate = true;
    	
    	// shifting ignored indices
    	for(int w = 0 ; w < ignore.length; w++){
       
       
    		if(ignore[w] > w){
       
       	    			
    			ignore[w]--;
    			
    			if(w > 0)
    				ignore[w-1] = ignore[w]-1;
    			terminate = false;
    			break;	    			
    		}
    	}
	}    		
}

递归算法

  • 递归算法具有较短的步骤。
  • 在对函数的每次调用中,我们传递元素列表,k和累积组合。
  • 然后我们有四个条件:
    1. 如果elements.length < k然后停止
    2. 如果k == 1然后将每个元素添加到累积的组合中
    3. 如果是,elements.length == k则将所有元素添加到累积的组合中。
    4. 如果是,elements.length > k则对每个元素e进行递归调用,以传递元素列表的子列表,k-1然后将元素添加e到累积的组合中。
  • 如下图所示

groups_recursive

public static void combination(List<String> e, int k, String accumulated){
       
       

	// 1. stop
	if(e.size() < k)
		return;
	
	// 2. add each element in e to accumulated
	if(k == 1)
		for(String s:e)
			print(accumulated+s);
	
	// 3. add all elements in e to accumulated
	else if(e.size() == k){
       
       
		for(String s:e)
			accumulated+=s;
		print(accumulated);
	}
	
	// 4. for each element, call combination
	else if(e.size() > k)
		for(int i = 0 ; i < e.size() ; i++)
			combination(e.subList(i+1, e.size()), k-1, accumulated+e.get(i));
	
}

源代码@ GitHub

 
 

猜你喜欢

转载自blog.csdn.net/allway2/article/details/114898722