字符串的排列

题目:输入一个字符串,打印该字符串的所有排列。例如输入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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值