11 Exercise 2.8 Output full permutation (programming questions) [PTA Zhejiang University Edition "Data Structure (2nd Edition)" topic set]

11 Exercise 2.8 Output full permutation (programming questions) [PTA Zhejiang University Edition "Data Structure (2nd Edition)" topic set]

1. Link to the original title

Exercise 2.8 Output full permutation (pintia.cn)

2. Topic description

Please write a program to output the former nnFull permutation of n positive integers (n < 10 n<10n<10 ), and passed 9 test cases (iennn from 1 to 9) observennThe running time of the program when n gradually increases.

Input format:

The input gives a positive integer nnn < 10 <10 <10)。

Output format:

output 1 to nnThe full permutation of n . Each permutation occupies one line, with no spaces between numbers. The output order of the arrangement is lexicographic order, that is, the sequencea 1 , a 2 , ⋯ , an { a_1, a_2, \cdots, a_n }a1,a2,,anRanked in sequence b 1 , b 2 , ⋯ , bn { b_1, b_2, \cdots, b_n }b1,b2,,bnBefore, if kk existsk使得 a 1 = b 1 , ⋯   , a k = b k a_1=b_1, \cdots, a_k=b_k a1=b1,,ak=bkAnd ak + 1 < bk + 1 a_{k+1}<b_{k+1}ak+1<bk+1

Input sample:

3

Sample output:

123
132
213
231
312
321

3. Refer to the answer

answer one

#include<stdio.h>
int a[9];
void arrange(int m, int n){
    
    
	int i,j,num;
	if(m==n){
    
    
        for(i=0;i<n;i++)
            for(j=i+1;j<n;j++)
                if(a[i]==a[j])
                    return;
        for(i=0;i<n;i++)
            printf("%d",a[i]);
        printf("\n");
	}
	else{
    
    
		for(num=1;num<=n;num++){
    
    
            a[m]=num;
            arrange(m+1, n);
		}
	}
}
int main(){
    
    
    int n;
    scanf("%d", &n);
    arrange(0, n); 
    return 0;
}

answer two

#include<stdio.h>
int a[9];
void arrange(int m, int n){
    
    
	int i,j,num;
	if(m==n){
    
    	
		for(i=0;i<n;i++)
			printf("%d",a[i]);
		printf("\n");
	}
	else{
    
    
		for(num=1;num<=n;num++){
    
    
			for(j=0;j<m;j++)
				if(a[j]==num)break;
			if(j==m){
    
    
				a[m]=num;
				arrange(m+1, n);
			}
		}
	}
}
int main(){
    
    
    int n;
    scanf("%d", &n);
    arrange(0, n); 
    return 0;
}

answer three

#include <stdio.h>

int visited[10]={
    
    0};
int  list[10];
void dfs(int n,int m){
    
    
    int i;
	if(m==n+1){
    
    
		for(int i=1;i<=n;i++)
			printf("%d",list[i]);
		printf("\n");
	}
	else{
    
    
		for(i=1;i<=n;i++){
    
    
			if(!visited[i]){
    
    
				list[m]=i;	
				visited[i]=1;
				dfs(n,m+1);	
				visited[i]=0;
			}
		}
	}
}
int main(){
    
    
	int n;
	scanf("%d", &n);
	dfs(n,1);
	return 0;
} 

answer four

#include <stdio.h>
int a[10];
void shift( int l, int i);
void shiftb( int l, int i );
void Per( int l, int r );
int main(){
    
    
    int n, i;
    scanf("%d", &n);
    for (i=0; i<=n; i++) a[i] = i;
    Per(1, n);
    return 0;
}
void shift( int l, int i){
    
    
     int j, t=a[i];
     for (j=i; j>l; j--)
         a[j]=a[j-1];
     a[l] = t;
}
void shiftb( int l, int i ){
    
    
     int j, t=a[l];
     for (j=l; j<i; j++)
         a[j] = a[j+1];
     a[i] = t;
}
void Per( int l, int r ){
    
    
     int i;
     if (r==l) {
    
    
        for (i=1; i<=r; i++) printf("%d", a[i]);
        printf("\n");
     }
     else {
    
    
          for (i=l; i<=r; i++) {
    
    
              shift(l, i);
              Per(l+1, r);
              shiftb(l, i);
          }
     }
}

4. Problem-solving ideas

nn before outputting this questionFull permutation of n positive integers (n < 10 n<10n<10 ), ifnnn is a fixed value, such as 3, then the full arrangement can be realized through a triple cycle, and the implementation method is as follows:

#include<stdio.h>
int main(){
    
    
	int i,j,k;
	for(i=1;i<=3;i++){
    
    
		for(j=1;j<=3;j++){
    
    
			for(k=1;k<=3;k++){
    
    
				if(i!=j&&j!=k&&i!=k)
					printf("%d%d%d\n",i,j,k);
			}
		}
	}
	return 0;
}

