前缀树
定义
输入字符串,按字符出现的顺序存到多叉树中。可以通过该树以O[str.lenth]
的时间复杂度获取到添加了几次给定字符串str
,或以str
为前缀的字符串在该树中添加了几个,以及删除功能。
代码
public class Node
{
private int pass;
private int end;
private Node[] nexts;
public Node()
{
pass = 0;
end = 0;
nexts = new Node[26];//共26个字母所以只用26个数组
}
}
public class Triel
{
private Node root;
public Triel()
{
root = new Node();
}
/*
* 插入新字符串
*
*/
public Boolean insert(String str)
{
if (str == null)//空字符串直接返回false
return false;
char[] chs = str.toCharArray();//转化成char数组
Node n = root;
n.pass++;//根节点经过次数增加
int index = 0;
for(int i = 0;i < chs.lenth;i++)
{
index = chs[i] - 'a';//获取字母对应的下标
if(n.nexts[index] == null)//原来的树上没有该节点
n.nexts[index] = new Node();//新建节点
n = n.nexts[index];//节点后移
n.pass++;//节点经过次数增加
}
//结束循环时,代表字符串全部结束,此时来到最后一个字符对应的节点
n.end++;//节点结束次数增加
return true;
}
/**
* 查询添加过几次str字符串
* 过程基本同插入
*/
public int search(String str)
{
if(str == null) return 0;
char[] chs = str.toCharArray();
Node n = root;
int index = 0;
for(int i = 0;i < chs.lenth;i++)
{
index = chs[i] - 'a';
//与插入的不同点,如果发现下一个节点不存在,就可以直接返回没添加过str字符串
if(n.nexts[index] == null) return 0;
n = n.nexts[index];
}
//返回以最后一个字符为结束的次数
return n.end;
}
/**
* 删除一次str字符串
*
*/
public Boolean delete(String str)
{
if(search(str) != 0)//先判断树中是否添加过该字符串,没有返回false
{
Node n = root;
n.pass--;//经过的节点减一
char[] chs = str.toCharArray();
int index = 0;
for(int i = 0;i < chs.lenth;i++)
{
index = chs[i] - 'a';
//如果有一个节点的经过次数减为0,则说明该节点及其后的节点在删除后都变成冗余部分
//要删除该节点及其后的节点
//所以通过其父节点对他的经过次数进行计算,要删除则直接将该指针置为null
//注意:如果是C、C++,则要手动释放空间
if(--n.nexts[index].pass == 0)
{
n.nexts[index] = null;
return true;
}
n = n.nexts[index];
}
n.end--;
return true;
}
return false;
}
/**
* 查询以str为前缀的字符串在树中添加了几次
*
*/
public int searchPre(String str)
{
if(str == null) return 0;
Node n = root;
char[] chs = str.toCharArray();
int index = 0;
for(int i = 0;i < chs.lenth;i++)
{
index = chs[i] - 'a';
if(n.nexts[index] == null) return 0;
n = n.nexts[index];
}
return n.pass;
}
}
拓展
如果字符的数量更多,可以用hash表来辅助查找下标。
桶排序
定义
桶排序是一种思想,不是一个排序方法。不通过比较进行排序而通过多个容器辅助排序的方法。一般对数据有一定的要求。
计数排序
描述
在基本确定知道待排序的数据的范围后,生成一个等大(或稍大)的辅助数组,每有一个数与下标值相等,则将下标对应的值加一,结果数组用辅助数组的下标进行按顺序填充,填充次数为辅助数组下标对应的值。
实现
/**
* max为所有数据中的最大值,arr为待排序数组
*
*/
public void countingsort(int max,int[] arr)
{
int[] help = new int[max + 1];//生成长度比最大值大一的辅助数组
for(int i:arr)
{
help[i]++;//按arr的值存入辅助数组对应下标的位置
}
int i = 0;
while(i < arr.lenth)
{
while(j < max + 1)
{
if(help[j]--) arr[i] = j;//如果辅助数组的下标数据不为0,结果数组存入一个下标值
else j++;//为0则看下一个下标
}
}
}
基数排序
描述
十进制通过1~9来当"桶",按个位、十位、百位…的顺序来排序。简单的做法是用队列实现,。
实现
左神比较骚,用的骚方法
/**
* 完全照搬左神的
* arr是待排序数组,L和R分别是要处理的左右边界,digit是arr中最大数的位数
*/
public void radixSort(int[] arr,int L,int R,int digit)
/*例:arr={103,54,89,12,37,114},L = 0,R = 5,digit = 3*/
{
final int radix = 10;
int i = 0,j = 0;
int[] help = new int[R - L + 1];
//d控制位数,从个位开始循环到最高位
//为什么从个位(低位)开始循环?
//因为排到高位时还是会将低位排好序的数打乱,优先按高位的大小排
for(int d = 1;d < digit;d++)
{
int[] count = new int[radix];
for(i = L;i <= R;i++)
{
j = getDigit(arr[i],d);
count[j]++;
}
/*上个循环结束后,count为
{0,0,1,1,2,0,0,1,0,1}*/
for(i = 1;i < radix;i++)
{
count[i] = count[i] + count[i - 1];
}
//此时count的含义为,arr中有count[i]个数的倒数第d位数的值小于等于i
/*上个循环结束后,count为
{0,0,1,2,4,4,4,5,5,6}*/
//这个循环从右往左遍历,因为我们不能知道count中同一个下标那么多个值左边第一个从哪开始
//但是我们可以确定右边的第一个,为count[j] - 1的位置
//把对应值放进辅助数组中
for(i = R;i >= L;i--)
{
j = getDigit(arr[i],d);
help[count[j] - 1] = arr[i];
count[j]--;
}
/*上个循环结束后,count为
{0,0,0,1,2,4,4,4,5,5}
help数组为
{12,103,54,114,37,89}
*/
//交接,对高一位的数进行排序
for(i = L,j = 0;i <= R;i++,j++)
{
arr[i] = help[j];
}
}
}
/**
* 获取数字n的从右往左数第d位数,没有则为0
*
*/
public int getDigit(int n,int d)
{
for(int i = 1;i < d;i++) n /= 10;
return n % 10;
}
排序总结
时间复杂度 | 额外空间复杂度 | 稳定性 | |
---|---|---|---|
选择排序 | O ( N 2 ) O(N^2) O(N2) | O ( 1 ) O(1) O(1) | 无 |
冒泡排序 | O ( N 2 ) O(N^2) O(N2) | O ( 1 ) O(1) O(1) | 有 |
插入排序 | O ( N 2 ) O(N^2) O(N2) | O ( 1 ) O(1) O(1) | 有 |
归并排序 | O ( N ∗ l o g N ) O(N*logN) O(N∗logN) | O ( N ) O(N) O(N) | 有 |
随机快排 | O ( N ∗ l o g N ) O(N*logN) O(N∗logN) | O ( l o g N ) O(logN) O(logN) | 无 |
堆排序 | O ( N ∗ l o g N ) O(N*logN) O(N∗logN) | O ( 1 ) O(1) O(1) | 无 |
*计数排序 | O ( N ) O(N) O(N) | O ( M ) O(M) O(M) | 有 |
%基数排序 | O ( N ) O(N) O(N) | O ( N ) O(N) O(N) | 有 |
可以适当的混合使用排序算法提升排序效率。