(全排列)数组的全排列问题

  • 问题一:https://www.nowcoder.com/practice/f0069cfcd42649e3b6b0c759fae8cde6?tpId=46&tqId=29148&tPage=3&rp=3&ru=/ta/leetcode&qru=/ta/leetcode/question-ranking

    • 这个题目意思是给定一个排列数组,然后要求出下一个排列的数组。比如说1234->1243    1243->1324等等。这个题目是一个全排列的题目,但是只是要求出后一个排列。STL容器中有一个next_permutation函数是来求出数组的所有排列的。
    • STL里面的next_permutation算法的主要思路:在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii。然后再从尾端寻找另一个元素*j,如果满足*i < *j,即将第i个元素与第j个元素对调,并将第ii个元素之后(包括ii)的所有元素颠倒排序,即求出下一个序列了。
    • next_permutation函数测试代码
      • #include <iostream>
        #include <algorithm>
        #include <vector>
        using namespace std;
        int main(){
            vector<int> str;
            for(int i=1; i<5; i++)
                str.push_back(i);
          //这里我是默认的将输入数组变成了升序排序,如果要求全排列必须重新排序。    sort(str.begin(), str.end());
            while(next_permutation(str.begin(), str.end())){
                for(int i=0; i<4; i++)
                    cout<<str[i];
                cout<<endl;
            }
        }
      • 测试结果
    • 好回到这个题目:主要思路和这个函数的解法是类似的,给出代码
      class Solution {
      public:
          void nextPermutation(vector<int> &num) {
              int size = num.size();
              if (size < 2)
                  return ;
              int i, j;
              for (i=size-2; i>=0; --i)//这个是从后往前找到相邻的两个数,其中第i个数小于第i+1个数
                  if (num[i] < num[i+1])
                      break;
              for (j=size-1; j>i; --j)//这个是从后找到一个大于第i个数的j
                  if (num[j] > num[i])
                      break;
              if (i>=0) swap(num[i], num[j]);//交换第i个数和第j个数
              reverse(num.begin()+i+1, num.end());//再将第i+1后面的所有数字进行翻转,就形成了下一个序列
          }
      };
  • 问题二:https://www.nowcoder.com/practice/4bcf3081067a4d028f95acee3ddcd2b1?tpId=46&tqId=29133&tPage=1&rp=1&ru=/ta/leetcode&qru=/ta/leetcode/question-ranking
  • 这个题目是求全排列的问题,其实只要稍微处理一下上面那个求后一个排列的函数,让他来个bool类型的返回值,(这里有一个重点:必须要是升序数组哦)。这样就可以实现了。
  • 代码:
    class Solution {
    public:
        bool nextPermutation(vector<int> &num) {
            int size = num.size();
            if (size < 2)
                return false;
            int i, j;
            for (i=size-2; i>=0; --i)//这个是从后往前找到相邻的两个数,其中第i个数小于第i+1个数
                if (num[i] < num[i+1])
                    break;
            for (j=size-1; j>i; --j)//这个是从后找到一个大于第i个数的j
                if (num[j] > num[i])
                    break;
            if (i>=0) swap(num[i], num[j]);//交换第i个数和第j个数
            reverse(num.begin()+i+1, num.end());//再将第i+1后面的所有数字进行翻转,就形成了下一个序列
    
            return i>=0;
        }
        vector<vector<int> > permute(vector<int> &num) {
            vector<vector<int> > res;
            sort(num.begin(), num.end());//这里是求全排列,所以我们需要将数组重新排序才能得出
               do{
                res.push_back(num);
            }while(nextPermutation(num));
            return res;
        }
    };
  • 问题三:用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列,如 abc 的全排列: abc, acb, bca, dac, cab, cba
    • 方法一:全排列的递归实现(不包含重复的元素)

      • 思路:为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑213和321这二个数是如何得出的。显然这二个都是123中的1与后面两数交换得到的。然后可以将123的第二个数和每三个数交换得到132。同理可以根据213和321来得231和312。因此可以知道——全排列就是从第一个数字起每个数分别与它后面的数字交换。找到这个规律后,递归的代码就很容易写出来了:
      • 代码:

            #include<iostream>
            using namespace std;
            #include<assert.h>
            #include <stdio.h>
        
            void Permutation(char* pStr, char* pBegin)
            {
                assert(pStr && pBegin);
        
                if(*pBegin == ‘\0‘)
                    printf("%s\n",pStr);
                else
                {
                    for(char* pCh = pBegin; *pCh != ‘\0‘; pCh++)
                    {
                        swap(*pBegin,*pCh);
                        Permutation(pStr, pBegin+1);
                        swap(*pBegin,*pCh);
                    }
                }
            }
        
            int main(void)
            {
                char str[] = "abc";
                Permutation(str,str);
                return 0;
            }
    • 方法二:去掉重复元素的全排列的递归实现。
      • 思路:由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

        换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。

        这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。下面给出完整代码:

      • 代码:

            #include<iostream>
            using namespace std;
            #include<assert.h>  
        
            //在[nBegin,nEnd)区间中是否有字符与下标为pEnd的字符相等
            bool IsSwap(char* pBegin , char* pEnd)
            {
                char *p;
                for(p = pBegin ; p < pEnd ; p++)
                {
                    if(*p == *pEnd)
                        return false;
                }
                return true;
            }
            void Permutation(char* pStr , char *pBegin)
            {
                assert(pStr);  
        
                if(*pBegin == ‘\0‘)
                {
                    static int num = 1;  //局部静态变量,用来统计全排列的个数
                    printf("第%d个排列\t%s\n",num++,pStr);
                }
                else
                {
                    for(char *pCh = pBegin; *pCh != ‘\0‘; pCh++)   //第pBegin个数分别与它后面的数字交换就能得到新的排列
                    {
                        if(IsSwap(pBegin , pCh))
                        {
                            swap(*pBegin , *pCh);
                            Permutation(pStr , pBegin + 1);
                            swap(*pBegin , *pCh);
                        }
                    }
                }
            }  
        
            int main(void)
            {
                char str[] = "baa";
                Permutation(str , str);
                return 0;
            }  
    • 方法三就是前面的非递归实现,大体思路是和上面的一样的,实现不一样而已。
  • 问题四:输入两个整数n和m,从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合。
    • 思路:

      • 1、问题其实本质上就是0/1背包问题,对于每一个n,我们采用贪婪策略,先考察是否取n,如果取n,那么子问题就变成了find(n-1,m-n),而如果舍弃n,子问题则为find(n-1,m)。

        2、那么,如何制定解的判定策略?我们知道,递归需要边界条件,而针对背包问题,边界条件只有两种,如果n<1或者m<1,那么便相当于“溢出”,无法combo出m,而另一种可能就是在剩余的n个里恰好满足m==n,即此时 背包刚好填充满,输出一组解单元。除此之外,再无其他。

        注:我们设置flag背包,用来标注对应的n+1是否被选中,1表示被选中,0则表示未选中,每当满足m==n时,则输出一组解。程序容易产生逻辑bug的地方在于length的使用(读者可以思考一下为何需要全局变量length,而不是直接使用n来代替for循环)。

    • 代码

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      int length;
      
      void findCombination(int n,int m,int *flag)
      {
          if(n < 1 || m < 1)
              return;
          if(n > m)
              n = m;
          if(n == m)
          {
              flag[n-1] = 1;
              for(int i=0;i<length;i++)
              {
                  if(flag[i] == 1)
                      printf("%d\t",i+1);
              }
              printf("\n");
              flag[n-1] = 0;
          }
          flag[n-1] = 1;
          findCombination(n-1,m-n,flag);
          flag[n-1] = 0;
      
          findCombination(n-1,m,flag);
      }
      
      int main()
      {
          int n, m;
          scanf("%d%d",&n,&m);
          length = n;
          int *flag = (int*)malloc(sizeof(int)*length);
          findCombination(n,m,flag);
          free(flag);
          return 0;
      }
