zoukankan      html  css  js  c++  java
  • 剑指offer系列——27.字符串的排序*(全排序)

    Q:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
    输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
    T:
    (部分内容引自:https://www.cnblogs.com/cxjchen/p/3932949.html)
    我们先来看一个计算题:

    字符串“alibaba”有_____个不同的排列。
    A. 5040 B. 840 C. 14 D.420
    计算方法:(A_7^7/(A_2^2*A_3^3)=420)

    A.
    递归算法
    对于字符串的排列问题:如果能生成n-1个元素的全排列,就能生成n个元素的全排列。对于只有一个元素的集合,可以直接生成全排列。所以全排列的递归终止条件很明确,只有一个元素时。我们可以分析一下全排列的过程:
    1.首先,我们固定第一个字符a,求后面两个字符bc的排列
    2.当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列
    3.现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列
    4.既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了

    还有一个问题要注意,就是如果字符串中有重复的字符串.由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换 了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不 同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
    换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
    这样,我们得到在全排列中去掉重复的规则:
    去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。

    代码:

    private:
        vector<string> result;
    public:
        vector<string> Permutation(string str) {
            if(str.empty())
                return result;
            sort(str.begin(), str.end());
            Permutations(str, 0);
            return result;
        }
    
        void Permutations(string str, int index) {
            int size = str.size();
            if (index == size) {
                //递归到最后,可以把结果放入了
                result.push_back(str);
                return;
            }
            for (int i = index; i < size; i++) {
                if (str[i] == str[i + 1])
                    //如果相同后面字母和前面相同,就不需要交换了,解决重复的问题
                    continue;
                //交换子串的第一个字符和后续字符
                swap(str[i], str[index]);
                //子串递归
                Permutations(str, index + 1);
            }
        }
    

    或者用回溯法做
    1.没有重复的

        private List<List<Integer>> result;
    
        public List<List<Integer>> permuteUnique(int[] nums) {
            if (nums.length == 0)
                return result;
            result = new LinkedList<>();
            List<Integer> list = new LinkedList<>();
            permute(nums, list);
            return result;
        }
    
        private void permute(int[] nums, List<Integer> list) {
            if (list.size() == nums.length) {
                List<Integer> list1 = new LinkedList<>(list);
                result.add(list1);
                return;
            }
            for (int num : nums) {
                if (list.contains(num))
                    continue;
                list.add(num);
                permute(nums, list);
                list.remove(list.size() - 1);
            }
        }
    

    2.有重复

        private List<List<Integer>> result;
        private boolean[] need;//判断一下是否读取过
    
        public List<List<Integer>> permuteUnique(int[] nums) {
            if (nums.length == 0)
                return result;
            result = new LinkedList<>();
            need = new boolean[nums.length];
            Arrays.fill(need, false);
            List<Integer> list = new LinkedList<>();
            Arrays.sort(nums);//一定记得要sort,不然后面相等略去就过不去
            permute(nums, list);
            return result;
        }
    
        private void permute(int[] nums, List<Integer> list) {
            if (list.size() == nums.length) {
                List<Integer> list1 = new LinkedList<>(list);
                result.add(list1);
                return;
            }
            for (int i = 0; i < nums.length; i++) {
                if (need[i])
                    continue;
                list.add(nums[i]);
                need[i] = true;
                permute(nums, list);
                list.remove(list.size() - 1);
                need[i] = false;
                while (i + 1 < nums.length && nums[i] == nums[i + 1])//有相等的跳过去
                    i++;
            }
        }
    

    字典排序算法:(非递归算法,引用:https://blog.csdn.net/happyrocking/article/details/83619392)
    字典序算法用来解决这样一个问题:给定其中一种排列,求基于字典序的下一种排列。
    比如给定一种排列为 abc,则其基于字典序的下一种排列为 acb。
    要求下一种排列既要比原排列大,又不能有第三种排列位于他俩之间。即下一种排列为大于原排列的最小排列。
    以输入为 358764 为例,字典序算法的步骤:
    1、从原排列中,从右至左,找到第一个左邻小于右邻的字符,记左邻位置为 a。
    示例中 a=1,list[a] = 5。
    2、重新从右至左,找到第一个比 list[a] 大的字符,记为位置为 b。
    示例中 b=4,list[b] = 6。
    3、交换 a 和 b 两个位置的值。
    示例变为了 368754。
    4、将 a 后面的数,由小到大排列。
    示例变为了 364578。
    算法结束,输出 364578。

    用于全排列时,如图:

    代码:

    private:
        vector<string> result;
    public:
        vector<string> Permutation(string str) {
            if (str.empty())
                return result;
            sort(str.begin(), str.end());
            int size = str.size();
            int left = size - 1;
            int right = size - 1;
            bool flag = false;
            while (true) {
                result.push_back(str);
                for (int i = size - 1; i >= 0; i--) {
                    if (str[i] < str[i + 1]) {
                        flag = true;
                        left = i;
                        break;
                    }
                }
                if (!flag)
                    break;
                flag = false;
                for (int i = size - 1; i >= 0; i--) {
                    if (str[i] > str[left]) {
                        right = i;
                        break;
                    }
                }
                swap(str[left], str[right]);
                sort(str.begin() + left + 1, str.end());
                left = right = size - 1;
            }
            return result;
        }
    

    当然,实际上……C++的STL库中直接有全排序的函数,前提是数据必须有序。next_permutation(temp.begin(), temp.end())求的是当前排列的下一个排列,所以用do_while防止缺失默认第一种情况。

    private:
        vector<string> result;
    public:
        vector<string> Permutation(string str) {
            if(str.empty())
                return result;
            sort(str.begin(), str.end());
            //注意这里要用do_while,否则会缺失默认输入
            do {
                result.push_back(str);
            } while (next_permutation(str.begin(), str.end()));
            return result;
        }
    
  • 相关阅读:
    大数相乘ADT(String)
    ubuntu16.04光流(.flo)转png格式图片
    7. Reverse Integer
    1. Two Sum
    Ubuntu16.04安装opencv3.2(cuda8.0)
    Ubuntu16.04安装PAPI API
    faster r-cnn demo.py理解
    window10远程连接ubuntu16.04
    lab_2 Selenium
    软件测试homework 3
  • 原文地址:https://www.cnblogs.com/xym4869/p/12295311.html
Copyright © 2011-2022 走看看