But the nn in this questionn is a variable. If it is realized by loop, the number of layers of loop is uncertain, so it can be realized by recursion. Three things can be seen from the implementation of the loop:

  1. Each layer of loops selects a number in order, and calling recursion naturally selects a number in order for each layer of calls;

  2. The output is output when the loop reaches the last layer, and calling the recursive function is naturally also recursively called to the last layer for output.

    //m记录当前层级,若当前层级m到达最大层级n,输出排列完毕之后的数组
    if(m==n){
          
          	
        for(i=0;i<n;i++)
            printf("%d",a[i]);
        printf("\n");
    }
    

    At the same time, the recursive function of the full arrangement requires at least two parameters: 1. The level of the current call; 2. The largest level, that is, the input nnn

    //排列permutation
    void Per( int now, int n )
    
  3. Each digit of the output is not repeated. If you want to make the output of each digit not repeated, there are four algorithms:

    • Algorithm 1: The above triple loop is to check the repeatability of each digit before outputting, nn in this questionn is uncertain, that is, the number of numbers is uncertain. If you check the repeatability of each digit before outputting, you need to use a double loop to compare each digit with each digit of other digits. The last one of the algorithm The test point will time out.
    • Algorithm 2: Optimize Algorithm 1, instead of checking the repeatability of numbers at the end, check when entering each layer. As can be seen from the triple loop in the previous example, each layer of arrangement traverses all the numbers. For example, the first layer of loop traverses 1 to 3, and the second layer of loop also traverses 1 to 3. Check whether the number selected at the current level has appeared in the previous level. If the current number has appeared in the previous position, skip the number. If the current number has not appeared in the previous position, put the number into the position of the current level of the array .
    • Algorithm 3: Further optimize Algorithm 2, do not use the traversal method to check the repeatability, mark each used number, and judge whether the selected number can be used by marking.
    • Algorithm 4: First create an array int a[10];, initialize it for (i=0; i<=n; i++) a[i] = i;, adjust the position of the array elements, and arrange the array completely, because the values ​​of the array elements have been determined from 1 to 9, and there will be no repetition of different digits. This is the approach used in the reference answer to this question.

5. Detailed answer

Detailed answer

The algorithm will time out at the last test point.

Enumerate the possible values ​​of each position, generate all possible permutations, realize the full permutation of integers from 1 to n, and use an array to store the value at each position, check whether the number is repeated before outputting, will satisfy Permutation output with no repeating elements.

The specific analysis is as follows:

  1. A global array is defined int a[9];to store each generated permutation.
  2. Defines a recursive function void arrange(int m, int n);whose job it is to generate permutations.
  3. arrangeThe parameters of the function mindicate the position of the array currently being generated, nand indicate the length of the array to be generated.
  4. When m=n, it means that a complete array has been generated. At this time, it is necessary to check whether there are repeated elements in the array, and if so, return it; otherwise, output the array and wrap it.
  5. When m<n, it is necessary to continue to generate the arrangement. At this time, try to fill in the current position m one by one from 1 to n, and then recursively generate the arrangement of the next position.
  6. In mainthe function, first read in the array length to be generated n, and then call arrangethe function to set the initial position 0.
#include<stdio.h>
int a[9];
//m是当前层级,n是最大层级
void arrange(int m, int n){
    
    
	int i,j,num;
    //数组排列到n-1位置就排列完了,m==n排列完成输出
	if(m==n){
    
    
        //二重循环检查每位数字的重复性
        for(i=0;i<n;i++)
            for(j=i+1;j<n;j++)
                if(a[i]==a[j])//如果有数字重复则不输出
                    return;
        //如果程序执行到这里,则没有数字重复输出结果
        for(i=0;i<n;i++)
            printf("%d",a[i]);
        printf("\n");
	}
	else{
    
    
         //每一层都遍历所有的数字1至n
		for(num=1;num<=n;num++){
    
    
            a[m]=num;
            arrange(m+1, n);//递归方程计算下一个层级的数字
		}
	}
}
int main(){
    
    
    int n;
    scanf("%d", &n);
    arrange(0, n); 
    return 0;
}

Answer two detailed explanation

Optimized on the basis of Algorithm 1, when selecting numbers at each layer, traverse the previous numbers to check whether the numbers are repeated.

#include<stdio.h>
int a[9];
//m是当前层级,n是最大层级
void arrange(int m, int n){
    
    
	int i,j,num;
    //数组排列到n-1位置就排列完了,m==n排列完成输出 
	if(m==n){
    
    	
		for(i=0;i<n;i++)
			printf("%d",a[i]);
		printf("\n");
	}
	else{
    
    
         //每一层都遍历所有的数字1至n
		for(num=1;num<=n;num++){
    
    
             //检查当前选择的数字num是否在前面的位置j出现过
			for(j=0;j<m;j++)
                 //如果当前选择的数字num在前面的位置j出现过则退出此循环,遍历下一个数字
				if(a[j]==num)break;
             //结束上面的循环之后,若j==m,即当前选择的数字num在之前没有出现过
			if(j==m){
    
    
				a[m]=num;
				arrange(m+1, n);//递归方程计算下一个层级的数字
			}
		}
	}
}
int main(){
    
    
    int n;
    scanf("%d", &n);
    arrange(0, n); 
    return 0;
}