时间: 2024-10-06 22:54:47

(全排列)数组的全排列问题的相关文章

数组的全排列

1.问题背景 学过数学的人都知道,全排列的意思是什么.现在如何用计算机的编程语言实现数组的全排列呢? 数组的全排列可用于求解八皇后问题,具体参见:全排列解决八皇后问题.与此同时,全排列经常会出现在笔试或者面试,如求字符串的全排列.之所以那它作为考题,因为它难度适中,既可以考察递归实现,又能进一步考察非递归的实现,便于区分出考生的水平.所以,掌握它很重要. 2.全排列的递归实现 2.1求解思路 全排列表示把集合中元素的所有按照一定的顺序排列起来,使用P(n, n) = n!表示n个元素全排列的个数

我的算法学习(一)----数组的全排列

看见别人写出来美丽有用的代码,最终下定决心好好学习算法,在这里记录下自己学习的成果. 前两天看到数组的全排列,于是自己照着别人的想法实现了一下,感觉自己理解了,有点小高兴,记载一下. /* * 数组的全排列*/ public class myAllSort { public static void sort(int[] number,int start,int end){ int temp; // 假设发现数组对掉元素到了最后一个,那么就输出,证明已经符合要求 if(start==end) {

求数组的全排列

给定一个数组,求出全排列的情形? 算法描述: /** - 给定数组 3 4 6 9 8 7 5 2 1 如何求出紧挨着的下一个排列? step1:从后面扫描,找到第一个下降的数(6),并记录: step2:依然从后面扫描,找到第一个大于step1(6)的数7,并记录: step3:交换step1(6).step2(7):=>3 4 7 9 8 6 5 2 1 step4:在step(7)之后的数字,首尾互换 =>3 4 7 1 2 5 6 8 9即为所求 */ static int count

多个数组进行全排列

package com.huang.solution; import java.util.ArrayList; import java.util.Arrays; /** * Created by huang on 17-4-9. */ public class QuanPaiLie { /** * 多个数组全排列 * 思路:数字的第一位是第一个数组中的一个数,下一个数字为下一个数组中的一个数 * 以此类推采用递归 * @param args */ public static void main(

产生一个数组的全排列,非冗余 C++实现

finds all the permutaitons of n elements, repeated elements are allowed and do not create redundant permutations. 蛋白质组学Ms-TopDown源代码auxfun.cpp中一个函数,看了很久没有注释,有点晕,但是以后可以直接拿来使用. 产生原始数组元素的全排列,但是要求是非冗余的,也就是说原始数组可以有重复的元素,比如{2,2,2}这个数组,只有一种排列,2这个元素必须按相同处理,只

[转载]数组的全排列问题

 申明:转自http://blog.csdn.net/wencheng2998/article/details/5971194 1 #include <iostream> 2 using namespace std; 3 int cnt = 0;//累计全排列的总数 4 5 6 void swap(char *a, char *b) 7 { 8 int temp; 9 temp = *a; 10 *a = *b; 11 *b = temp; 12 } 13 //k表示从下表为k的元素开始排列,

字符串数组的全排列——数组

题目描写叙述: 输入一个字符串,打印出该字符串中字符的全部排列. 解题思路: 參考july大神的编程艺术系列.使用字典排序.求当前排列的下一个字典序列.即全排列的下一个排列. 所谓字典序列,即给定两个偏序集A和B,(a,b)和(a′,b′)属于笛卡尔集 A × B.则字典序定义为 (a,b) ≤ (a′,b′) 当且仅当 a < a′ 或 (a = a′ 且 b ≤ b′). 所以给定两个字符串.逐个字符比較,那么先出现较小字符的那个串字典顺序小.假设字符一直相等.较短的串字典顺序小. 比如:a

字符数组的全排列

package structure; import org.junit.Test; /*递归全排列*/ public class perm { public void perm1(char[] a,int start){ if(start==a.length-1){ for(int i=0;i<a.length;i++){ System.out.print(a[i]); } System.out.println(); }else { for(int i=start;i<a.length;i++

javascript数组元素全排列

多个数组(数量不定)例如三个数组 {a,b} {1,2} {d}排列组合后为a,1,da,2,db,1,db,2,d是js的算法哦 var arr = [["a","b"],["1","2"],["d"]]; var sarr = [[]]; for (var i = 0; i < arr.length; i++) {     var tarr = [];     for (var j = 0; j