无重复字符的最长字串-算法练习
算法题目
无重复字符的最长字串:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
算法思路
package com.test.day7_2;
/*
解法一:暴力解法
思路:两重for循环,用一个变量记录最大长度。
解法二:
思路:由于单纯的双重循环时间效率太低,我们会发现,每当遇到重复元素时,可以移动头部元素的
位置,直到不重复,采用队列辅助存储
解法三:
思路:如果要立即得到重复元素的位置,可以利用HashMap来节约查找时间。
解法四:
思路:可以创建128空间大 小的数组来辅助查找当前是否重复
总结:后面二种解法需要用到快慢指针,来计算比较最大长度。
*/
import java.util.*;
/**
@author cosefy
@date 2020/7/2
*/
public class LengthofLongestSubstring {
public static void main(String[] args) {
String s = "abba";
int rs1 = test1(s);
System.out.println(rs1);
int rs2 = test2(s);
System.out.println(rs2);
int rs3 = test3(s);
System.out.println(rs3);
int rs4 = test4(s);
System.out.println(rs4);
}
//解法一
private static int test1(String s) {
int maxlen = 0;
for (int i = 0; i < s.length(); i++) {
List<Character> list = new ArrayList<>();//用列表来辅助存储
list.add(s.charAt(i));
for (int j = i + 1; j < s.length(); j++) {
if (list.contains(s.charAt(j))) //查询列表中是否存在当前元素
break;
list.add(s.charAt(j));
}
maxlen = Math.max(maxlen, list.size());
}
return maxlen;
}
//解法二:
public static int test2(String s) {
int len = s.length();
if (len < 2)
return len;
int lindex = 0, rindex = 1, maxlen = 1;
Deque<Character> deque = new LinkedList<>();
deque.add(s.charAt(0));
while (lindex <= rindex && rindex < len) {
if (deque.contains(s.charAt(rindex))) { //直到当前队列不包含重复元素
deque.poll();
++lindex;
continue;
}
deque.add(s.charAt(rindex));
++rindex;
maxlen = Math.max(maxlen, deque.size());
}
return maxlen;
}
//解法三:
public static int test3(String s) {
int len = s.length();
if (len < 2)
return len;
int lindex = 0, rindex = 0, maxlen = 0;
HashMap<Character, Integer> map = new HashMap<>();
while (lindex <= rindex && rindex < len) {
if (map.containsKey(s.charAt(rindex))) {
//当遇到重复元素,我们只是移动了慢指针,并没有将前面的重复元素移除,所以要再次进行比较。
lindex =Math.max(map.get(s.charAt(rindex)) + 1,lindex) ;
}
map.put(s.charAt(rindex), rindex);
maxlen = Math.max(maxlen, rindex - lindex + 1);
++rindex;
}
return maxlen;
}
//解法四
public static int test4(String s) {
int len = s.length();
if (len < 2)
return len;
int lindex = 0, rindex = 0, maxlen = 0;
Integer[] charindex_arr = new Integer[128]; //128空间的数组用来表示ASCII字符下标值。
while (lindex <= rindex && rindex < len) {
char r_char = s.charAt(rindex);
if (charindex_arr[r_char] != null) {
//当遇到重复元素,我们只是移动了慢指针,并没有将前面的重复元素移除,所以要再次进行比较。
lindex = Math.max(charindex_arr[r_char] + 1,lindex);
}
charindex_arr[r_char] = rindex;
maxlen = Math.max(maxlen, rindex - lindex + 1);
++rindex;
}
return maxlen;
}
}
总结
/*
1,最开始使用的暴力算法,是最容易想到的直接思路,但是我们每次都从当前元素去和以后的所有元素进行比较,
存在大量的重复操作。
2,滑动窗口利用的是我们当前元素的最大长度可以利用上一次最大长度,减少了比较次数,缺点就是我们用到辅助
队列,还需要每次从队列中移除元素,直到队列中不存在重复元素
3,哈希表的改进是,可以直接定位和快指针元素相同的慢指针的位置,另一个改进是,我们并没有从哈希表中将
重复元素位置之前的元素都移除,而是通过对慢指针进行比较来解决这一问题,提升了效率。
4,利用数组的改进体现在可以节约内存,如果是大量的数据,只需要常数级的辅助空间,另外数组的查询效率也很高。
5,快慢指针的高效利用。
*/