LeetCode127——单词接龙

版权声明:版权所有,转载请注明原网址链接。 https://blog.csdn.net/qq_41231926/article/details/81545756

原题链接:https://leetcode-cn.com/problems/word-ladder/description/

题目描述:

知识点:队列,图的广度优先遍历

基本思路:

(1)新建一个辅助的类LevelWord,类包含两个属性,代表word的String类型的变量,以及代表word层级的int型变量level。在构造函数可以给这两个变量赋值。

(2)新建一个辅助函数,用以判断两个字符串之间是否能够转换。遍历两个字符串,根据题意,如果不相等的字符的数量大于1,则无法相互转换。只有不相等的字符的数量等于1时,这两个字符串才能够相互转换。

(3)在ladderLength()函数里,我们首先判断endWord字符串是否在所给的wordList中,如果endWord字符串不在所给的wordList中,说明最短路径不存在,无法进行转换,依据题意,我们直接返回0。

(4)为了实现的方便,如果beginWord不在wordList中,我们将beginWord添加进wordList。

(5)新建一个boolean类型的二维数组nextWords用来表示wordList中第i个位置和第j个位置的字符串能否相互转换,如果能相互转换,则nextWords[i][j] = true。我们通过遍历nextWord两次来设置nextWords的值。由于两个点之间的转换是相互的,所以本题本质上是一个无向图。我们在遍历nextWord两次的时候可以做一次优化,同时设置nextWords[i][j]和nextWords[j][i]的值,因此我们只需遍历矩阵的上三角或者下三角即可

(6)新建一个HashMap类型的数据visited,其键类型为String,其值类型为Boolean,用来记录wordList中对应的字符串是否已经被访问过。初始化visited时,将wordList中的所有元素都放入visited中,每个元素对应的值设为false,表示所有节点都未被访问。

(7)新建一个队列queue,里面存放的是LevelWord类型的数据,初始时,新建一个word值为beginWord,level值为0的LevelWord类型的变量入队,并将visited中beginWord对应的值设为true。

(8)写一个循环,只要队列queue不为空就一直进行该循环,在循环体里我们做以下几件事。

a.取出队首元素temp,判断temp的word属性是否和endWord相等,注意判断两个字符串变量相等我们需要用equals()方法而不能用==号。如果相等,则直接返回temp.level + 1,为什么要+1呢?因为我们在(7)中第一个入队的元素的level值为0。

b.新建一个List<String>类型的变量nextWord,用来记录可以与temp.word字符串相互转换的所有字符串。根据我们在(5)中得到的二维数组nextWords来设置nextWord中包含哪些元素。

c.遍历nextWord中的元素,如果该元素还没有被访问,即该元素的visited键对应的值为false,则新建一个word值为该元素,level值为temp.level + 1的LevelWord类型的变量入队,并将相应的visited键对应的值设为true。

(8)while循环结束之后,虽然我们知道这个函数在while循环里一定会直接返回,不会进行while循环之后的所有语句,但编译器并不知道这一点。如果我们不在while循环后加一个return语句,编译器是会报错的,因此在while循环后我们return一个任意的int型数据,这里我return的是0。

JAVA代码:

class LevelWord {
		
		String word;
		int level;
		
		public LevelWord(String word, int level) {
			this.word = word;
			this.level = level;
		}
		
	}
	
	public int ladderLength(String beginWord, String endWord, List<String> wordList) {
		int end = wordList.indexOf(endWord);
		if(end == -1) {
			return 0;
		}
		if(!wordList.contains(beginWord)) {
			wordList.add(beginWord);
		}
		boolean[][] nextWords = new boolean[wordList.size()][wordList.size()];
		HashMap<String, Boolean> visited = new HashMap<>();
        for (int i = 0; i < wordList.size(); i++) {
        	for (int j = 0; j < i; j++) {
        		if(hasPath(wordList.get(i).toCharArray(), wordList.get(j).toCharArray())) {
        			nextWords[i][j] = nextWords[j][i] =	true;
        		}
			}
        	visited.put(wordList.get(i), false);
		}
        Queue<LevelWord> queue = new LinkedList<>();
        queue.add(new LevelWord(beginWord, 0));
        visited.put(beginWord, true);
        while(!queue.isEmpty()) {
        	LevelWord temp = queue.poll();
        	if(temp.word.equals(endWord)) {
        		return temp.level + 1;
        	}
        	List<String> nextWord = new ArrayList<>();
        	int n = wordList.indexOf(temp.word);
        	for (int i = 0; i < nextWords[n].length; i++) {
				if(nextWords[n][i]) {
					nextWord.add(wordList.get(i));
				}
			}
        	for (int i = 0; i < nextWord.size(); i++) {
        		String nextTemp = nextWord.get(i);
        		if(!visited.get(nextTemp)) {
        			queue.add(new LevelWord(nextTemp, temp.level + 1));
        			visited.put(nextTemp, true);
        		}
			}
        }
        return 0;
    }
	
	private boolean hasPath(char[] arr1, char[] arr2) {
		int diff = 0;
		for (int i = 0; i < arr1.length; i++) {
			if(arr1[i] != arr2[i]) {
				diff++;
			}
		}
		if(diff == 1) {
			return true;
		}
		return false;
	}

复杂度分析:

时间复杂度:

(1)判断wordList各个字符串间是否有路径的复杂度是O(m * n ^ 2),n表示wordList中的元素个数,m表示wordList中字符串的长度。

(2)在队列中进行的操作的复杂度是O(n * x),x为能与每个字符串相互转换的字符串数量,是一个未知值。

总的时间复杂度为O(m * n ^ 2)。

空间复杂度:

(1)需要存储一个邻接矩阵用以判断wordList中各个字符串间是否有路径,该邻接矩阵的空间为O(n * n)。

(2)其他还有一些比如存放是否已访问visited变量等都是O(n)级别的复杂度。

总的空间复杂度为O(n * n)。

猜你喜欢

转载自blog.csdn.net/qq_41231926/article/details/81545756