zoukankan      html  css  js  c++  java
  • 编程之法:面试和算法心得(字符串的全排列)

    内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java

    题目描述

    输入一个字符串,打印出该字符串中字符的所有排列。

    例如输入字符串abc,则输出由字符a、b、c 所能排列出来的所有字符串

    abc、acb、bac、bca、cab 和 cba。

    分析与解法

    解法一、递归实现

    从集合中依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理,从而得到所有元素的全排列。以对字符串abc进行全排列为例,我们可以这么做:以abc为例

    • 固定a,求后面bc的排列:abc,acb,求好后,a和b交换,得到bac
    • 固定b,求后面ac的排列:bac,bca,求好后,c放到第一位置,得到cba
    • 固定c,求后面ba的排列:cba,cab。
       public static void allString(char[] str, int i) {  
                  if (str.length<=i||i<0)  
                      return;  
                  if (i == str.length - 1) {  
                      System.out.println(String.valueOf(str));//记录字符串  
                  } else {  
                      for (int j = i; j < str.length; j++) {  
                          if(is_same(str,i,j)){  
                              swap(str,i,j);  
                              allString(str, i + 1);  
                              swap(str,i,j);  
                          }  
             
                      }  
                  }  
              }  
                
              //判断从子串的第一个字符串开始,直到j-1位置,看是否有重复的字符,若有重复字符,则不再交换  
              public static boolean is_same(char[] str,int begin,int j){  
                 for(int i=begin;i<j;i++){  
                   if(str[i] == str[j]){  
                     return false;  
                    }  
                 }  
                     return true;  
              }  
                
              //两个字母进行交换  
              public static void swap(char[] str,int i,int j){  
                  char temp = str[j];  
                  str[j] = str[i];  
                  str[i] = temp;  
              }

      解法二、字典序排列

      首先,咱们得清楚什么是字典序。根据维基百科的定义:给定两个偏序集A和B,(a,b)和(a′,b′)属于笛卡尔集 A × B,则字典序定义为

      (a,b) ≤ (a′,b′) 当且仅当 a < a′ 或 (a = a′ 且 b ≤ b′)。

      所以给定两个字符串,逐个字符比较,那么先出现较小字符的那个串字典顺序小,如果字符一直相等,较短的串字典顺序小。例如:abc < abcd < abde < afab。

      那有没有这样的算法,使得

      • 起点: 字典序最小的排列, 1-n , 例如12345
      • 终点: 字典序最大的排列,n-1, 例如54321
      • 过程: 从当前排列生成字典序刚好比它大的下一个排列

      答案是肯定的:有,即是STL中的next_permutation算法。

      在了解next_permutation算法是怎么一个过程之前,咱们得先来分析下“下一个排列”的性质。

      • 假定现有字符串(A)x(B),它的下一个排列是:(A)y(B’),其中A、B和B’是“字符串”(可能为空),x和y是“字符”,前缀相同,都是A,且一定有y > x。
      • 那么,为使下一个排列字典顺序尽可能小,必有:
        • A尽可能长
        • y尽可能小
        • B’里的字符按由小到大递增排列

      现在的问题是:找到x和y。怎么找到呢?咱们来看一个例子。

      比如说,现在我们要找21543的下一个排列,我们可以从左至右逐个扫描每个数,看哪个能增大(至于如何判定能增大,是根据如果一个数右面有比它大的数存在,那么这个数就能增大),我们可以看到最后一个能增大的数是:x = 1。

      而1应该增大到多少?1能增大到它右面比它大的那一系列数中最小的那个数,即:y = 3,故此时21543的下一个排列应该变为23xxx,显然 xxx(对应之前的B’)应由小到大排,于是我们最终找到比“21543”大,但字典顺序尽量小的23145,找到的23145刚好比21543大。

      由这个例子可以得出next_permutation算法流程为:

      next_permutation算法

        • 定义
          • 升序:相邻两个位置ai < ai+1,ai 称作该升序的首位
        • 步骤(二找、一交换、一翻转)
        • 找到排列中最后(最右)一个升序的首位位置i,x = ai
        • 找到排列中第i位右边最后一个比ai 大的位置j,y = aj
        • 交换x,y
        • 把第(i+ 1)位到最后的部分翻转

      还是拿上面的21543举例,那么,应用next_permutation算法的过程如下:

      • x = 1;
      • y = 3
      • 1和3交换
        • 得23541
      • 翻转541
        • 得23145

      23145即为所求的21543的下一个排列。参考实现代码如下:

    /*
             * next_permutation实现思路: 在当前序列中,从尾端向前寻找两个相邻元素,前一个记为*i,后一个记为*j,并且满足*i < *j。然后再从尾端寻找另一个元素*t,如果满足*i < *t,即将第i个元素与第t个元素对调,并将第j个元素之后(包括j)的所有元素颠倒排序,即求出下一个序列了。
             * 即C++中的next_permutation
             */
            static int[] array = new int[10];
            static void reverse(int begin, int end) {
                int mid = (begin+end) / 2;
                int cnt = 0;
                for(; ;) {
                    if(cnt > mid-begin) break;
                    int t = array[begin+cnt]; array[begin+cnt] = array[end-cnt]; array[end-cnt] = t;
                    cnt++;
                }
            }
            static boolean next_per(int begin, int end) {
                if(begin == end)  return false;   //为空
                if(begin+1 == end) return false;  //只有一个元素
                int i = end;
                for(; ;) {
                    int j = i; --i;
                    if(array[i] < array[j]) {                 //前<后   相邻
                        int t = end;
                        while(array[i] >= array[t]) {
                            --t;
                        }
    
                        //System.out.println(array[i] +"~"+ array[t]);
                        int temp = array[i]; array[i] = array[t]; array[t] = temp;
                        reverse(j, end);
                        return true;
                    }
                    if(i == begin) {              //前一元素已经指向首元素,反转整个区间,无下一元素    dcba的下一个排列是abcd
                        reverse(begin, end);
                        return false;
                    }
                }
            }      

    参考了一下两个博客

    C++ STL之next_permutation小证明与Java实现

    字符串全排列算法理解与java实现

  • 相关阅读:
    学习H5一周随笔
    vue项目中vux的使用
    git操作常用命令
    vue2.0 实现全选和全不选
    鼠标事件以及clientX、offsetX、screenX、pageX、x的区别
    js编写当天简单日历
    UIView.frame的骗局
    设计模式笔记感悟
    实用图像处理入门
    实用图像处理入门
  • 原文地址:https://www.cnblogs.com/icysnow/p/8202547.html
Copyright © 2011-2022 走看看