例子
设计一种结构,用户可以:
前缀树的实现方式:
1)固定数组实现
2)哈希表实现
//前缀树节点
public static class Node1{
//经过次数
private int pass;
//结尾次数
private int end;
//横向数组【路径选择】
private Node1[] nexts;
public Node1(){
this.pass = 0;
this.end = 0;
//字符串,一共26种字母组成【26种路径】
nexts = new Node1[26];
}
}
//添加字符串
public void insert(String word){
if(word == null){
return;
}
char[] chs = word.toCharArray();
Node1 node = root;
//添加元素,经过root的pass++
node.pass++;
int path = 0;
for(int i = 0; i < chs.length; i++){
//找到是哪条路径
path = chs[i] - 'a';
//该路径上没有字母,新建【有:pass++】
if(node.nexts[path] == null){
node.nexts[path] = new Node1();
}
//继续向下走
node = node.nexts[path];
node.pass++;
}
node.end++;
}
//删除前缀树的单词【删除一条路径】
public void delete(String word){
//先判断该单词是否存在于树中
if(search(word) != 0){
char[] chs = word.toCharArray();
Node1 node = root;
//路径从根开始
node.pass--;
int path = 0;
for(int i = 0; i < chs.length; i++){
path = chs[i] - 'a';
//先--,如果pass为0,就将其设为null
if(--node.nexts[path].pass == 0){
node.nexts[path] = null;
return;
}
node = node.nexts[path];
}
node.end--;
}
}
//搜寻某个单词在树中出现了几次
public int search(String word){
if(word == null){
return 0;
}
char[] chs = word.toCharArray();
//从根节点开始向下找
Node1 node = root;
int index = 0;
for(int i = 0; i < chs.length; i++){
//走哪条路径
index = chs[i] - 'a';
if(node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.end;
}
//所有加入的字符串中,有多少以pre作为前缀
public int prefixNumber(String pre){
if(pre == null){
return 0;
}
char[] chs = pre.toCharArray();
Node1 node = root;
int index = 0;
for(int i = 0; i < chs.length; i++){
index = chs[i] - 'a';
//该路径下面为null,直接返回0
if(node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.pass;
}
//前缀树节点
public static class Node1{
//经过次数
private int pass;
//结尾次数
private int end;
//横向数组【路径选择】
private Node1[] nexts;
public Node1(){
this.pass = 0;
this.end = 0;
//字符串,一共26种字母组成【26种路径】
nexts = new Node1[26];
}
}
public static class Trie1{
//根节点
private Node1 root;
public Trie1(){
root = new Node1();
}
//添加字符串
public void insert(String word){
if(word == null){
return;
}
char[] chs = word.toCharArray();
Node1 node = root;
//添加元素,经过root的pass++
node.pass++;
int path = 0;
for(int i = 0; i < chs.length; i++){
//找到是哪条路径
path = chs[i] - 'a';
//该路径上没有字母,新建【有:pass++】
if(node.nexts[path] == null){
node.nexts[path] = new Node1();
}
//继续向下走
node = node.nexts[path];
node.pass++;
}
node.end++;
}
//删除前缀树的单词【删除一条路径】
public void delete(String word){
//先判断该单词是否存在于树中
if(search(word) != 0){
char[] chs = word.toCharArray();
Node1 node = root;
//路径从根开始
node.pass--;
int path = 0;
for(int i = 0; i < chs.length; i++){
path = chs[i] - 'a';
//先--,如果pass为0,就将其设为null
if(--node.nexts[path].pass == 0){
node.nexts[path] = null;
return;
}
node = node.nexts[path];
}
node.end--;
}
}
//搜寻某个单词在树中出现了几次
public int search(String word){
if(word == null){
return 0;
}
char[] chs = word.toCharArray();
//从根节点开始向下找
Node1 node = root;
int index = 0;
for(int i = 0; i < chs.length; i++){
//走哪条路径
index = chs[i] - 'a';
if(node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.end;
}
//所有加入的字符串中,有多少以pre作为前缀
public int prefixNumber(String pre){
if(pre == null){
return 0;
}
char[] chs = pre.toCharArray();
Node1 node = root;
int index = 0;
for(int i = 0; i < chs.length; i++){
index = chs[i] - 'a';
//该路径下面为null,直接返回0
if(node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.pass;
}
}
桶排序思想下的排序:计数排序 & 基数排序
1)桶排序思想下的排序都是不基于比较的排序
2)时间复杂度O(N),空间复杂度O(M)
3)应用范围有限,需要样本的数据状况来满足桶的划分
计数排序和基数排序
//计数排序
public static void countSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
int max = Integer.MIN_VALUE;
for(int i : arr){
max = Math.max(i, max);
}
//创建桶【假设最大值是14】
// 【0 1 2 3 4 .....13 14】
int[] buckets = new int[max+1];
//入桶
for(int i = 0; i < arr.length; i++){
buckets[arr[i]]++;
}
//出桶【从小的出】
int index = 0;
for(int j = 0; j < buckets.length; j++){
while(buckets[j]-- > 0){
arr[index++] = j;
}
}
}
进阶写法:不用创建多个桶,直接在help数组上修改
//基数排序:arr中只能为正值【如果有负值,需要做特殊处理】
public static void radixSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
//获取arr中的最大位数【循环几次】
public static int maxbits(int[] arr){
int max = Integer.MIN_VALUE;
for(int i : arr){
max = Math.max(max, i);
}
int res = 0;
while (max != 0){
max /= 10;
res++;
}
return res;
}
//arr[L, R]排序 digit:最大的十进制位数
public static void radixSort(int[] arr, int L, int R, int digit){
final int radix = 10;
int i = 0, j = 0;
//有多少个数,准备多少个辅助空间
int[] help = new int[R - L + 1];
//有多少位,就进出多少次
for(int d = 1; d <= digit; d++){
//count -> count' [前缀和]
//10个空间
//count[0] 当前位【d位】是0的的数字有多少个【个、十、百、...】
//count[1] 当前位【d位】是0和1的数字有多少个
//count[2] 当前位【d位】是0、1、2的数字有多少个(<=2)
//count[i] 当前位【d位】是0~i的数字有多少个
int[] count = new int[radix]; //count[0...9]
for(i = L; i <= R; i++){
j = getDigit(arr[i], d);
count[j]++;
}
for(i = 1; i < radix; i++){
//变为前缀和count[1]表明<=1的数有几个
count[i] = count[i] + count[i-1];
}
for(i = R; i >= L; i--){
j = getDigit(arr[i], d);
help[count[j] - 1] = arr[i];
count[j]--;
}
for(i = L, j = 0; i <= R; i++, j++){
arr[i] = help[j];
}
}
}
//获取x每一位的数值
public static int getDigit(int x, int d){
return ((x / (int) Math.pow(10, d - 1)) % 10);
}
注:希尔排序也是不稳定的,时间复杂度O(N*logN)
工程上对于排序的改进:
1)稳定性考虑
Java内部的Arrays.sort()内部是改良后的快排,会根据传入的参数是否是基础类型,如果是基础类型,对稳定性无要求,直接快排,如果非基础类型,有稳定性要求,走归并
2)充分利用O(N*logN)和O(N^2)排序各自的优势
快排如果L + 60 > R 内部直接走插入(常数项小)