zoukankan      html  css  js  c++  java
  • [LeetCode]Permutation

    给定一系列数字,关于从小到大的所有可能的排列的相关题目

    题目1.next Permutation

    给定一个数字,找到这一系列数字中从小到大排序中该数字的下一个数字值。

    思路:

    按位从后往前(从个位开始往上)找到给的数字的首个非增序的数字;

    然后继续从前往后找到比该数字大且最接近它的数字,交换这两个数字;

    最后,将尾部到首个非增序的一串数字全部反序。

    /***************************************************************************************************
    Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
    If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
    The replacement must be in-place, do not allocate extra memory.
    Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
    1,2,3 → 1,3,2
    3,2,1 → 1,2,3
    1,1,5 → 1,5,1
    ***************************************************************************************************/
    #include<stdio.h>
    
    void reverse(int *nums,int start,int end){
            for(int i = start,j = end;i < j;i++,j--){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
    
    void nextPermutation(int* nums, int numsSize) {
            int i,j,k;
        for(i = numsSize - 1;i >= 1;i--){//从后往前查找
            if(nums[i] > nums[i - 1]){//找到首个不是增序的位置
                int temp = nums[i - 1];
                j = i + 1;
                while(j < numsSize && temp < nums[j])j++;//找到该非增序数最接近且比它大的数字
                nums[i - 1] = nums[j - 1];//交换该非增序数与最接近且比它大的数字
                nums[j - 1] = temp;
                reverse(nums,i,numsSize - 1);//反序从尾部到该非增序数的所有数字
                return;
            }
        }
    
        reverse(nums,0,numsSize - 1);
    }
    
    void main(){
        int a[] = {1,3,1};
    
        nextPermutation(a,sizeof(a)/sizeof(int));
        
        for(int i = 0;i < sizeof(a)/sizeof(int);i++){
            printf("%d ",a[i]);
        }
    }

    题目2:Permutations

    给定各不相同的一串数字,返回他的所有可能排列。

    暴力搜索的思想:

    用一个数组记录所有访问过的数字;

    递归找到没有访问过的数字,然后记录当前可能的数字组合。

    递归的深度是数字的个数。

    /***************************************************************************************************
    Given a collection of distinct numbers, return all possible permutations.
    For example,
    [1,2,3] have the following permutations:
    [
      [1,2,3],
      [1,3,2],
      [2,1,3],
      [2,3,1],
      [3,1,2],
      [3,2,1]
    ]
    ***************************************************************************************************/
    #include<stdio.h>
    
    struct linklist{
        int *val;
        struct linklist *next;
    };
    
    struct linklist *getPermutes(int *nums,int numsSize,int *choosedIds,int step){
        if(step >= numsSize)return NULL;//所有数字都访问过了
        struct linklist *result = NULL;
        struct linklist *head = NULL;
        struct linklist *pre = NULL;
        int j = 0,flag = 0;
        for(int i = 0;i < numsSize;i++){//遍历所有数字
            flag = 0;
            for(j = 0;j < step;j++){//判断当前数字是否访问过
                if(i == choosedIds[j]){
                    flag = 1;
                    break;
                }
            }
            if(flag == 1)continue;
            choosedIds[step] = i;
            struct linklist *p = getPermutes(nums,numsSize,choosedIds,step + 1);
            if(p == NULL){//当前是最后一个数字
                p = (struct linklist *)malloc(sizeof(struct linklist));
                p->val = (int *)malloc(numsSize*sizeof(int));
                p->next = NULL;
            }
            head = p;
            while(p != NULL){//将当前组合加入链表
                p->val[step] = nums[i];
                if(p->next == NULL)pre = p;
                p = p->next;
            }
            pre->next = result;
            result = head;
        }
    
        return result;
    }
    
    /**
     * Return an array of arrays of size *returnSize.
     * Note: The returned array must be malloced, assume caller calls free().
     */
    int** permute(int* nums, int numsSize, int* returnSize) {
            struct linklist *result = NULL;
            struct linklist *head = NULL;
            struct linklist *pre = NULL;
            int count = 0;
        int *choosedIds = (int *)malloc(numsSize*sizeof(int));
        memset(choosedIds,-1,numsSize*sizeof(int));
            result = getPermutes(nums,numsSize,choosedIds,0);
            head = result;
        while(head != NULL){//统计可能组合的个数
            count++;
            head = head->next;
        }
    
        int **ret = (int **)malloc(count*sizeof(int*));
        int i = 0;
        while(result != NULL){//转化为数组
            ret[i] = (int *)malloc(numsSize*sizeof(int));
            head = result;
            result = result->next;
            for(int j = 0;j < numsSize;j++){
                ret[i][j] = head->val[j];
            }
            free(head);
            i++;
        }
    
        *returnSize = count;
        return ret;
    }
    
    void main(){
        int array[] = {1,2,3,4};
        int size = 0;
        int **a = permute(array,sizeof(array)/sizeof(int),&size);
    
        for(int i = 0;i < size;i++){
            for(int j = 0;j < sizeof(array)/sizeof(int);j++){
                printf("%d ",a[i][j]);
            }
            printf("
    ");
            free(a[i]);
        }
        free(a);
    }

    题目3:Permutations II

    给定一串数字(可能相同),返回他的所有可能排列。

    暴力搜索的思想:

    为防止重复先对给定的数字排序;

    用一个数组记录所有访问过的数字;

    递归找到没有访问过的数字,然后记录当前可能的数字组合;

    最后跳过后面和当前数字相等的数字,找到下一个不同的数字。

    递归的深度是数字的个数。

    /***************************************************************************************************
    Given a collection of distinct numbers, return all possible permutations.
    For example,
    [1,2,3] have the following permutations:
    [
      [1,2,3],
      [1,3,2],
      [2,1,3],
      [2,3,1],
      [3,1,2],
      [3,2,1]
    ]
    ***************************************************************************************************/
    #include<stdio.h>
    
    struct linklist{
        int *val;
        struct linklist *next;
    };
    
    int cmp(const void *a , const void *b){
        return *(int *)a > *(int *)b ? 1 : -1;
    }
    
    struct linklist *getPermutes(int *nums,int numsSize,int *choosedIds,int step){
        if(step >= numsSize)return NULL;
        struct linklist *result = NULL;
        struct linklist *head = NULL;
        struct linklist *pre = NULL;
        int j = 0,flag = 0;
        for(int i = 0;i < numsSize;i++){
            flag = 0;
            for(j = 0;j < step;j++){
                if(i == choosedIds[j]){
                    flag = 1;
                    break;
                }
            }
            if(flag == 1)continue;
            choosedIds[step] = i;
            struct linklist *p = getPermutes(nums,numsSize,choosedIds,step + 1);
            if(p == NULL){
                p = (struct linklist *)malloc(sizeof(struct linklist));
                p->val = (int *)malloc(numsSize*sizeof(int));
                p->next = NULL;
            }
            head = p;
            while(p != NULL){
                p->val[step] = nums[i];
                if(p->next == NULL)pre = p;
                p = p->next;
            }
            pre->next = result;
            result = head;
            while(nums[i] == nums[i + 1])i++;//跳过重复元素
        }
    
        return result;
    }
    
    /**
     * Return an array of arrays of size *returnSize.
     * Note: The returned array must be malloced, assume caller calls free().
     */
    int** permuteUnique(int* nums, int numsSize, int* returnSize) {
            struct linklist *result = NULL;
            struct linklist *head = NULL;
            struct linklist *pre = NULL;
            int count = 0;
        int *choosedIds = (int *)malloc(numsSize*sizeof(int));
        memset(choosedIds,-1,numsSize*sizeof(int));
    
        qsort(nums,numsSize,sizeof(int),cmp);//排序
    
            result = getPermutes(nums,numsSize,choosedIds,0);
            head = result;
        while(head != NULL){
            count++;
            head = head->next;
        }
    
        int **ret = (int **)malloc(count*sizeof(int*));
        int i = 0;
        while(result != NULL){
            ret[i] = (int *)malloc(numsSize*sizeof(int));
            head = result;
            result = result->next;
            for(int j = 0;j < numsSize;j++){
                ret[i][j] = head->val[j];
            }
            free(head);
            i++;
        }
    
        *returnSize = count;
        return ret;
    }
    
    void main(){
        int array[] = {1,3,1,2,1,4};
        int size = 0;
        int **a = permuteUnique(array,sizeof(array)/sizeof(int),&size);
    
        for(int i = 0;i < size;i++){
            for(int j = 0;j < sizeof(array)/sizeof(int);j++){
                printf("%d ",a[i][j]);
            }
            printf("
    ");
            free(a[i]);
        }
        free(a);
    }

     题目:Permutation Sequence

    题目给定n和k,表示1-n个数字,它们按顺序组成多种不同的数字,让我们输出第K个。

    思路:

    爆搜,通过递归找到每个数字组合,当找到第K个时,将它返回。

    但是,复杂度太大,无法通过。

    package com.example.medium;
    
    /**
     * ERROR:Time Limit Exceeded
     * @author FuPing
     * 方法不好
     */
    public class GetPermutation {
        int num;//记录数字组合的个数
        StringBuffer str;//返回的字符串
        /**
         *The set [1,2,3,…,n] contains a total of n! unique permutations.
         *By listing and labeling all of the permutations in order,
         *We get the following sequence (ie, for n = 3):
         *1. "123"
         *2. "132"
         *3. "213"
         *4. "231"
         *5. "312"
         *6. "321"
         *Given n and k, return the kth permutation sequence.
         *Note: Given n will be between 1 and 9 inclusive.
         * @author FuPing
         *
         */
        private void getKthPermutation(int n,int k,int []selected,int step){
            if(step >= n) return;
            for(int i = 1;i <= n;i++){
                boolean flag = false;
                for(int j = 0;j < step;j++){//找到下一个未选用数字
                    if(selected[j] == i){
                        flag = true;
                        break;
                    }
                }
                if(flag)continue;
                selected[step] = i;//标记当前选择的数字
                getKthPermutation(n,k,selected,step + 1);
                if(str == null && step == n - 1){//最后一个数字,且找到第一次的数字组合
                    num++;
                }
                if(num == k){//找到当前k个数字组合
                    if(str == null)str = new StringBuffer(i + "");
                    else str.insert(0, i + "");
                    break;
                }
                selected[step] = 0;//还原标记
            }
        }
        public String getPermutation(int n, int k) {
            if(k <= 0)return "";
            if(n == 0){
                if(k == 1)return "0";
                return "";
            }
            int []selected = new int[n];
            num = 0;
            getKthPermutation(n,k,selected,0);
            if(str != null)return str.toString();
            return "";
        }
     
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println(new GetPermutation().getPermutation(9, 206490));
        }
    
    }

    思路2:

    实际上数字组合的每位和K有一定的对应关系。

    关系如下:

    数字的个数  组合总数

      1      1 = 1

      2      2 = 2*1

      3      6 = 3*2*1

      4      24 = 4*3*2*1

      ...      ...

    则如果一共有n个数字,要找其中第k个元素;

    则当k<=6时,最高位必定是1;当6<k<=12,最高位是2;当12<k<=18,最高位是3;当18<k<=24,最高位是4;

    不妨设最高位为m,同理,当k-6m<=2时,次高位去剩下的最小数;当2<k-6m<=4时,次高位去剩下的第二小的数;当4<k-6m<=6时,次高位去剩下的最大数。

    如此类推。

    从上面可以发现,每个数字的界限是一个斐波拉切数。

    package com.example.medium;
    
    public class GetPermutation2 {
    
        private int facs[];
        /**
         * 考虑直接找到kth个解得排序关系,发现它与K的值中各个数的阶乘有关
         * 例如n=4,k=16;k=2*3!+2!+2!;则第一个数应该是3rd大的数字,即是3,同理第二个数是2,最后结果3241
         * @param n
         * @param k
         * @return
         */
        private int factorial(int n){
            if(n == 1){
                facs[n] = 1;
                return 1;
            }
            if(facs[n] == 0)facs[n] = n*factorial(n - 1);
            return facs[n];
        }
        private char findKthNum(char array[],int n,int k){//找到第k个数
            int index = 0;
            char ch = '0';
            for(int i = 0;i < n;i++){
                if(array[i] == '0')continue;//为0表示已用过
                if(index == k){//找到
                    ch = array[i];
                    array[i] = '0';
                    return ch;
                }
                index++;
            }
            return ch;
        }
        public String getPermutation(int n, int k) {
            if(k <= 0)return "";
            if(n == 0){
                if(k == 1)return "0";
                return "";
            }
            facs = new int[n + 1];
            facs[0] = 1;
            factorial(n);//求出1-n的所有斐波拉切数字
            if(k > facs[n])return "";
            char []array = new char[n];
            char []str = new char[n];
            for(int i = 0;i < n;i++)array[i] = (char) (i + '1');
            for(int i = 0;i < n;i++){//从最后一位向前求组合数字
                int index = k/facs[n - i - 1];
                k = k%facs[n - i - 1];//下一位的数字的对应k值
                if(k > 0){
                    str[i] = findKthNum(array,n,index);
                }else{//后面的数字按照从小到大排列
                    str[i++] = findKthNum(array,n,index - 1);
                    for(int j = n - 1;j >= 0;j--){
                        if(array[j] != '0')str[i++] = array[j];
                    }
                    break;
                }
            }
            return new String(str);
        }
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            System.out.println(new GetPermutation2().getPermutation(9, 206490));
            //System.out.println(new GetPermutation2().getPermutation(4, 16));
        }
    
    }
  • 相关阅读:
    ssh的配置[待写]
    回文检测题解
    叠积木/银河系英雄传说[NOI2002]题解
    酒店之王解题报告
    摆花解题报告
    摆渡车(noip2018 pj t3)
    NOIP2018 普及 凉凉记
    子矩阵(NOIP2014T4)
    花店橱窗布置(洛谷:P1854)
    我想大声告诉你
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6670581.html
Copyright © 2011-2022 走看看