Answer three detailed explanation

Depth-first search (DFS). Iterate over all combinations and check whether the number has been used by marking when selecting the number at each layer.

The array int visited[10]records whether a number has been used, which visited[i] = 0indicates that the number i has not been used, and visited[i] = 1indicates that the number i has been used.

The array int list[10];represents the resulting permutation, where list[i]represents ithe number at position 1 in the permutation.

The function dfs(n, step) indicates the situation when the search is at the step position of the generated array, where n indicates the length of the array (ie 1~n), and step indicates the current search position.

When step = n+1, a complete arrangement has been generated, just output it directly.

Otherwise, try to use the number in 1~n as the number of the current position in turn, and mark the number in the visited array as already used, and then recursively search for the next position. When backtracking to the current location, it is necessary to unmark the visited array.

#include <stdio.h>

int visited[10]={
    
    0}; //做标记,数组下标对应相应的数字,没用过记作 0, 用过后记作 1 
int  list[10];
//m是当前层级,n是最大层级
void dfs(int n,int m){
    
    
    int i;
    //m==n+1时,n个层级排列完毕
	if(m==n+1){
    
    
		for(int i=1;i<=n;i++)
			printf("%d",list[i]);
		printf("\n");
	}
	else{
    
    
         //每一层都遍历所有的数字1至n
		for(i=1;i<=n;i++){
    
    
			if(!visited[i]){
    
    //如果选择的数字没用过 
				list[m]=i;	//把它存入数组对应层级的位置
				visited[i]=1;	//标记为已使用
				dfs(n,m+1);	//进入下一层
				visited[i]=0;	//取消标记
			}
		}
	}
}
int main(){
    
    
	int n;
	scanf("%d", &n);
	dfs(n,1);	//代表从第一层开始搜索 
	return 0;
} 

Detailed answer four

Adjust the position of the array elements and arrange the array completely.

The specific analysis is as follows:

  1. defines an 10integer array of length a[], and declares three functions: shift(), shiftb()and Per().
  2. In main()the function, first input an integer n, and then use a loop to a[]initialize the array to integers from 1 to n.
  3. Then call Per()the function.
  4. In Per()the function, if the left and right ends are the same, the current full arrangement from 1to to is output.n
  5. Otherwise, for each of the ranges from the left endpoint lto the right endpoint , do the following: ri
    • Call shift()the function to move the th element a[]in the array to the position of the left endpoint.il
    • Operates recursively on the range l+1reached .r
    • Call shiftb()the function to a[]restore the array to its original state.
#include <stdio.h>
int a[10];//程序中用不到a[0],只使用a[1]至a[9]
void shift( int l, int i);
void shiftb( int l, int i );
void Per( int l, int r );

int main(){
    
    
    int n, i;
    scanf("%d", &n);
    for (i=0; i<=n; i++) a[i] = i;
    Per(1, n);
    return 0;
}

//对数组元素进行交换,将数组a[]中第i个元素移动到左端点l的位置
void shift( int l, int i){
    
    
     int j, t=a[i];
     for (j=i; j>l; j--)
         a[j]=a[j-1];
     a[l] = t;
}
//对数组元素进行交换,将数组a[]还原到原始状态
void shiftb( int l, int i ){
    
    
     int j, t=a[l];
     for (j=l; j<i; j++)
         a[j] = a[j+1];
     a[i] = t;
}
//排列permutation
void Per( int l, int r ){
    
    
     int i;
     //如果左右两端点相同,则排列完毕,输出当前的全排列
     if (r==l) {
    
    
        for (i=1; i<=r; i++) printf("%d", a[i]);
        printf("\n");
     }
     else {
    
    
          for (i=l; i<=r; i++) {
    
    
              //将数组a[]中第i个元素移动到左端点l的位置
              shift(l, i);
              //每一层调用都会有一个for循环,for循环改变每一层函数调用的左边界位置的数字
              Per(l+1, r);
              //每一层会先改变了元素位置,内层函数执行完毕后,再把位置改变回去以便下一个循环排列
              shiftb(l, i);
          }
     }
}

6. Knowledge expansion

depth-first search

This question mainly reviews recursion. Depth-first search will be explained in detail later, and here is only a brief introduction.

Depth-First Search (DFS for short) is a commonly used graph traversal algorithm, which is implemented by traversing nodes in the graph in depth.

Specifically, the DFS algorithm starts from a certain node in the graph, first visits the node, and then selects an adjacent unvisited node to continue to visit until all adjacent nodes have been visited. If the current node has no unvisited adjacent nodes, backtrack to the previous node and continue to visit its unvisited adjacent nodes until all nodes have been visited.

The DFS algorithm can be implemented using recursion or using a stack. When using recursive implementation, you can use a visited array to record the visited nodes to prevent repeated visits; when using stack implementation, you can use the last-in-first-out (LIFO) feature of the stack to save the unvisited neighbors of the current node node.

Guess you like

Origin blog.csdn.net/weixin_40171190/article/details/130048516