参考:《程序员代码面试指南 IT名企算法与数据结构题目最优解》
题目
给定一个字符串s,判断s中是否存在所有字符都出现过一次,根据以下两种要求实现不同的函数。
要求一:实现时间复杂度为O(N)的方法
要求二:在保证额外空间复杂度为O(1)的前提下,请实现时间复杂度尽量低的方法。
解题思路:
要求一:时间复杂度为O(N)
遍历一遍s,使用hashmap来记录每种字符穿线的情况,那么这样就可以在遍历时遍历时发现字符重复出现的情况。
代码实现如下:
public static boolean once(String s){
int len = s.length();
if(len==0||len==1)
return true;
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
for(int i = 0;i<len;i++){
if(map.containsKey(s.charAt(i)))
return false;
else
map.put(s.charAt(i), 1);
}
return true;
}
要求二:保证额外空间复杂度为O(1),尽量降低时间复杂度。
要求空间复杂度O(1),基本思路是先将s进行排序,然后判断排序后的字符是否存在重复情况,所以这里复杂度便取决于排序算法。任何时间复杂度为O(N)的算法都是基于非比较的,其空间复杂度不可能为O(1);时间复杂度为O(NlogN)的排序算法由归并排序、快速排序、希尔排序和堆排序。归并排序的基本思想是不断将两个已经排好序的小部分合成一个大部分,在这个过程中需要辅助数组才能完成,虽然归并排序使用手摇算法可以实现空间复杂度为O(1),但是最坏情况相爱时间复杂度会升至O(N^2),快速排序的时间复杂度为O(logN);希尔排序的时间复杂度取决于步长的选择。所以最终选择堆排序,其可以实现空间复杂度为O(1)的情况下时间复杂度为O(NlogN)。
但是一般情况的堆排序实现过程都是基于递归时间的,因为递归撑撑中需要使用函数栈空间,导致堆排序的空间复杂度为O(logN),若想要实现其空间复杂度为O(1),需要使用非递归的方式实现堆排序。
代码实现如下:
public static boolean isUnique(String s){
int len = s.length();
if(len==0||len==1)
return true;
char[] c = s.toCharArray();
heapSort(c);
System.out.println(String.valueOf(c));
for(int i = 1;i<len;i++){
if(c[i]==c[i-1])
return false;
}
return true;
}
//非递归方式实现堆排序
public static void heapSort(char[] c){
int len = c.length;
//构造大顶堆
for(int i = len/2;i>-1;i--){
adjustHeap(c,i,len);
System.out.println(String.valueOf(c));
}
//交换根节点和叶子节点,并调整大顶堆
for(int i = len-1;i>-1;i--){
swap(c,i,0);
adjustHeap(c,0,i);
}
}
//不断将父亲节点和其子节点进行比较,将大的节点和父亲节点交换
private static void adjustHeap(char[] c,int p, int size) {
for(int i = p;i<size;i++){
int left = i*2 + 1;
int right = i*2 + 2;
int max = i;
if(left < size){
if(c[i]<c[left])
max = left;
if(right<size && c[right] > c[max])
max = right;
if(max!=i)
swap(c,i,max);
}else
break;
}
}
private static void swap(char[] c, int i, int j) {
char temp;
temp = c[i];
c[i] = c[j];
c[j] = temp;
}