C语言实现字符串全排列的深度解析

一、问题描述

全排列_牛客题霸_牛客网
给定一个字符串,生成该字符串的所有可能排列。例如,对于字符串 "abc",其全排列为:

abc
acb
bac
bca
cba
cab

这个问题可以通过递归和回溯算法来解决。递归用于深度优先搜索,回溯用于撤销当前选择,尝试其他可能性。
 

二、代码实现

以下是实现字符串全排列的C语言代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>

bool used[7] = {0};  // 标记数组,用于记录字符是否被使用
int result[7] = {0}; // 用于存储当前排列的结果
int len = 0;         // 字符串长度

// 回溯函数,用于生成全排列
void backtrack(char* str, int deepth) {
    if (deepth == len) {  // 如果深度等于字符串长度,说明找到一个完整的排列
        for (int i = 0; i < deepth; i++) {
            printf("%c", result[i]);  // 输出当前排列
        }
        printf("\n");
        return;
    }

    // 尝试每个字符
    for (int i = 0; i < len; i++) {
        if (!used[i]) {  // 如果当前字符未被使用
            result[deepth] = str[i];  // 将当前字符加入结果数组
            used[i] = true;           // 标记当前字符为已使用
            backtrack(str, deepth + 1); // 递归调用,进入下一层
            used[i] = false;          // 回溯,撤销当前选择
        }
    }
}

int main() {
    char str[7];
    scanf("%s", str);  // 输入字符串
    len = strlen(str); // 获取字符串长度

    backtrack(str, 0); // 从深度 0 开始递归

    return 0;
}

三、代码解析

1. 核心思想

全排列问题的核心在于递归回溯。递归用于深度优先搜索,回溯用于撤销当前选择,尝试其他可能性。具体逻辑如下:

  • 使用一个布尔数组 used 来标记每个字符是否已经被使用。

  • 使用一个整型数组 result 来存储当前的排列结果。

  • 当递归深度等于字符串长度时,输出当前排列。

  • 在每一层递归中,尝试所有未被使用的字符,并标记为已使用,然后进入下一层递归。

  • 在回溯时,撤销当前选择,恢复字符的未使用状态。

2. 关键点解析

  • 递归终止条件:当递归深度等于字符串长度时,说明已经生成了一个完整的排列。

  • 回溯操作:在每次递归返回时,撤销当前选择,恢复字符的未使用状态,以便尝试其他可能性。

回溯示意图:字符串 "abc" 的全排列

1. 初始状态

从根节点开始,递归深度为 0,尚未选择任何字符。

[ ]

2. 第一层递归

从字符串 "abc" 中选择一个字符作为排列的第一个字符。可以选择 abc

       [ ]
       / | \
     [a] [b] [c]
3. 第二层递归

以选择 a 为例,进入第二层递归。此时,a 已被使用,只能从剩余的字符 bc 中选择。

       [ ]
       / | \
     [a] [b] [c]
     / \
   [ab] [ac]
4. 第三层递归

以选择 ab 为例,进入第三层递归。此时,ab 已被使用,只能选择剩余的字符 c

       [ ]
       / | \
     [a] [b] [c]
     / \
   [ab] [ac]
   /     \
 [abc]  [acb]

此时,abc 是一个完整的排列,递归终止。

5. 回溯操作

abc 开始回溯,撤销选择 c,回到第二层递归,尝试选择其他字符。

       [ ]
       / | \
     [a] [b] [c]
     / \
   [ab] [ac]
   /     \
 [abc]  [acb]

撤销选择 c,回到 [ab],尝试选择其他字符(但已无其他选择),继续回溯。

       [ ]
       / | \
     [a] [b] [c]
     / \
   [ab] [ac]
   /
 [abc]

撤销选择 b,回到 [a],尝试选择其他字符(如 c)。

       [ ]
       / | \
     [a] [b] [c]
     / \
   [ab] [ac]

选择 c,进入第三层递归,选择剩余的字符 b

       [ ]
       / | \
     [a] [b] [c]
     / \
   [ab] [ac]
        \
       [acb]

此时,acb 是另一个完整的排列,递归终止。剩下以此类推。
 

回溯示意图(带虚线表示回溯)

以下是完整的回溯树,虚线表示回溯撤销操作:

       [ ]
       / | \
     [a] [b] [c]
     / \   / \
   [ab] [ac] [ba] [bc]
   /     \   /     \
 [abc]  [acb] [bac] [bca]

回溯过程:

  • [abc] 回溯到 [ab],撤销选择 c

  • [ab] 回溯到 [a],撤销选择 b

  • [a] 回溯到根节点,撤销选择 a

  • 从根节点选择 b,进入 [b]

  • [b] 选择 a,进入 [ba]

  • [ba] 选择 c,生成 [bac]

  • [bac] 回溯到 [ba],撤销选择 c

  • [ba] 回溯到 [b],撤销选择 a

  • [b] 选择 c,进入 [bc]

  • [bc] 选择 a,生成 [bca]

  • [bca] 回溯到 [bc],撤销选择 a

  • [bc] 回溯到 [b],撤销选择 c

  • [b] 回溯到根节点,撤销选择 b

  • 从根节点选择 c,进入 [c]

  • [c] 选择 a,进入 [ca]

  • [ca] 选择 b,生成 [cab]

  • [cab] 回溯到 [ca],撤销选择 b

  • [ca] 回溯到 [c],撤销选择 a

  • [c] 选择 b,进入 [cb]

  • [cb] 选择 a,生成 [cba]

  • [cba] 回溯到 [cb],撤销选择 a

  • [cb] 回溯到 [c],撤销选择 b

  • [c] 回溯到根节点,撤销选择 c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值