LeetCode126 Word Ladder II 字梯

Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:

  1. Only one letter can be changed at a time
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Note:

  • Return an empty list if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

Example 1:

Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]

Example 2:

Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: []

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

题源:here;完整实现:here

思路:

我们按照单词之间的距离生成一个树。该树的父子结点之间只有一个字母的不同,根节点是beginWord。因为生成树的方式不同,其遍历的时间也不同。这里提供两种生成树的方案,其中第一种直观简单,但是遍历时时间复杂度高,而且非常耗费空间,提交后超时;第二种没有超时。但是请注意,本质上这两种方式是一样的。不过第二种方法实现起来确实比较麻烦。这道题的接受率也算是比较低的。

1 简单粗暴版

以Example1 为例,我们生成的二叉树是这样的:

从图中我们可以看到,我们是一个一个的往下生成,但是层之间没有联系,这样会有很多冗余的空间,但是实现相对简单。

private:
	struct TreeNode{
		string val;
		vector<TreeNode*> next;
		TreeNode(string x) : val(x){}
	};

public:
	vector<string> changeOne(string s, vector<string>& list){
		vector<string> res;
		vector<string> left;
		for (int i = 0; i < list.size(); i++){
			int diffCount = 0;
			for (int j = 0; j < s.size(); j++){
				if (s[j] != list[i][j]) diffCount++;
			}
			if (diffCount == 1) res.push_back(list[i]);
			else left.push_back(list[i]);
		}
		list = left;
		return res;
	}

	TreeNode *genTree(string begin,vector<string> list){
		TreeNode *root = new TreeNode(begin);
		vector<string> nextList = changeOne(begin, list);
		if (nextList.size() == 0) return root;
		for (int i = 0; i < nextList.size(); i++){
			root->next.push_back({});
			root->next[i] = genTree(nextList[i], list);
		}
		return root;
	}

	void DFS(TreeNode *root, string end, vector<string> solution, vector<vector<string>>& res){
		if (!root) return;
		solution.push_back(root->val);
		if (root->val == end){
			res.push_back(solution);
			return;
		}

		for (int i = 0; i < root->next.size(); i++){
			DFS(root->next[i], end, solution, res);
		}
	}

	vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
		TreeNode *root = genTree(beginWord, wordList);
		vector<vector<string>> res;
		DFS(root, endWord, {}, res);
		int minLen = INT_MAX;
		for (int i = 0; i < res.size(); i++){
			minLen = min(minLen, int(res[i].size()));
		}
		vector<vector<string>> temp;
		for (int i = 0; i < res.size(); i++){
			if (res[i].size() == minLen)
				temp.push_back(res[i]);
		}
		return temp;
	}

 2 去冗余版

我们给出没有冗余的时候生成的树:

可以看到,层与层之间会通过他们的子节点有联系,这样会大大降低搜索时的时间复杂度并减少存储空间。

vector<TreeNode*> getNext(TreeNode* root, vector<TreeNode*> nodes){
	vector<TreeNode*> nextNodes;
	vector<TreeNode*> leftNodes;
	for (int i = 0; i < nodes.size(); i++){
		int diffCount = 0;
		for (int j = 0; j < root->val.size(); j++){
			if (root->val[j] != nodes[i]->val[j])
				diffCount++;
		}
		if (diffCount == 1) nextNodes.push_back(nodes[i]);
		else leftNodes.push_back(nodes[i]);
	}
	return nextNodes;
}

void genTree2(TreeNode *root, vector<TreeNode*> nodes){
	vector<TreeNode*> record;
	vector<TreeNode*> restNodes;
	set<TreeNode*> usedNodes;
	for (int i = 0; i < nodes.size(); i++)
		if (root->val != nodes[i]->val)
			usedNodes.insert(nodes[i]);

	copy(usedNodes.begin(), usedNodes.end(), back_inserter(restNodes));
	record.push_back(root);
	TreeNode *last = root;

	while (record.size() && restNodes.size()){
		TreeNode *curr = record.front();
		record.erase(record.begin());
		vector<TreeNode*> nextNodes = getNext(curr, restNodes);
			
		curr->next = nextNodes;
		for (int i = 0; i < nextNodes.size(); i++){
			if (find(record.begin(), record.end(), nextNodes[i]) == record.end())
				record.push_back(nextNodes[i]);
			usedNodes.erase(nextNodes[i]);
		}

		if (curr == last){
			if (restNodes.size() == usedNodes.size()) return;
			last = record.back();
			restNodes = {};
			copy(usedNodes.begin(), usedNodes.end(), back_inserter(restNodes));
		}
	}
}

void DFS2(TreeNode *root, string end, vector<string> solution, vector<vector<string>>& res, int layer, int depth){
	layer++;
	if (layer > depth) return;
	if (!root) return;
	solution.push_back(root->val);
	if (root->val == end){
		res.push_back(solution);
		return;
	}

	for (int i = 0; i < root->next.size(); i++){
		DFS2(root->next[i], end, solution, res, layer, depth);
	}
}

vector<vector<string>> findLadders2(string beginWord, string endWord, vector<string>& wordList){
	vector<TreeNode*> nodes;
	for (int i = 0; i < wordList.size(); i++){
		nodes.push_back(new TreeNode(wordList[i]));
	}

	TreeNode *root = new TreeNode(beginWord);
	genTree2(root, nodes);

	vector<vector<string>> res;
	DFS2(root, endWord, {}, res, 0, nodes.size());
	int minLen = INT_MAX;
	for (int i = 0; i < res.size(); i++){
		minLen = min(minLen, int(res[i].size()));
	}
	vector<vector<string>> temp;
	for (int i = 0; i < res.size(); i++){
		if (res[i].size() == minLen)
			temp.push_back(res[i]);
	}

	return temp;
}

树节点的定义请参看第一份代码或完整实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值