题目:输入一个字符串,打印该字符串的所有排列。例如输入abc,打印它的所有排列为abc,acb,bac,bca,cab,cba。
思路:
思路就是按照如上的递归树。
那么如何实现它呢?其实就是遍历这棵树。我们分析第一个节点的递归过程:首先它交换了第一个字符,然后进入另外一棵树(递归过程),递归返回到第二个分支,交换了A和B,然后又进入另外一棵树(递归过程),紧接着交换了A和C(小心!前一个递归过程,交换了A和B,这个时候要交换回来,延生思考,每次交换都要保证次序不变),然后又进入另外一棵树。
//swap values at two pointers
void swap(char *x, char *y)
{
char temp;
temp = *x;
*x = *y;
*y = temp;
}
extern void PrintPermutation(char *pStr, char *pBegin);
//Print all permutation(排列) of characters
//Input: pStr - a string
void PrintAllPermutation(char *pStr)
{
PrintPermutation(pStr,pStr);
}
//Print all permutation
void PrintPermutation(char *pStr, char *pBegin)//pStr是用来打印字符串用的,字符移动是用pBegin
{
if(!pStr || !pBegin)
return;
//每次递归什么东西一直在变?
//没错,就是pBegin!当pBegin等于NUL时也就是产生了一个字符排列
if(*pBegin == '\0'){
printf("%s\n",pStr);
}else{//一个分支的递归过程。pCh锁定第一个字符,比如在根节点的递归过程中是A,pBegin锁定要交换的字符,一直在变动,比如在根节点的递归过程是B,C。。(一直到达字符串结尾)。
for(char *pCh = pBegin; *pCh != '\0'; pCh++){//以下说明以根节点为例
swap(pCh,pBegin);//交换A和A
PrintPermutation(pStr,pBegin + 1);//进入最左子树
swap(pCh,pBegin);//A和B交换回来
}
}
}
该算法的时间复杂度是Ο(n*n!)。
记住传参的时候一定要用字符数组,不能用字符串常量!
Add:下面用非递归解法:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct tagNode
{
char *pStr;
int iCurr;
struct tagNode *next;
};
typedef struct tagNode node;
//生成一个新节点
//输入:pStr - 节点的字符串
// iCurr - 下次要交换的字符下标
// next -
node *NewNode(char *pStr, int iCurr, node *next)
{
char *pTemp;
pTemp = (char *)malloc(strlen(pStr) + 1);//每个节点保存字符串的空间
node *pNode = (node *)malloc(sizeof(node));//节点的空间
pNode->pStr = pTemp;//让节点的字符指针指向分配好的空间
strcpy(pNode->pStr, pStr);//将字符串复制到分配好的空间
pNode->iCurr = iCurr;//置iCurr
pNode->next = next;//置next
return pNode;
}
//返回交换后的字符串
//输入:i - 字符数组的下标
// j - 字符数组的下标
char *swap(char *pStr, int i, int j)
{
char *pTemp = (char *)malloc(strlen(pStr) + 1);
strcpy(pTemp, pStr);
*(pTemp + i) = *(pTemp + j);
*(pTemp + j) = *(pStr + i);
return pTemp;
}
void PrintPermutation(char *pStr, int i, int j)
{
node *topNode = NewNode(pStr, i, NULL);//生成根节点
int k;
while(topNode != NULL){//如果根节点不为空
node *popNode = topNode;
topNode = topNode->next;
if(popNode->iCurr == j - 1)
printf("%d:%s\n", i++, popNode->pStr);
else{
for(k = popNode->iCurr; k < j; k++)
topNode = NewNode(swap(popNode->pStr,popNode->iCurr,k),popNode->iCurr + 1,topNode);
free(popNode);
}
}
}
int main(void)
{
char *pStr = "abc";
PrintPermutation(pStr, 0, strlen(pStr));
return 0;
}
解释:
iCurr:用来保存用来交换的位置的下标,依次为0,1,2
topNode:指向链表的头结点
popNode:指向要打印出拍好顺序的字符串的结点
next:是用来连接链表的
Add:去掉重复排列
#include <stdio.h>
//swap values at two pointers
void swap(char *x, char *y)
{
char temp;
temp = *x;
*x = *y;
*y = temp;
}
int IsSwap(char *pBegin, char *pEnd)
{
char *p;
for(p = pBegin; p < pEnd; p++){
if(*p == *pEnd)
return 0;
}
return 1;
}
extern void PrintPermutation(char *pStr, char *pBegin);
//Print all permutation(排列) of characters
//Input: pStr - a string
void PrintAllPermutation(char *pStr)
{
PrintPermutation(pStr,pStr);
}
//Print all permutation
void PrintPermutation(char *pStr, char *pBegin)//pStr是用来打印字符串用的,字符移动是用pBegin
{
if(!pStr || !pBegin)
return;
//每次递归什么东西一直在变?
//没错,就是pBegin!当pBegin等于NUL时也就是产生了一个字符排列
if(*pBegin == '\0'){
printf("%s\n",pStr);
}else{//一个分支的递归过程。pCh锁定第一个字符,比如在根节点的递归过程中是A,pBegin锁定要交换的字符,一直在变动,比如在根节点的递归过程是B,C。。(一直到达字符串结尾)。
for(char *pCh = pBegin; *pCh != '\0'; pCh++){//以下说明以根节点为例
if(IsSwap(pBegin,pCh)){
swap(pCh,pBegin);//交换A和A
PrintPermutation(pStr,pBegin + 1);//进入最左子树
swap(pCh,pBegin);//A和B交换回来
}
}
}
}
int main(void)
{
char a[] = "122";
PrintAllPermutation(a);
return 0;
}
参考:http://www.geeksforgeeks.org/write-a-c-program-to-print-all-permutations-of-a-given-string/