LeetCode:Permutations(全排列算法的递归与非递归实现)

全排列算法的递归与非递归实现

全排列算法是常见的算法,用于求一个序列的全排列,本文使用C语言分别用递归与非递归两种方法实现,可以接受元素各不相同的输入序列。

题目来自leetcode:

Given a collection of 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], and [3,2,1].

递归实现

对于序列Sn={s0,s1,…,sn},其全排列可以看做s0与后面的n-1个元素交换,然后后面的n-1个元素再次进行全排列。也就是说Sn的全排列可以写为{s0,Sn-1},{s1,Sn-1},…{sn,Sn-1}。以此类推。这显然是一个递归的过程。

#include<stdio.h>
#include<stdlib.h>
void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void permut(int **result, int numbers[], int n, int *rows, int m)
{//完成下标为m到n的全排列
    int i;
    if (m == n)
    {//已经处理到最后一个元素,递归返回
        (*rows)--;
        for (i = 0; i < n; i++)
            result[(*rows)][i] = numbers[i];
    }
    else
    {
        for (i = m; i < n; i++)
        {
            swap(&numbers[m], &numbers[i]);//交换
            permut(result, numbers, n, rows, m + 1);//向后处理
            swap(&numbers[m], &numbers[i]);//再次交换
        }
    }
}
int **permute(int numbers[], int n, int *numRows)
{
    int **result, i;
    *numRows = 1;
    //计算全排列数
    for (i = n; i>1; i--)
        (*numRows) *= i;
    //分配结果数组
    result = (int **)malloc((*numRows)*sizeof(int *));
    for (i = 0; i<(*numRows); i++)
        *(result + i) = (int *)malloc(n*sizeof(int));
    int rows = *numRows;
    //从第一个元素开始排列
    permut(result, numbers, n, &rows, 0);

    return result;
}
int main()
{
    int i, j;
    int numbers[3] = {1,2,2};
    int numRows = 0, n = 3;

    int **result = permute(numbers, n, &numRows);

    for (i = 0; i<numRows; i++)
    {
        for (j = 0; j<n; j++)
        {
            printf("%d ", result[i][j]);
        }
        printf("\n");
    }
    system("pause");
}

非递归实现

非递归实现全排列的思想是:

1,将序列排序,生成最小序列Smin
2,然后找到比Smin大但比其他序列都小的序列Smin+1
3,反复执行2,直到序列最大

比如序列{2,1,3},首先排序得到最小序列{1,2,3},然后找到次小的序列{1,3,2},然后是{2,1,3}……以此类推直到序列{3,2,1}结束。

其算法是在序列{s0,s1,…si,sj,…sk,…,sn-1}中
1,从后向前查找,找到第一个si < <script type="math/tex" id="MathJax-Element-1"><</script>sj,如果i=0时依然没有找到,说明该序列已经最大,返回。
2,从后向前查找,找到第一个比si大的元素sk,交换si和sk。为了保证得到的是次小的序列,将序列si+1到sn-1逆向。
3,重复1、2两步直到退出

#include<stdio.h>
#include<stdlib.h>

void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void sort(int numbers[], int n)
{//冒泡排序
    int i, j, k;
    for (i = 0; i < n; i++)
    {
        for (j = i + 1; j < n; j++)
        {
            if (numbers[i]>numbers[j])
                swap(&numbers[i], &numbers[j]);
        }
    }
}
void reverse(int numbers[], int i, int j)
{//逆置
    int p, q;
    for (p = i, q = j; p < q; p++, q--)
        swap(&numbers[p], &numbers[q]);
}
void store(int **result, int numbers[], int n, int row)
{//将序列存入结果数组中
    int i;
    for (i = 0; i < n; i++)
        result[row][i] = numbers[i];
}
int **permute(int numbers[], int n, int *numRows)
{
    int **result, i,j,row;
    *numRows = 1;
    for (i = n; i>1; i--)
        (*numRows) *= i;
    result = (int **)malloc((*numRows)*sizeof(int *));
    for (i = 0; i<(*numRows); i++)
        *(result + i) = (int *)malloc(n*sizeof(int));
    //首先对序列进行排序,找到最小序列
    sort(numbers,n);
    row = 0;

    while (true)
    {
        store(result, numbers, n, row++);
        //找到第一个比后一个元素小的元素numbers[i]
        if (n < 2)//当序列只有一个元素时直接退出
            return result;
        for (i = n - 2; i >= 0; i--)
        {
            if (numbers[i] < numbers[i + 1])
                break;
            else if (i == 0)
                return result;
        }
        //找到从后面开始比numbers[i]大的第一个元素
        for (j = n - 1; j > i; j--)
        {
            if (numbers[j] > numbers[i])
                break;
        }
        swap(&numbers[i], &numbers[j]);
        reverse(numbers, i + 1, n-1);

    }
}

int main()
{
    int i, j;
    int numbers[3] = {1,2,3};
    int numRows = 0, n = 3;

    int **result = permute(numbers, n, &numRows);

    for (i = 0; i<numRows; i++)
    {
        for (j = 0; j<n; j++)
        {
            printf("%d ", result[i][j]);
        }
        printf("\n");
    }
    system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值