问题描述:对于任意一个由字母组成的字符串,返回字符串中第一个非重复字符的位置;如果不存在非重复字符则返回-1。
例如给出 s = 'leetcode' 返回 0,给出s = 'loveleetcode' 返回 2。
对此设计一个算法,其中心思想是建立一快一慢两个指针对字符串进行扫描,慢指针为当前考察字符串,快指针用于检测慢指针后是否有字符与慢指针相同。如果检测到了重复字符,则将该字符加入重复字符集中,后面慢指针再次遇到重复字符则直接跳过。
CHECK_UNIQUE(S)
if S.length = 0:
return -1
if S.length = 1:
return 0
rept_str = {};
//用于存储已探索到的重复字符
SelectKey:
for i from 1 to S.length:
if S[i] in rept_str:
if i = S.length:
return -1
continue;
//如果当前字符在重复字符集中则直接跳过
else
if i=S.length:
return i
for j from i+1 to S.length:
if S[j] = S[i]:
add S[j] to rept_str
//将新探测到的重复字符加入rept_str中
continue loop SelectKey
return i
根据 jhnack的博客介绍,求解算法的时间复杂度的具体步骤是:
⑴ 找出算法中的基本语句;
算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
⑵ 计算基本语句的执行次数的数量级;
只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率。
⑶ 用大Ο记号表示算法的时间性能。
将基本语句执行次数的数量级放入大Ο记号中。
如果算法中包含嵌套的循环,则基本语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间复杂度相加。那么,以上算法的时间复杂度应该是O(n^2)。后面附上以实现此算法的java代码。这个算法在java中的速度超过40%左右的答案,并不算是很高效。其主要的原因在于,在查找S[i]是否在重复集内以及快指针字符是否与慢指针字符相等时增加了一次遍历。
在此分析leetcode上的另一个快速答案。这个解决方案本质上也是双指针遍历,但在判定慢指针当前字母是否为重复字母时,使用的是数组记录字母出现数的方式而非判定是否重复集元素的方式,因此速度更快;此外,这个方法的遍历方式为快指针主动遍历并修改记录数带动慢指针移动的方式,因此循环结构与慢指针主动移动+快指针寻找重复的结构有所不同。
public class Solution {
public int firstUniqChar(String s) {
if (s==null || s.length()==0) return -1;
int len = s.length();
if (len==1) return 0;
char[] cc = s.toCharArray();
int slow =0, fast=1;
int[] count = new int[256];
count[cc[slow]]++;
while (fast < len) {
count[cc[fast]]++;
// if slow pointer is not a unique character anymore, move to the next unique one
while (slow < len && count[cc[slow]] > 1) slow++;
if (slow >= len) return -1; // no unique character exist
if (count[cc[slow]]==0) { // not yet visited by the fast pointer
count[cc[slow]]++;
fast=slow; // reset the fast pointer
}
fast++;
}
return slow;
}
}
附:java代码实现的原算法解答
package testChar3;
import java.util.*;
public class Checkdup {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s = "f";
System.out.println(checkunique(s));
}
private static int checkunique(String s){
Set<Character> rept_str = new HashSet<Character>();
SelectKey:
for(int i=0;i<s.length();i++){
if(rept_str.contains(s.charAt(i))){
if(i==s.length()-1){
return -1;
}
continue;
}
else
if(i == s.length()-1){
return i;
}
for(int j=i+1;j<s.length();j++){
if(s.charAt(j) == s.charAt(i)){
rept_str.add(s.charAt(j));
continue SelectKey;
}
}
return i;
}
return -1;
}
}