【算法】滑动窗口三步走 (7)

当然,不使用集合,使用哈希表做也是可以的:这里的哈希表键值对是(字符,出现频数)

class Solution { public int lengthOfLongestSubstring(String s) { int left = 0; int right = 0; // 使用右边界来遍历数组 int result = 0; // 用来存放最长子串的长度 char c = 0; // 用来遍历字符串中每个字符 Map<Character, Integer> map = new HashMap<>(); // 前闭后开,滑动窗口 // 每次遍历都添加频度 // 如果不重复就加入哈希表,右边界前进,比较最大长度 // 如果重复,删除哈希表,左边界前进 while (right < s.length()) { // 使用右边界来遍历数组,后面再判断是否加入哈希表中 c = s.charAt(right); int count = map.getOrDefault(c, 0) + 1; if (count > 1) { // 重复,左边界前进,哈希表删除 map.put(s.charAt(left), map.get(s.charAt(left)) - 1); left++; } else { // 右边界前进,哈希表增加 map.put(c, count); right++; result = result > (right - left)? result : (right - left); } } return result; } }

通过观察,我们能看出来。如果是最坏情况的话,我们每一个字符都可能会访问两次,left一次,right一次,时间复杂度达到了O(2N),这是不可饶恕的。不理解的话看下图:

假设我们的字符串为“abcdc”,对于abc我们都访问了2次。

【算法】滑动窗口三步走

那如何来进一步优化呢?

其实我们可以定义字符到索引的映射,而不是简单通过一个集合来判断字符是否存在。这样的话,当我们找到重复的字符时,我们可以立即跳过该窗口,而不需要对之前的元素进行再次访问。

【算法】滑动窗口三步走

而这里的哈希表的键值对是(字符,字符出现的索引+1)

//java public class Solution { public static int lengthOfLongestSubstring(String s) { int n = s.length(), result = 0; Map<Character, Integer> map = new HashMap<>(); for (int right = 0, left = 0; right < n; right++) { if (map.containsKey(s.charAt(right))) { left = Math.max(map.get(s.charAt(right)), left); } result = Math.max(result, right - left + 1); map.put(s.charAt(right), right + 1); } return result; } }

【算法】滑动窗口三步走

我的:

//java // 上面的哈希表记录的是(字符,频数) // 这里的哈希表记录的是(字符,出现索引+1) public class Solution { public static int lengthOfLongestSubstring(String s) { int n = s.length(), result = 0; Map<Character, Integer> map = new HashMap<>(); int left = 0; int right = 0; // 使用右边界来遍历数组 char c = 0; while (right < s.length()) { c = s.charAt(right); int count = map.getOrDefault(c, -1); // -1代表没有出现过 if (count == -1) { // 没有出现过 map.put(c, right + 1); right++; result = Math.max(result, right - left); } else { // 出现过 left = Math.max(count, left); // 这里需要着重注意,因为滑动窗口left只能前进,不能倒退回去,只能取最大的 map.put(c, right + 1); right++; result = Math.max(result, right - left); } } // for (int right = 0, left = 0; right < n; right++) { // if (map.containsKey(s.charAt(right))) { // left = Math.max(map.get(s.charAt(right)), left); // } // result = Math.max(result, right - left + 1); // map.put(s.charAt(right), right + 1); // } return result; } }

修改之后,我们发现虽然时间复杂度有了一定提高,但是还是比较慢!如何更进一步的优化呢?我们可以使用一个256位的数组来替代hashmap,以进行优化。(因为ASCII码表里的字符总共有128个。ASCII码的长度是一个字节,8位,理论上可以表示256个字符,但是许多时候只谈128个。具体原因可以下去自行学习~)

//java class Solution { public int lengthOfLongestSubstring(String s) { int len = s.length(); int result = 0; int[] charIndex = new int[256]; for (int left = 0, right = 0; right < len; right++) { char c = s.charAt(right); left = Math.max(charIndex[c], left); result = Math.max(result, right - left + 1); charIndex[c] = right + 1; } return result; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwyxf.html