排序
^运算:
^ 异或操作,理解为不进位相加
有交换率。
a^a=0
a^0=a
public static void sway(int[] arr,int i,int j){
if(i!=j){
//不能两个值指向同一地址
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];//就是arr[i]^arr[j]^arr[j]就表示a
arr[i]=arr[i]^arr[j];//表示arr[i]^arr[j]^arr[i]^arr[j]^arr[j]就是b
}
}
找一个单数
一组数只有一个数出现一次,其他出现两次,找出这个数:
public static void main(String[] args) {
int[] ints = {2, 2, 4, 4, 6, 8, 99, 99, 6, 8, 43, 76, 43, 76, 111};
int med=0;
for (int a:ints){
med^=a;//两次出现就会变成0,单数就保留
}
System.out.println(med);
}
找两个个单数
一组数只有两个数出现一次,其他出现两次,找出这两个数:
public static void main(String[] args) {
int[] ints = {2, 2, 4, 4, 6, 8, 99, 99, 6, 8, 43, 76, 43, 76, 111,333};
int med=0;
for (int a:ints){
med^=a;//两个不同的单数^最后得到med
}
int rightOne=med&(~med+1);//取出med中二进制为1的位值(必存在,因为不同值)
int med1=0;
for (int a:ints){
//对应位为1的值取出进行^最后的到两个单数对应位为1的
// (a&rightOne)== 0得到对应位为0
if ((a&rightOne)== rightOne){
med1^=a;
}
}
System.out.println(med1);//两个单数其中一个值
System.out.println(med^med1);//两个单数令一个值
}
对数器
public static void isSuccess(){
Random random = new Random();
int[] ints = new int[100];
int[] ints1;
for (int i=0;i<10000;i++){
for (int i1 = 0; i1 < ints.length; i1++) {
ints[i1]=random.nextInt(100);
}
ints1=ints.clone();
int[]ints2=ints.clone();
//两种排序方法进行排序
mergeSort(ints,0,ints.length-1,new int[ints.length]);
optionSort(ints1);
if (!Arrays.equals(ints,ints1)){
System.out.println(Arrays.toString(ints2)+"失败");
return;
}
}
System.out.println("成功");
冒泡排序
public static void papawSort(int[] arr,int start,int end){
boolean flag;
for (int i=end;i>start;i--){
flag=true;
for (int j=start;j<i;j++){
if(arr[j]>arr[j+1]){
sway(arr,j,j+1);
flag=false;
}
}
if (flag){
return;
}
}
}
选择排序
public static void optionSort(int[] arr) {
int len = arr.length - 1;
for (int i = 0; i < len; i++) {//不需要进行最后一个
int temp = i;
for (int j = temp + 1; j <= len; j++) {//要查找最后一位
if (arr[temp] > arr[j]) {//下标转换
temp = j;
}
}
sway(arr, i, temp);
}
}
插入排序
前移(快):
public static void insertSort(int[] arr) {
for (int i=1;i<arr.length;i++){
int insertValue = arr[i];
int i2 = i-1;
while (i2>=0&&insertValue<arr[i2]){
arr[i2+1]=arr[i2];
i2--;
}
arr[i2+1]=insertValue;
}
}
交换:
public static void insertSort(int[] arr) {
for (int i=1;i<arr.length;i++){
for (int j=i-1;j>=0;j--){
if (arr[j]>arr[j+1]){
sway(arr,j,j+1);
}else {
break;
}
}
}
}
希尔排序
public static void shellSort(int[] arr) {
int N=arr.length;
for (int eachNum=N/3;eachNum>0;eachNum/=3){
for (int index=eachNum;index<arr.length;index++){
int target=arr[index];
int step=index-eachNum;
while (step>=0&&target<arr[step]){
arr[step+eachNum]=arr[step];
step-=eachNum;
}
arr[step+eachNum]=target;
}
}
}
快速排序
public static void quickSort(int[] arr,int left,int right) {
if (left>=right){
return;
}
int base=arr[left];
int i=left;
int j=right;
while (i!=j){
while (i!=j&&arr[j]>=base){
j--;
}
while (i!=j&&arr[i]<=base){
i++;
}
sway(arr,i,j);
}
sway(arr,left,i);
quickSort(arr,left,i-1);
quickSort(arr,i+1,right);
桶排序
public static void bucketSort(int[] arr) {
int max=arr[0];
for (int a:arr)
if (a>max)
max=a;
int[] bucket = new int[max+1];
for (int a:arr){
bucket[a]++;
}
int index=0;
for (int i = 0; i < bucket.length; i++) {
for (int j=0;j<bucket[i];j++){
arr[index++]=i;
}
}
基数排序
public static void RadixSort(int[] arr) {
int max=arr[0];
for (int value : arr)
if (max < value)
max = value;
int maxNum=max+"".length();
int[][] bucket=new int[10][arr.length];
int[] numberValue=new int[10];
for (int i=0;i<maxNum;i++){
for (int j = 0; j < arr.length; j++) {
int a=arr[j]/(int)Math.pow(10,i)%10;
bucket[a][numberValue[a]++]=arr[j];
}
int index=0;
for (int j=0;j<10;j++){
for (int k=0;k<numberValue[j];k++){
arr[index++]=bucket[j][k];
}
numberValue[j]=0;
}
}
}
归并排序
归并排序没有浪费任何一次比较,都将比较的结果记录了下来
public static void mergeSortTest(int[] arr, int left, int right, int[] temp){
if(left<right){//不能相同
int m=(left+right)/2;
mergeSortTest(arr,left,m,temp);
mergeSortTest(arr,m+1,right,temp);
mergeTest(arr,left,m,right,temp);
}
}
private static void mergeTest(int[] arr, int left, int m, int right, int[] temp) {
int i=left;
int j=m+1;
int tempIndex=0;
while (i<=m&&j<=right){//这里需要是相同
temp[tempIndex++]=arr[i]<arr[j]?arr[i++]:arr[j++];
}
while (i<=m)//这里需要是相同
temp[tempIndex++]=arr[i++];
while (j<=right)//这里需要是相同
temp[tempIndex++]=arr[j++];
System.arraycopy(temp, 0, arr, left, tempIndex);
}
基于归并排序的小数和
数组的每个值前比他本身小的和的总和。求数组的小数和
public static int mergeSortTest(int[] arr, int left, int right, int[] temp){
if(left<right){
int m=(left+right)/2;
return
mergeSortTest(arr,left,m,temp)+
mergeSortTest(arr,m+1,right,temp)+
mergeTest(arr,left,m,right,temp);
}
return 0;
}
private static int mergeTest(int[] arr, int left, int m, int right, int[] temp) {
int i=left;
int j=m+1;
int tempIndex=0;
int res=0;
while (i<=m&&j<=right){
res+=arr[i]<arr[j]?arr[i]*(right-j+1):0;//若左小,就是会出现小数的位置。个数有右侧确定。
temp[tempIndex++]=arr[i]<arr[j]?arr[i++]:arr[j++];
}
while (i<=m)
temp[tempIndex++]=arr[i++];
while (j<=right)
temp[tempIndex++]=arr[j++];
System.arraycopy(temp, 0, arr, left, tempIndex);
return res;
}
堆排序
public static void heapSort(int[] arr){
int N=arr.length-1;
for (int i=(arr.length-1)/2;i>=0;i--){
sink(arr,i,N);
}
while (N!=0){
sway(arr,N--,0);
sink(arr,0,N);
}
}
private static void sink(int[] arr,int k,int N){
while (N>=2*k+1){
int i=2*k+1;
if(i+1<=N&&arr[i]<arr[i+1]){
i++;
}
if (arr[k] > arr[i]) {
break;
}
sway(arr,k,i);
k=i;
}
}
二分
import java.util.ArrayList;
import java.util.List;
public class thirteen_FindAlgorithm {
public static void main(String[] args) {
InsertValue.insertValue(new int[]{1, 2, 4, 5,}, 0, 3, 3);
BinaryResearch.singleBinaryResearch(new int[]{1, 2, 4, 5,}, 0, 3, 3);
BinaryResearch.binaryResearch(new int[]{1, 2, 3, 4, 5,}, 3);
}
}
class BinaryResearch{
//非递归形式
public static int binaryResearch(int[] arr,int value){
int left=0;
int right=arr.length-1;
while (left<=right){
int med=(left+right)/2;
if(arr[med]>value){
right=med;
}else if (arr[med]<value){
left=med;
}else {
return med;
}
}
return -1;
}
//递归形式
public static int singleBinaryResearch(int[] arr,int left,int right,int value){
//结束条件
if(left> right){
return -1;
}
int med=(left+right)/2;
//确定递归方向
if(arr[med]>value){
return singleBinaryResearch(arr,left,med-1,value);
}else if(arr[med]<value){
return singleBinaryResearch(arr,med+1,right,value);
}else {
//返回下标
return med;
}
}
//返回该值所有的对应值的下标,返回列表
public static List<Integer> listBinaryResearch(int[] arr,int left,int right,int value){
//结束条件
if(left> right){
return null;
}
int med=(left+right)/2;
//确定递归方向
if(arr[med]>value){
return listBinaryResearch(arr,left,med-1,value);
}else if(arr[med]<value){
return listBinaryResearch(arr,med+1,right,value);
}else {
List<Integer> list=new ArrayList<>();
//左搜索相同的数,记录下标
int l=med;
while (arr[med]==arr[--l]){
list.add(l);
}
//记录本身
list.add(med);
//右搜索相同的数,记录下标
int r=med;
while (arr[med]==arr[++r]){
list.add(r);
}
return list;
}
}
}
class InsertValue{
public static int insertValue(int[] arr, int left, int right, int value){
//结束条件arr[left]>value||arr[right]<value是必要的,防止med等于一个数
if(left> right||arr[left]>value||arr[right]<value){
return -1;
}
//改变中值方式
int med=left+(value-arr[left])*(right-left)/(arr[right]-arr[left]);
//确定递归方向
if(arr[med]>value){
return insertValue(arr,left,med-1,value);
}else if(arr[med]<value){
return insertValue(arr,med+1,right,value);
}else{
//返回下标
return med;
}
}
}
查找极小值
该值比左右的值都小,如果是在数组两侧则只比较一点即可。
在无序数组中找到该极小值
public class Test{
public static void main(String[] args) {
System.out.println(limitMin(new int[]{235, 34,53, 53, 354, 454, 45, 65}, 0, 7));
}
public static int limitMin(int[] arr,int left,int right){
if(arr[left]<arr[left+1]){
return arr[left];
}else if (arr[right]<arr[right-1]){
return arr[right];
}
int med=(left+right)/2;
if(arr[med+1]>arr[med]){
return limitMin(arr,left,med);
}else {
return limitMin(arr,med,right);
}
}
}
稀疏数组
public class one_SparseArray {
public static void main(String[] args){
// 创建数组
int[][] arr =new int[11][11];
arr[2][6]=1;
arr[6][1]=4;
arr[4][8]=3;
SparseArray.show(arr);
//转为稀疏数组
int[][]sparse= SparseArray.arrToSparse(arr);
// 打印稀疏数组
SparseArray.show(sparse);
//将稀疏数组内容读取出来返回原始数组
int[][] retrunArray= SparseArray.sparseToArray(sparse);
//再次打印得到的元素
SparseArray.show(retrunArray);
}
}
class SparseArray {
public static void show(int[][] arr){
System.out.println();
for(int[] arr1:arr){
for (int a:arr1){
System.out.printf("%d\t",a);
}
System.out.println();
}
}
public static int[][] arrToSparse(int[][]arr){
// 查看共存在多少个非零元素
int num = 0;
for(int[] arr1:arr){
for (int a:arr1){
if(a!=0)
num++;
}
}
// 根据非零元素的个数创建稀疏数组
int[][] sparse=new int[num+1][3];
// 先将第一行进行初始化,记录总元素元素行列数和个数
sparse[0][0]=11;
sparse[0][1]=11;
sparse[0][2]=num;
// 用于判断是否查到了所有数据
int rowindex=1;
boolean b=true;
b:for(int i=0;i<11;i++){
for(int j=0;j<11;j++){
// 若元素不等于零则存储在sparse中
if(arr[i][j]!=0){
sparse[rowindex][0]=i;
sparse[rowindex][1]=j;
sparse[rowindex][2]=arr[i][j];
//够了就退出循环
if(++rowindex==num+1){
break b;
}
}
}
}
return sparse;
}
public static int[][] sparseToArray(int[][] sparse){
int[][] retrunArray=new int[sparse[0][0]][sparse[0][1]];
//这里是 <= 不是 < 一定要注意
for (int ints=1 ; ints<=sparse[0][2];ints++) {
retrunArray[sparse[ints][0]][sparse[ints][1]]=sparse[ints][2];
}
return retrunArray;
}
}
队列
线性队列
import java.util.Scanner;
/*
线性队列:
先进先出
*/
public class two_LineQueue {
public static void main(String[] args) {
LineQueue queue = new LineQueue(4);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请输入0:添加 1:获取 2:展示 3:是否满了 -1:退出");
int i = scanner.nextInt();
switch (i){
case -1:
return;
case 0:
System.out.println("请输入");
queue.add(scanner.nextInt());
break;
case 1:
queue.get();
break;
case 2:
queue.show();
break;
case 3:
queue.isFull();
}
}
}
}
class LineQueue {
private int header;
private int tail;
private int eleNum;
private int[] arr;
public void isFull(){
if(header==eleNum){
System.out.println("满了");
}else {
System.out.println("未满");
}
}
public LineQueue(int eleNum) {
this.eleNum = eleNum;
arr=new int[eleNum];
}
public void add(int i){
if(header!=eleNum){
arr[header++]=i;
}
else {
System.out.println("添加失败,元素已满~~~");
}
}
public void get(){
if(tail!=header){
System.out.println(arr[tail++]);
}
else {
System.out.println("队中已无元素");
}
}
public void show(){
for (int i =tail; i<header;i++) {
System.out.printf("arr[%d] = %d\n",i,arr[i]);
}
}
}
环形队列
所需队列个数:eleNum-1
真实个数:eleNum
队列满:(header + 1) % eleNum == tail
队列空:header==tail
队列元素个数:(header-tail+eleNum)%eleNum:
import java.util.Scanner;
/*
环形队列:
重复使用空间
*/
public class three_CircleQueue {
public static void main(String[] args) {
CircleCQueue cQueue = new CircleCQueue(4);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请输入0:添加 1:获取 2:展示 3:是否满了 4:是否空 -1:退出");
int i = scanner.nextInt();
switch (i){
case -1:
return;
case 0:
System.out.println("请输入");
cQueue.add(scanner.nextInt());
break;
case 1:
cQueue.get();
break;
case 2:
cQueue.show();
break;
case 3:
if(cQueue.isFull()){
System.out.println("满了");
}
else {
System.out.println("没满");
}
break;
case 4:
if(cQueue.isEmpty()){
System.out.println("空了");
}
else {
System.out.println("没空");
}
}
}
}
}
class CircleCQueue {
int[] arr;
int header;
int tail;
int eleNum;
/*
环形队列最重要的就是:何如区分队列中满情况和空情况
若创建的队列大小和所需要的大小相同则很难区别他们
创建比所需大一个的队列就能满足:
所需队列个数:eleNum-1
真实个数:eleNum
队列满:(header + 1) % eleNum == tail
队列空:header==tail
队列元素个数:(header-tail+eleNum)%eleNum:
关于:header-tail+eleNum
我们在运行过程中不知道header和tail谁大
当header>tail时:
header-tail就是元素个数,此时+eleNum没有起到作用,但是%eleNum起到了作用
另外(因为header指向空,所以不是header-tail+1)
当header<tail时:
header-tail就是一个负数,且该负数绝对值和元素个数之和为eleNum
所以+eleNum能使变为正数同时变成了元素个数
由此可见:这句代码可以写成:
if(header-tail>=0){
元素个数 = (header-tail)%eleNum;
}
else{
元素个数 = header-tail+eleNum;
}
在程序中保证:
header指向的空间位置不存在元素(范围为:0~10,所以要%eleNum,而非真实大小eleNum-1)
tail指向的空间位置除了空以外都不指向空(范围为:0~10)
*/
public CircleCQueue(int n){
this.eleNum=n+1;
arr=new int[eleNum];
}
public boolean isFull(){
return (header + 1) % eleNum == tail;
}
public boolean isEmpty(){
return header==tail;
}
public void add(int i){
if(!this.isFull()){
arr[header++]=i;
//不能让header一直++,控制在0~10范围内
header=header%eleNum;
}
else {
System.out.println("添加失败,元素已满~~~");
}
}
public void get(){
if(!this.isEmpty()){
System.out.println(arr[tail++]);
//不能让tail一直++,控制在0~10范围内
tail=tail%eleNum;
}
else {
System.out.println("队中已无元素");
}
}
public void show(){
if(this.isEmpty()){
System.out.println("无元素,显示毛线");
return;
}
//遍历时要从tail开始,以 tail+元素个数 结束
for (int i =tail; i<tail+(header-tail+eleNum)%eleNum;i++) {
//i%(eleNum)或i%(eleNum-1)都可以,严谨的是i%(eleNum-1)
System.out.printf("arr[%d] = %d\n",i%(eleNum-1),arr[i%(eleNum-1)]);
}
}
}
栈
import java.util.Objects;
public class seven_ArrayStack {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(3);
stack.add(new SevenPerson("aaaa",33));
stack.add(new SevenPerson("bbbb",3));
stack.add(new SevenPerson("ccc",34));
stack.add(new SevenPerson("eee",33));
stack.show();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.show();
}
}
class ArrayStack{
private int top;
private int maxNum;
private SevenPerson[] stack;
public ArrayStack(int maxNum) {
this.maxNum= maxNum;
this.stack=new SevenPerson[maxNum];
this.top = -1;
}
public void add(SevenPerson person){
if(top+1==maxNum){
System.out.println("添加失败,栈元素已满。");
return;
}
stack[++top]=person;
}
public SevenPerson pop(){
if(top==-1){
System.out.println("栈空,无元素");
return null;
}
System.out.println("已出栈:"+stack[top]);
return stack[top--];
}
public void show(){
if(top==-1){
System.out.println("无元素");
}
for (int i = top; i!=-1; i--) {
System.out.println(stack[i]);
}
}
}
class SevenPerson{
private String name;
private int age;
public SevenPerson(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SevenPerson that = (SevenPerson) o;
return age == that.age &&
Objects.equals(name, that.name);
}
@Override
public String toString() {
return "SevenPerson{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
栈应用之无括号加减运算
public class eight_NumberStack {
public static void main(String[] args) {
NumberOpStack numberOpStack = new NumberOpStack("1-2*211-1+1+1-3+1");
System.out.println(numberOpStack.res());
}
}
class NumberOpStack {
String string;
public NumberOpStack(String s){
this.string=s;
}
public int res(){
assert string != null;
OpStack opStack = new OpStack(string.length());
NumStack numStack = new NumStack(string.length());
for(int i=0;i< this.string.length();i++){
char a=string.charAt(i);
if (isOper(a)){
if(opStack.isEmpty()){
opStack.add(a);
continue;
}
//>=不是>
if (priority(a) < priority(opStack.showPop())) {
//前一位优先级高就先运算
int num1 = numStack.pop();
int num2 = numStack.pop();
int i1 = jiSua(num2, num1, opStack.pop());
numStack.add(i1);
}
opStack.add(a);
}else {
String s=String.valueOf(a);
//连续读取字符串中连续的数字知道读至字符串末尾或读至符号
while (true){
if(i+1==string.length()||isOper(string.substring(i+1,i+2).charAt(0))){
break;
} else {
s+=string.substring(i+1,i+2);
i++;
}
}
numStack.add(Integer.parseInt(s));
}
}
//进行只有+或-的运算
while (true){
if (opStack.showPop()==0){
return numStack.pop();
}
int num1=numStack.pop();
int num2=numStack.pop();
char o=opStack.pop();
//若该符号前面为‘-’,则需要‘-’变‘+’,‘+’变‘-’
if(opStack.showPop()=='+'||opStack.showPop()==0){
numStack.add(jiSua(num2, num1,o));
}else {
numStack.add(jiSua(num2, num1,opReserve(o)));
}
}
}
private char opReserve(char a){
if (a == '+')
return '-';
else
return '+';
}
private Integer jiSua(int a,int b,char c){
switch (c){
case '+':
return a+b;
case '-':
return a-b;
case '*':
return a*b;
case '/':
return a/b;
}
return null;
}
private int priority(char oper){
if(oper=='*'||oper=='/'){
return 1;
} else if (oper=='+'||oper=='-'){
return 0;
}else {
return -1;
}
}
private boolean isOper(char val){
return val=='+'||val=='-'||val=='*'||val=='/';
}
}
class NumStack{
int top;
int[] numStack;
NumStack(int n){
numStack=new int[n];
top=-1;
}
public void add(int num){
numStack[++top]=num;
}
public int pop(){
return numStack[top--];
}
}
class OpStack{
int top;
char[] opStack;
OpStack(int n){
opStack=new char[n];
top=-1;
}
public boolean isEmpty(){
return top==-1;
}
public void add(char op){
opStack[++top]=op;
}
public char pop(){
if(top==-1){
return 0;
}
return opStack[top--];
}
public char showPop(){
if(top==-1){
return 0;
}
return opStack[top];
}
}
逆波兰表达式
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class none_NumberStack {
public static void main(String[] args) {
NStack nStack = new NStack("(((1)+((2+3)*4))-5)");
System.out.println(nStack.res());
}
}
class NStack{
String string;
public NStack(String s){
this.string=s;
}
public int res(){
Stack s1=new Stack();
Stack s2=new Stack();
//**********************将字符串转化为逆波兰表达式****************
for(int i=0;i<string.length();i++){
char c = string.charAt(i);
if(isOper(c)){
//为非数字时:
if(c==')'){
char ch;
while(true){
ch=(char)s2.pop();
if (ch=='('){
break;
}
s1.push(ch);
}
} else if(s2.isEmpty()||
(char) s2.get(s2.size()-1)=='('||
c=='('||
priority(c)>priority((char)s2.get(s2.size()-1))){
s2.push(c);
}else {
s1.push(s2.pop());
while (true){
if((s2.isEmpty()||(char) s2.get(s2.size()-1)=='('||priority(c)>priority((char)s2.get(s2.size()-1)))){
break;
}
s1.push(s2.pop());
}
s2.push(c);
}
}else {
//为数字时:
String s=""+c;
//读取所有紧邻数字:
while (true){
if(i+1==string.length()||isOper(string.substring(i+1,i+2).charAt(0))){
break;
}
else {
s+=string.substring(i+1,i+2);
i++;
}
}
s1.push(s);
}
}
//最后将所有s2中数据放在s1中
while (!s2.isEmpty()) {
s1.push(s2.pop());
}
//真正的逆波兰表达式为反过来的:
s1=Reserve(s1);
//放在list中
List<String> list = new ArrayList();
while (!s1.isEmpty()){
list.add(String.valueOf(s1.pop()));
}
//*****************************计算逆波兰表达式********************
Stack<String> s = new Stack<>();
for (String s3 : list) {
if(s3.matches("\\d+")){
s.push(s3);
}else {
int num1=Integer.parseInt(s.pop());
int num2=Integer.parseInt(s.pop());
int i = jiSua(num2, num1, s3.charAt(0));
s.push(String.valueOf(i));
}
}
//数据就放在栈s中
return Integer.parseInt(s.pop());
}
private Stack Reserve(Stack stack){
Stack s=new Stack();
while (!stack.isEmpty()) {
s.push(stack.pop());
}
return s;
}
private Integer jiSua(int a,int b,char c){
switch (c){
case '+':
return a+b;
case '-':
return a-b;
case '*':
return a*b;
case '/':
return a/b;
}
return null;
}
private int priority(char oper){
if(oper=='*'||oper=='/'){
return 1;
} else if (oper=='+'||oper=='-'){
return 0;
}else {
return -1;
}
}
private boolean isOper(char val){
return val=='+'||val=='-'||val=='*'||val=='/'||val=='('||val==')';
}
}
链表
单链表
// 链表
public class four_SingleLinkedList {
public static void main(String[] args) {
SingleLinkedList sLL = new SingleLinkedList();
sLL.add(new Person("小明",13,1));
sLL.add(new Person("小王",15,2));
sLL.add(new Person("小张",13,3));
sLL.add(new Person("小红",12,5));
sLL.add(new Person("小蓝",14,6));
sLL.insertIndex(new Person("死培明",101,4));
sLL.reload(new Person("驴培明",101,4));
sLL.reload(new Person("培明",101,7));
sLL.delete(6);
sLL.show();
SingleLinkedList s=sLL.reserve();
s.show();
}
}
//可以创建一个指向最后一个节点的指针,方便运算
class SingleLinkedList {
//初始化节点
Person header=new Person(null,0,0);
//**************************尾部添加节点**********************
public void add(Person p){
Person tail=header;
while(null != tail.next){
tail=tail.next;
}
tail.next=p;
}
//***************************显示数据***************************
public void show(){
if(null == header.next){
System.out.println("空链表");
return;
}
Person tail=header.next;
while(null != tail){
System.out.println(tail);
tail=tail.next;
}
}
//*************************按排名添加*****************************
public void insertIndex(Person p){
Person tail=header;
boolean flag=false;
while (true){
if(tail.next==null){
break;
}
if(tail.next.index>p.index){
break;
}else if (tail.next.index==p.index){
flag=true;
break;
}
tail=tail.next;
}
if(flag){
System.out.println("已有排名: "+p.index);
}else {
p.next=tail.next;
tail.next=p;
}
}
//********************修改元素*********************
public void reload(Person p){
if(null==header.next){
System.out.println("没有这个人");
return;
}
Person tail=header.next;
boolean flag=false;
while (tail != null) {
if (tail.index == p.index) {
tail.name = p.name;
tail.age = p.age;
return;
}
tail = tail.next;
}
System.out.println("没找到,修改失败: "+p);
}
//*******************删除节点*********************
public void delete(int index){
Person tail=header;
while (tail.next!=null){
//包括了判断最后一个节点
if(tail.next.index==index){
tail.next=tail.next.next;
return;
}
tail=tail.next;
}
System.out.println("没有这个元素");
}
//******************反序链表的元素*****************
//每遍历一个元素就插入到头结点的下一个值
public SingleLinkedList reserve(){
SingleLinkedList s = new SingleLinkedList();
Person tail=header;
while (tail.next!=null){
Person newLeaf= null;
try {
//深度克隆
newLeaf = tail.next.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
assert newLeaf != null;
newLeaf.next=s.header.next;
s.header.next=newLeaf;
tail=tail.next;
}
return s;
}
}
class Person implements Cloneable{
String name;
int age;
int index;
Person next;
public Person(String name, int age, int index) {
this.name = name;
this.age = age;
this.index = index;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", index=" + index +
'}';
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
环形单链表
import java.util.Objects;
public class six_CircleSingleLinkedList {
public static void main(String[] args) {
CircleSingleLinkedList cSLL=new CircleSingleLinkedList(7);
cSLL.show();
System.out.println();
try {
cSLL.delete(4);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class CircleSingleLinkedList{
private CirclePerson header=null;
//构造器创建n个孩子
CircleSingleLinkedList(int n){
CirclePerson tail=null;
for (int i = 0; i < n; i++) {
CirclePerson str=new CirclePerson(i+1);
//若是第一个孩子,要特殊考虑创建头header
if(i+1==1){
tail=str;
tail.next=tail;
header=tail;
}else {
tail.next=str;
str.next=header;
tail=str;
}
}
}
public void show(){
if(header==null){
System.out.println("空");
return;
}
CirclePerson tail=header;
while (tail.next!=header){
System.out.println(tail);
tail=tail.next;
}
}
public void delete(int num){
if (header==null){
throw new RuntimeException("无小孩");
}
CirclePerson tail=header;
//创建一个指针指向遍历元素后一个,以便后续删除节点
while (tail.next != header) {
tail = tail.next;
}
while (true){
//若只有一个元素,结束
if(tail==header){
System.out.println(header);
return;
}
for (int i = 0; i < num-1; i++) {
tail=tail.next;
header=header.next;
}
System.out.println(header);
header=header.next;
//只需要改变tail指向,不需要改变tail本身位置
tail.next=header;
}
}
}
class CirclePerson{
CirclePerson next;
int num;
CirclePerson(int num){
this.num =num;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CirclePerson that = (CirclePerson) o;
return Objects.equals(num, that.num);
}
@Override
public String toString() {
return "CirclePerson{" +
"num=" + num +
'}';
}
}
双链表
import java.util.Objects;
public class five_DoubleLinkedList {
public static void main(String[] args) {
DoubleLinkedList dLL = new DoubleLinkedList();
}
}
class DoubleLinkedList{
DoubleList header=new DoubleList(null);
//********************添加******************
public void add(DoubleList newNum){
DoubleList tail=header;
while (tail.down!=null){
tail=tail.down;
}
tail.down= newNum;
newNum.up=tail;
}
//*******************删除***************
public void delete(DoubleList newNum){
if(header.down==null){
System.out.println("空");
return;
}
DoubleList tail=header.down;
while (tail!=null){
if(tail.equals(newNum)){
tail.up.down=tail.down;
if(tail.down!=null){//若只有一个元素就不用运行
tail.down.up=tail.up;
}
return;
}
tail=tail.down;
}
System.out.println("没有,删除失败");
}
//******************修改******************
//****************显示*****************
public void show(){
if(null == header.down){
System.out.println("空链表");
return;
}
DoubleList tail=header.down;
while(null != tail){
System.out.println(tail);
tail=tail.down;
}
}
}
class DoubleList{
DoubleList up;
Integer num;
DoubleList down;
public DoubleList(Integer num) {
this.num = num;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DoubleList that = (DoubleList) o;
return Objects.equals(num, that.num);
}
@Override
public String toString() {
return "DoubleList{" +
"num=" + num +
'}';
}
}
递归
迷宫问题
public class ten_Maze {
public static void main(String[] args) {
Maze maze = new Maze(10, 10);
maze.addMaze(4,1);
maze.addMaze(4,2);
maze.addMaze(1,3);
maze.exit(1,2);
maze.show();
}
}
class Maze{
int[][] maze;
int rows;
int lines;
//初始化
public Maze(int rows,int lines){
this.lines=lines;
this.rows=rows;
maze=new int[rows][lines];
for(int i=0;i<rows;i++){
maze[i][0]=1;
maze[i][rows-1]=1;
}
for(int i=0;i<lines;i++){
maze[0][i]=1;
maze[rows-1][i]=1;
}
}
//添加阻碍
public void addMaze(int i,int j){
if(!(i>0||i<rows-1)){
throw new RuntimeException("行数错误");
}
if(!(j>0||j<lines-1)){
throw new RuntimeException("列数错误");
}
maze[i][j]=1;
}
//进行找出路
public boolean exit(int i,int j){
if(maze[rows-2][lines-2]=='*'){//定义出口
return true;
}else {
if(maze[i][j]==0){
maze[i][j]='*';
if(exit(i+1,j))//下
return true;
else if (exit(i,j-1))//左
return true;
else if (exit(i,j+1))//右
return true;
else if (exit(i-1,j))//上
return true;
else {
maze[i][j]='#';
return false;
}
}else return false;
}
}
//显示
public void show(){
for(int i=0;i<rows;i++){
for(int j=0;j<lines;j++){
System.out.print((char)maze[i][j]+"\t");
}
System.out.println();
}
}
}
八皇后
package Test;
import sun.util.resources.cldr.mgh.CalendarData_mgh_MZ;
import java.util.Arrays;
public class Test{
public static void main(String[] args) {
EightQueen eightQueen = new EightQueen();
eightQueen.walk(0);
System.out.println(eightQueen.num);
}
}
class EightQueen{
int []arr;
int num;
public EightQueen(){
arr=new int[8];
}
public void walk(int n){
if (n==8){
System.out.println(this);
num++;
}else {
for (int i=0;i<arr.length;i++){
arr[n]=i;
if (isSuccess(n)){
walk(n+1);
}
}
}
}
private boolean isSuccess(int n){
for(int i=0;i<n;i++){
if(arr[i]==arr[n]||Math.abs(i-n)==Math.abs(arr[i]-arr[n])){
return false;
}
}
return true;
}
@Override
public String toString() {
return "EightQueen{" +
"arr=" + Arrays.toString(arr) +
'}';
}
}
汉诺塔
public class twenty_one_HanoiTower {
public static void main(String[] args) {
HanoiTower.hanoiTower(5,'A','B','C');
System.out.println(HanoiTower.count);
}
}
class HanoiTower{
static int count;
//相当于将num个从a移动到c
public static void hanoiTower(int num,char a,char b,char c){
if(num==1){
count++;
System.out.println(a+"-->"+c);
}else {
//中间一定有上面的num-1个从a移动到了b
//我们会将num-1个看成num个给hanoiTower,并且将b看成c柱子
hanoiTower(num-1,a,c,b);
System.out.println(a+"-->"+c);
count++;
//中间一定有上面的num-1个从b移动到了c
//我们会将num-1个看成num个给hanoiTower,并且将b看成a柱子
hanoiTower(num-1,b,a,c);
}
}
}
马踏棋盘
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class twenty_eight_Horse {
public static void main(String[] args) {
long l = System.currentTimeMillis();
Horse.horse(4,5);
long l1 = System.currentTimeMillis();
System.out.println(l1-l);
Horse.show();
}
}
class Horse {
//棋盘显示每个格子的第几步的值
private static int[][] chessboard;
private static int rows;
private static int lines;
//是否运行完,区分失败回溯还是成功返回结束程序
private static boolean finished;
public static void horse(int row,int line){
chessboard=new int[row][line];
rows=row;
lines=line;
finished=false;
int step=1;
horse(chessboard,0,0,step);
}
private static void horse(int[][] board,int startRow,int startLines,int step){
board[startRow][startLines]=step;
List<Point> nextPoints = next(new Point(startRow, startLines));//返回下一步可以走的点集合
sort(nextPoints);//优先运行该节点拥有的下一个节点最少的
while (!nextPoints.isEmpty()){
Point p=nextPoints.remove(0);
if(board[p.x][p.y]==0){//若没有运行过
horse(board,p.x,p.y,step+1);
}
}
//step<rows*lines表示未成功,回溯重试
//!finished防止在成功后结束运行的过程中将棋盘置为0
if(step<rows*lines&&!finished){
board[startRow][startLines]=0;
}else if(step==rows*lines){//只在成功时设置为true,在成功路径上就不会将棋盘设置为0
finished=true;
}
}
private static void sort(List<Point> list){
list.sort((o1, o2) -> {
int num1=next(o1).size();
int num2=next(o2).size();
return num1-num2;
});
}
private static List<Point> next(Point p) {
List<Point> list = new ArrayList<>();
Point p1 = new Point();
if ((p1.x = p.x - 1) >= 0 && (p1.y = p.y - 2) >= 0) {
list.add(new Point(p1));
}
if ((p1.x = p.x - 1) >= 0 && (p1.y = p.y + 2) < lines) {
list.add(new Point(p1));
}
if ((p1.x = p.x + 1) < rows && (p1.y = p.y - 2) >= 0) {
list.add(new Point(p1));
}
if ((p1.x = p.x + 1) < rows && (p1.y = p.y + 2) < lines) {
list.add(new Point(p1));
}
if ((p1.x = p.x - 2) >= 0 && (p1.y = p.y - 1) >= 0) {
list.add(new Point(p1));
}
if ((p1.x = p.x - 2) >= 0 && (p1.y = p.y + 1) < lines) {
list.add(new Point(p1));
}
if ((p1.x = p.x + 2) < rows && (p1.y = p.y + 1) < lines) {
list.add(new Point(p1));
}
if ((p1.x = p.x + 2) < rows && (p1.y = p.y - 1) >= 0) {
list.add(new Point(p1));
}
return list;
}
public static void show(){
for (int[] ints : chessboard) {
System.out.println(Arrays.toString(ints));
}
}
}
哈希表
线性探测法
数据数量小于
class LPHST<K, V> {
private int N;
private int M = 16;
private K[] keys;
private V[] values;
public LPHST() {
keys = (K[]) new Object[M];
values = (V[]) new Object[M];
}
public LPHST(int M) {
keys = (K[]) new Object[M];
values = (V[]) new Object[M];
this.M = M;
}
public int hashCode(K key) {
return key.hashCode() % M;
}
private void resize(int m) {
LPHST<K, V> lphst = (LPHST<K, V>) new LPHST<>(m);
for (int i = 0; i < M; i++)
if (keys[i] != null)
lphst.put(this.keys[i], this.values[i]);
this.keys = lphst.keys;
this.values = lphst.values;
this.M = lphst.M;
}
private void put(K key, V value) {
if (N >= M / 2) resize(M * 2);
int i;
for (i = hashCode(key); keys[i] != null; i = (i + 1) % M) {
if (keys[i].equals(key)) {
values[i] = value;
return;
}
}
keys[i] = key;
values[i] = value;
N++;
}
public V get(K key) {
for (int i = hashCode(key); keys[i] != null; i = (i + 1) % M)
if (keys[i].equals(key))
return values[i];
return null;
}
public void delete(K key) {
int hash = key.hashCode();
while (keys[hash] == null || key.equals(keys[hash]))
hash = (hash + 1) % M;
if (keys[hash] == null)
return;
keys[hash] = null;
values[hash] = null;
N--;
for (hash = (hash + 1) % M; keys[hash] != null; N--, hash = (hash + 1) % M) {
put(keys[hash], values[hash]);
keys[hash] = null;
values[hash] = null;
}
if (N > 0 && N == M / 8) resize(M / 2);
}
}
拉链法
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class fourteen_HashMap {
public static void main(String[] args) {
HashDemo hashDemo = new HashDemo(5);
hashDemo.hashAdd(new HashDemo.Emp("xiaoming",13));
hashDemo.hashAdd(new HashDemo.Emp("xiaoming",13));
hashDemo.hashAdd(new HashDemo.Emp("xiaoming",-2));
hashDemo.hashFind(new HashDemo.Emp("xiaoming",13));
hashDemo.hashShow();
}
}
class HashDemo{
//哈希表中的链表集合
private LinkedList[] lists;
//哈希表中的链表个数
private int num;
//哈希构造器
public HashDemo(int num){
this.num=num;
lists=new LinkedList[num];
for (int i = 0; i < num; i++) {
lists[i]=new LinkedList();
}
}
//在哈希表中添加元素,根据myHash值调用指定LinkedList中add方法
public void hashAdd(Emp emp){
int i =myHash(emp);
if(i==-1){
return;
}
lists[i].add(emp);
}
//在哈希表中显示元素,按顺序调用LinkedList中show方法
public void hashShow(){
System.out.println("哈希表中所有链表为:");
for (int i = 0; i < num; i++) {
lists[i].show(i+1);
}
}
//在哈希表中查找元素,根据myHash值调用LinkedList中find方法,匹配时调用toString方法
public void hashFind(Emp emp){
int i=myHash(emp);
lists[i].find(i,emp);
}
//生成Emp元素的哈希值
private int myHash(Emp emp) {
if(emp.id<0){
System.out.println("对象id错误");
return -1;
}
return emp.id/num;
}
//哈希表中对应的每个链表对象
class LinkedList{
Emp header;
private void add(Emp emp){
if(header==null){
header=emp;
return;
}
Emp tail=header;
while (tail.next!=null){
tail=tail.next;
}
tail.next=emp;
}
private void show(int index){
if(header==null){
System.out.println("第"+index+"链表为空");
return;
}
Emp tail=header;
System.out.println(index+"中有:");
while (tail!=null){
System.out.println(tail);
tail=tail.next;
}
}
private Set<Map.Entry<Object, Object>> find(int index, Emp emp){
if(header==null){
System.out.println("没有该元素");
}
Emp tail=header;
while (tail!=null){
if(tail.equals(emp)){
HashMap<Object, Object> map = new HashMap<>();
map.put(index,emp);
Set<Map.Entry<Object, Object>> entries = map.entrySet();
return entries;
}
tail=tail.next;
}
System.out.println("没有该元素");
return null;
}
}
//哈希中的元素,设置为静态的,在main函数中可以被直接调用
static class Emp{
private String name;
private int id;
private Emp next;
Emp(String name,int id){
this.name=name;
this.id=id;
}
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Emp emp = (Emp) o;
return id == emp.id &&
Objects.equals(name, emp.name);
}
}
}
二叉树
class AVLTree{
AVLNode root;
public void add(int num){
if(root==null){
root=new AVLNode(num);
return;
}
root.add(new AVLNode(num));
}
public void sort(){
if(root==null){
System.out.println("空");
return;
}
root.sort();
}
public void delete(int value){
if(root==null){
return;
}else {
//定位删除的节点
AVLNode search = root.search(value);
if(search==null){
return;
}
//说明存在value的节点,若是根节点则:
if(root.right==null&&root.left==null){
root=null;
return;
}
//获取value的父节点
AVLNode searchParent = root.searchParent(value);
//若删除节点为根节点,此时root根只有一侧有节点则进行root下沉
//若root中有两侧节点则继续进行程序,在 删除节点有两个子节点 处进行右子树最小值赋值给根节点
if(searchParent==null){
if(search.left!=null&&search.right==null){
root=root.left;
return;
}else if(search.right!=null&&search.left==null){
root=root.right;
return;
}
}
//删除节点没有子节点
if(search.left==null&&search.right==null){
//确定删除节点在父节点的位置
if(searchParent.left!=null&&searchParent.left.num==value){
searchParent.left=null;
if(searchParent.left.)
}else if (searchParent.right!=null&&searchParent.right.num==value){
searchParent.right=null;
}
//删除节点有两个子节点
}else if (search.left!=null&&search.right!=null){
//直接将该值改为右子数的最小值
search.num=root.delTreeMin(search.right);
}else {//删除节点有一个子节点
//左节点不为空
if(search.left!=null){
if(searchParent.left.num==value){
searchParent.left=search.left;
}else {
searchParent.right=search.left;
}
}else {
if(searchParent.left.num==value){
searchParent.left=search.right;
}else {
searchParent.right=search.right;
}
}
}
}
}
}
class AVLNode{
int num;
AVLNode left;
AVLNode right;
public AVLNode(int num) {
this.num = num;
}
public int leftHeight(){
if(left==null){
return 0;
}else {
return left.height();
}
}
public int rightHeight(){
if(right==null){
return 0;
}else {
return right.height();
}
}
public int height(){
return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
}
public void leftRotate(){
AVLNode newNode=new AVLNode(this.num);
newNode.left=this.left;
newNode.right=this.right.left;
this.num=right.num;
this.right=right.right;
this.left=newNode;
}
public void rightRotate(){
AVLNode newNode=new AVLNode(this.num);
newNode.right=this.right;
newNode.left=this.left.right;
this.num=left.num;
this.left=left.left;
this.right=newNode;
}
public void add(AVLNode avl){
if(!(avl.num >= this.num)){
//知道找到适合该值的空节点
if(this.left==null){
this.left=avl;
}else {
this.left.add(avl);
}
}else {
if(this.right==null){
this.right=avl;
}else {
this.right.add(avl);
}
}
if(rightHeight()-leftHeight()>1){
if(right.leftHeight()>right.rightHeight()){
right.rightRotate();
}
this.leftRotate();
}
if(leftHeight()-rightHeight()>1){
if(left.rightHeight()>left.leftHeight()){
left.leftRotate();
}
this.rightRotate();
}
}
public void delete(int n){
}
public AVLNode searchParent(int value){
//递归返回条件
if((this.left!=null&&this.left.num==value)||
(this.right!=null&&this.right.num==value)){
return this;
}else {
//递归的方向问题还是没有对应的节点
if(this.num>value&&this.left!=null){
return this.left.searchParent(value);
}else if(value>=this.num&&this.right!=null){
return this.right.searchParent(value);
}else {
return null;
}
}
}
public AVLNode search(int n){
if(this.num<n){
if (this.left!=null)
return this.right.search(n);
}else if (this.num>n){
if (this.right!=null)
return this.left.search(n);
}else {
return this;
}
return null;
}
//根据该节点进行中序遍历,刚好也是排序
public void sort(){
if(this.left!=null){
this.left.sort();
}
System.out.println(this);
if(this.right!=null){
this.right.sort();
}
}
@Override
public String toString() {
return "AVLNode{" +
"num=" + num +
'}';
}
public int delTreeMin(AVLNode right) {
AVLNode tail=this.right;
if(tail.left==null){
this.right=tail.right;
return this.right.num;
}
while (tail.left.left!=null){
tail=tail.left;
}
if(tail.left.right==null){
int n=tail.left.num;
tail.left=null;
return n;
}else {
int n=tail.left.num;
tail.left=tail.left.right;
return n;
}
}
}
线索化二叉树
public class sixteen_LinkedBinaryTree {
public static void main(String[] args) {
Leaf leaf1 = new Leaf(1);
Leaf leaf2 = new Leaf(2);
Leaf leaf3 = new Leaf(3);
Leaf leaf4 = new Leaf(4);
Leaf leaf5 = new Leaf(5);
Leaf leaf6 = new Leaf(6);
leaf1.left=leaf2;
leaf2.left=leaf4;
leaf2.right=leaf5;
leaf1.right=leaf3;
leaf3.left=leaf6;
LinkedBinaryTree linkedBinaryTree=new LinkedBinaryTree(leaf1);
try {
linkedBinaryTree.linkedBinaryShow();
}catch (Exception e){
System.out.println(e.getMessage());
}
linkedBinaryTree.linkedBinaryTree();
linkedBinaryTree.linkedBinaryShow();
}
}
class LinkedBinaryTree{
Leaf root;
Leaf tail;
//确保先序列化
boolean flag;
public LinkedBinaryTree(Leaf root){
this.root=root;
}
public void linkedBinaryTree(){
if(root!=null){
linkedBinaryTree(this.root);
flag= true;
}
else
System.out.println("空树");
}
private void linkedBinaryTree(Leaf root){
if(root==null){
return;
}
//一直向左遍历,知道root.getLeft()为null时,就进行回溯
// 此时该节点就是最左边的节点,也是中序的起始节点,从该节点开始进行线索化
linkedBinaryTree(root.left);
//对当前结点进行设置前驱结点,第一次运行在这时是中序第一个节点,该节点为了遍历应将leftBoolean设置为true
if(root.left==null){
root.left=tail;
root.leftBoolean=true;
}
//对当前结点进行设置后继结点
if(tail!=null&&tail.right==null){
tail.right=root;
tail.rightBoolean=true;
}
//让后一个前进一位,root该后进一位了
tail=root;
//基于当前节点进行向右遍历
linkedBinaryTree(root.right);
}
public void linkedBinaryShow(){
if(!flag){
throw new RuntimeException("请先线索化");
}
Leaf node=root;
while (node!=null){
// 在一开始的时候可以寻找到中序的第一个元素
// 注意:中序的第一个元素在线索化时leftBoolean已经赋值为true
// 中序的第一个元素也是唯一一个在最左边路径上leftBoolean为true的节点
// 这样才可以使该段代码直接遍历到第一个元素
// 控制遍历过程向左走,直到头才开始输出
while (!node.leftBoolean){
node=node.left;
}
System.out.println(node);
//两种向右遍历的方式:
// 根据链式化遍历的节点就是中序遍历的节点,可以直接输出
// 二叉树节点式遍历的节点式数生成的右节点,并不是中序遍历的节点,不能直接输出,要判断左端是否有节点
//控制向右根据链式化遍历
while (node.rightBoolean){
node=node.right;
System.out.println(node);
}
//控制向右二叉树节点式遍历
node=node.right;
}
}
}
class Leaf{
Integer num;
Leaf left;
Leaf right;
//只有线索化时才为true,正常的树节点均为false(中序第一个节点要为true)
boolean leftBoolean;
boolean rightBoolean;
public Leaf(Integer num) {
this.num = num;
}
@Override
public String toString() {
return "Leaf{" +
"num=" + num +
", leftBoolean=" + leftBoolean +
", rightBoolean=" + rightBoolean +
'}';
}
}
赫夫曼压缩
import java.util.*;
import java.util.Map.Entry;
public class seventeen_HaFuMan {
public static void main(String[] args) {
String s="like love perfect";
HaFuMan.stringToHuffmanBytes(s);
System.out.println(HaFuMan.byteToString());
}
}
class HaFuMan{
//存放每个叶子节点的赫夫曼值
static Map<Byte,String> huffmanCodes=new HashMap<>();
//压缩后的赫夫曼数据
static StringBuffer buffer=new StringBuffer();
//以byte形式存放string对应的赫夫曼数据
static Byte[] bytes;
//存储根节点
static Node huffmanTreeRoot;
static int lastNum;
public static String byteToString(){
StringBuffer sB = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
if(i==bytes.length-1){
sB.append(byteToBitString(lastNum, bytes[i]));
}else {
sB.append(byteToBitString(8,bytes[i]));
}
}
HashMap<String, Byte> objectObjectHashMap = new HashMap<>();
for (Entry<Byte, String> byteStringEntry : huffmanCodes.entrySet()) {
objectObjectHashMap.put(byteStringEntry.getValue(),byteStringEntry.getKey());
}
List<Byte>list=new ArrayList<>();
for (int i = 0; i < sB.length();) {
int n=1;
while (true){
String substring = sB.substring(i, i + n);
if(objectObjectHashMap.containsKey(substring)){
i+=n;
list.add(objectObjectHashMap.get(substring));
break;
}else {
n++;
}
}
}
byte[] bytes=new byte[list.size()];
int i=0;
for (Byte aByte : list) {
bytes[i++]=aByte;
}
return new String(bytes);
}
private static String byteToBitString(int n,byte b){
int temp=b;
if(n==8){
temp |= 256;//若是正数,补高位,因为是取8位,整数不够8位,要对前八位进行补位,所以要用按位或256或者256的2的n次方倍的数进行按位或
String str=Integer.toBinaryString(temp);
return str.substring(str.length()-n);
}else{
String s = temp + "";
for(int i=0;i<n-s.length();i++){
s="0"+s;
}
return s;
}
}
public static void stringToHuffmanBytes(String s){
haFuManTree(s);
getCodes(huffmanTreeRoot);
zip(s.getBytes());
}
//填充buffer和bytes,要在填充huffmanCodes后
private static void zip(byte[] b){
for (byte b1 : b) {
buffer.append(huffmanCodes.get(b1));
}
int len;//len=(buffer.length()+7)/8
if(buffer.length()%8==0){
len=buffer.length()/8;
}else {
len=buffer.length()/8+1;
}
lastNum = buffer.length() % 8;
bytes=new Byte[len];
int index=0;
for (int i = 0; i < buffer.length(); i+=8) {
String ss;
if(i+8>buffer.length()){
ss=buffer.substring(i);
}else {
ss=buffer.substring(i,i+8);
}
bytes[index++]=(byte)Integer.parseInt(ss,2);
}
}
//填充huffmanCodes
private static void getCodes(Node root){
if(root==null){
System.out.println("空");
}else {
getCodes(root,"",new StringBuffer());
}
}
private static void getCodes(Node node,String code,StringBuffer stringBuffer){
StringBuffer s=new StringBuffer(stringBuffer);
s.append(code);
if(node!=null){
if(node.aByte==null){
getCodes(node.left,"0",s);
getCodes(node.right,"1",s);
}else {
huffmanCodes.put(node.aByte,s.toString());
}
}
}
//构造树
private static void haFuManTree(String s){
if(s.length()==0){
System.out.println("没有元素");
return;
}
List<Node> list=toList(s);
while (list.size()>1){
Collections.sort(list);
Node node = list.get(0);
Node node1=list.get(1);
//中间节点值为null,便于以后区分节点
Node node2=new Node(null,node.integer+node1.integer);
node2.left=node;
node2.right=node1;
list.remove(node);
list.remove(node1);
list.add(node2);
}
huffmanTreeRoot=list.get(0);
}
//将string数据转成Node存放在List中
private static List<Node> toList(String s){
byte[] bytes = s.getBytes();
Map<Byte,Integer> map= new HashMap<>();
for (byte aByte : bytes) {
map.merge(aByte, 1, Integer::sum);
}
List<Node> list=new ArrayList<>();
for (Entry<Byte, Integer> byteIntegerEntry : map.entrySet()) {
list.add(new Node(byteIntegerEntry.getKey(),byteIntegerEntry.getValue()));
}
return list;
}
}
class Node implements Comparable<Node>{
Integer integer;
Byte aByte;
Node left;
Node right;
public Node( Byte aByte,Integer integer) {
this.integer = integer;
this.aByte=aByte;
}
@Override
public String toString() {
Byte[] bytes=new Byte[]{aByte};
return "Node{" +
" aByte=" + Arrays.toString(bytes) +
", integer=" + integer +
'}';
}
@Override
public int compareTo(Node o) {
return Integer.compare(this.integer,o.integer);
}
}
无向图
深度优先
class DeepSearch{
private boolean[] marked;
private Integer[] path;
private Integer start;
int count;//连通分量值
public DeepSearch(Graph graph,Integer start){
marked=new boolean[graph.V()];
this.start=start;
for(int s=0;s<graph.V();s++){
if(!marked((Integer)s)){
dfs(graph,start);
count++;
}
}
}
//深度优先
private void dfs(Graph graph,Integer start) {
marked[start]=true;
for (Object o : graph.adj(start))
if (!marked[(Integer)o]){
path[(Integer)o]=start;//下一步存储上一部路径
dfs(graph,(Integer) o);
}
}
//返回start到end的路径过程
public Stack pathTo(Integer end){
if (!marked[end])return null;
Stack<Object> pathStack = new Stack<>();
for (Integer x=end;!x.equals(this.start);x= path[x])
pathStack.add(x);
pathStack.add(this.start);
return pathStack;
}
}
class Graph{
private final int V;
private int E;
private ArrayList<Integer>[] adj;//邻接表
public Graph(int num){
this.V=num;
adj= new ArrayList[num];
}
public int V(){
return V;
}
public int E(){
return E;
}
public void addEdge(Integer v,Integer w){
adj[v].add(w);
adj[w].add(v);
this.E++;
}
public ArrayList adj(Integer w){
return adj[w];
}
}
深度优先有无环判断
class CircleDeepSearch {
private boolean[] marked;
private boolean hasCircle;
public CircleDeepSearch(Graph graph){
marked=new boolean[graph.V()];
// 注意每次循环的marked不用重置,因为只要在子图总存在环,第一次进入后必定已判断
// 该循环的目的是防止一个图中存在非连通图即多个子图,对于连通图没有任何作用
for (int i=0;i<graph.V();i++)
if(!marked[i])
dfs(graph,i,i);
}
/**
* @param graph 图名称
* @param start 以该节点进行为基准点进行深度优先
* @param last 记录start节点从哪一个节点开始,用于判断在start为基准时遍历下一个节点不能因为是上一个节点而判断有环
*/
private void dfs(Graph graph,Integer start,Integer last) {
marked[start]=true;
for (Object o:graph.adj(start)){
if (!marked[(Integer)o]) dfs(graph,(Integer) o,start);
else if (!o.equals(last)) hasCircle=true;
}
}
public boolean hasCircle(){
return this.hasCircle;
}
}
class Graph{
private final int V;
private int E;
private ArrayList<Integer>[] adj;//邻接表
public Graph(int num){
this.V=num;
adj= new ArrayList[num];
}
public int V(){
return V;
}
public int E(){
return E;
}
public void addEdge(Integer v,Integer w){
adj[v].add(w);
adj[w].add(v);
this.E++;
}
public ArrayList adj(Integer w){
return adj[w];
}
}
深度优先双色问题
class TwoColorDeepSearch {
private boolean[] marked;
private boolean[] color;
private boolean isTwoColor=true;//有环情况下才可能出现不为双色图情况
public TwoColorDeepSearch(Graph graph){
marked=new boolean[graph.V()];
color=new boolean[graph.V()];
for (int i=0;i<graph.V();i++){
if (!marked[i])
dfs(graph,i);
}
}
private void dfs(Graph graph,Integer tail) {
marked[tail]=true;
for (Object o:graph.adj(tail)){
if (!marked[(Integer)o]) {
color[(Integer)o]=!color[tail];
dfs(graph,(Integer) o);
}
else if (color[(Integer)o]==color[tail]) isTwoColor=false;
//如果该节点已经被访问(说明有环)并且颜色和tail相同那么说明此时图的连接点不能满足
}
}
}
class Graph{
private final int V;
private int E;
private ArrayList<Integer>[] adj;//邻接表
public Graph(int num){
this.V=num;
adj= new ArrayList[num];
for (int v=0;v<V;v++)
adj[v]=new ArrayList();
}
public int V(){
return V;
}
public int E(){
return E;
}
public void addEdge(Integer v,Integer w){
adj[v].add(w);
adj[w].add(v);
this.E++;
}
public ArrayList adj(Integer w){
return adj[w];
}
}
广度优先
package Test;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.Stack;
public class Test {
public static void main(String[] args) {
}
}
class DeepSearch{
private boolean[] marked;
private Integer[] path;
private final Integer start;
public DeepSearch(Graph graph,Integer start){
marked=new boolean[graph.V()];
this.start=start;
bfs(graph,start);
}
//广度优先
private void bfs(Graph graph,Integer start) {
Queue<Integer> deque= new ArrayDeque<>();
marked[start]=true;
deque.add(start);
while (!deque.isEmpty()){
Integer v=deque.poll();
for (Object w:graph.adj(v)){
if (!marked[(Integer)w]){
marked[(Integer)w]=true;//标记
deque.add((Integer)w);
path[(Integer)w]=v;//记录路径
}
}
}
}
//返回start到end的路径过程
public Stack pathTo(Integer end){
if (!marked[end])return null;
Stack<Object> pathStack = new Stack<>();
for (Integer x=end;!x.equals(start);x= path[x])
pathStack.add(x);
pathStack.add(this.start);
return pathStack;
}
}
class Graph{
private final int V;
private int E;
private ArrayList<Integer>[] adj;//邻接表
public Graph(int num){
this.V=num;
adj= new ArrayList[num];
for (int v=0;v<V;v++)
adj[v]=new ArrayList();
}
public int V(){
return V;
}
public int E(){
return E;
}
public void addEdge(Integer v,Integer w){
adj[v].add(w);
adj[w].add(v);
this.E++;
}
public ArrayList adj(Integer w){
return adj[w];
}
}
有向图
寻找有向环
class DirectedCycle {
private boolean[] marked;
//记录路径
private Integer[] path;
//记录当前经过的路径上的节点
private boolean[] thisPath;
Stack<Integer> circle;
public DirectedCycle(Graph graph){
marked=new boolean[graph.V()];
for (int i=0;i<graph.V();i++){
if (!marked[i])
dfs(graph,i);
}
}
private void dfs(Graph graph,Integer tail) {
marked[tail]=true;
//标记此时从起始节点到该节点要经过的节点,用来记录是否在该路径上存在所需节点
thisPath[tail]=true;
for (Object o:graph.adj(tail)){
if (!marked[(Integer)o]) {
path[(Integer) o]=tail;//记录路径
dfs(graph,(Integer) o);
}else if (thisPath[(Integer)o]){
circle=new Stack<>();
//存放环路径节点
for (Integer x=tail;!x.equals(o);x=path[x])
circle.add(x);
circle.add((Integer) o);
circle.add(tail);
return;
}
}
//若程序运行到这里表示经过tail节点的后续路径上未发现可以形成环的节点
thisPath[tail]=false;
}
}
class Graph{
private final int V;
private int E;
private ArrayList<Integer>[] adj;//邻接表
public Graph(int num){
this.V=num;
adj= new ArrayList[num];
for (int v=0;v<V;v++)
adj[v]=new ArrayList();
}
public int V(){
return V;
}
public int E(){
return E;
}
public void addEdge(Integer v,Integer w){
adj[v].add(w);
this.E++;
}
public ArrayList adj(Integer w){
return adj[w];
}
//找到指向节点的边
public Graph reverse(){
Graph graph = new Graph(this.V);
for (int v=0;v<this.V;v++)
for (Object w:adj(v))
graph.addEdge((Integer) w,v);
return graph;
}
}
顶点排序和拓扑排序
拓扑排序前提是无有向环
解决调度问题
class DirectedCycle {
private boolean[] marked;
private Integer[] path;
Stack<Integer> circle;
Queue<Integer> pre;//前序
Queue<Integer> post;//后续
Stack<Integer> resPost;//逆后续,拓扑排序
public DirectedCycle(Graph graph){
marked=new boolean[graph.V()];
pre=new ArrayDeque<>();
post=new ArrayDeque<>();
resPost=new Stack<>();
for (int i=0;i<graph.V();i++){
if (!marked[i])
dfs(graph,i);
}
}
private void dfs(Graph graph,Integer tail) {
pre.add(tail);
marked[tail]=true;
for (Object o:graph.adj(tail)){
if (!marked[(Integer)o]) {
path[(Integer) o]=tail;
dfs(graph,(Integer) o);
}
}
post.add(tail);
resPost.add(tail);
}
}
class Graph{
private final int V;
private int E;
private ArrayList<Integer>[] adj;//邻接表
public Graph(int num){
this.V=num;
adj= new ArrayList[num];
for (int v=0;v<V;v++)
adj[v]=new ArrayList();
}
public int V(){
return V;
}
public int E(){
return E;
}
public void addEdge(Integer v,Integer w){
adj[v].add(w);
this.E++;
}
public ArrayList adj(Integer w){
return adj[w];
}
//找到指向节点的边
public Graph reverse(){
Graph graph = new Graph(this.V);
for (int v=0;v<this.V;v++)
for (Object w:adj(v))
graph.addEdge((Integer) w,v);
return graph;
}
}
计算有向图的强连通性
class KoarajuSCC {
private boolean[] marked;
private int[] id;
int count;
public KoarajuSCC(EdgeWeightDigraph G) {
marked = new boolean[G.V()];
id = new int[G.V()];
//获取该图的深度搜索的逆后序遍历,此时被指向的强连通分量定在栈前端,遍历时不能够进入其他强联通分量。
Stack stack = new Post(G).getStack();
for (int w : (Stack<Integer>) stack)
if (!marked[w]){
dfs(G, w);
count++;
}
}
private void dfs(EdgeWeightDigraph g, int w) {
marked[w] = true;
id[w] = count;
for (int v : (ArrayList<Integer>) g.adj(w))
if (!marked[v])
dfs(g, v);
}
class Post {
private Stack<Integer> stack;
private boolean[] isVisited;
private Post(EdgeWeightDigraph g) {
stack = new Stack();
isVisited = new boolean[g.V()];
for (int i = 0; i < g.V(); i++)
if (!marked[i])
post(g, i);
}
private void post(EdgeWeightDigraph digraph, int v) {
isVisited[v] = true;
for (int w : (ArrayList<Integer>) digraph.adj(v)) {
if (!isVisited[w]) {
post(digraph, w);
}
}
stack.add(v);
}
private Stack<Integer> getStack() {
return this.stack;
}
}
}
最小生成树(有向无向基本相同)
加权无向和带权边的数据类型(邻接表)
class Edge {
private final int v;
private final int w;
private final double weight;
public Edge(int v, int w, double weight) {
this.v = v;
this.w = w;
this.weight = weight;
}
public double getWeight() {
return this.weight;
}
public int either() {
return v;
}
public int other(int s) {
return s == v ? w : v;
}
public int compareTo(Edge edge) {
return Double.compare(this.weight, edge.weight);
}
@Override
public String toString() {
return "Edge{" +
"v=" + v +
", w=" + w +
", weight=" + weight +
'}';
}
}
class EdgeWeightGraph {
private final int V;
private int E;
private ArrayList[] adj;//邻接表
public EdgeWeightGraph(int num) {
this.V = num;
adj = new ArrayList[num];
for (int v = 0; v < V; v++)
adj[v] = new ArrayList();
}
public int V() {
return V;
}
public int E() {
return E;
}
public void addEdge(Edge edge) {
int v = edge.either();
int w = edge.other(v);
adj[v].add(edge);
adj[w].add(edge);
this.E++;
}
public ArrayList adj(Integer w) {
return adj[w];
}
public ArrayList<Edge> edges(){
ArrayList<Edge> list=new ArrayList<>();
for (int i=0;i<this.V;i++)
for (Edge e:(ArrayList<Edge>)adj(i))
if (e.other(i)>i)
list.add(e);
return list;
}
}
Prim邻接矩阵
import java.util.Arrays;
import java.util.List;
public class twenty_four_Prim {
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C", "D", "E");
PrimGrid primGrid = new PrimGrid(list);
primGrid.addGrid("A","C",9);
primGrid.addGrid("A","B",9);
primGrid.addGrid("A","E",3);
primGrid.addGrid("D","B",4);
primGrid.addGrid("D","E",9);
primGrid.addGrid("D","C",5);
primGrid.addGrid("E","C",9);
primGrid.addGrid("E","B",2);
try {
Prim.prim(primGrid,"A");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Prim{
public static void prim(PrimGrid primGrid,String name){
boolean[] visited=new boolean[primGrid.num];
int index = primGrid.name.indexOf(name);
if(index==-1){
throw new RuntimeException("Name Error");
}
visited[index]=true;
int index1=-1;
int index2=-1;
int minWeight= 100;
//便利次数
for(int i=1;i<primGrid.num;i++){
//遍历所有节点
for(int j=0;j<primGrid.num;j++){
for (int k=0;k<primGrid.num;k++){
if(visited[j]&&!visited[k]&&primGrid.weight[j][k]!=0&&primGrid.weight[j][k]<minWeight){
minWeight=primGrid.weight[j][k];
index1=j;
index2=k;
}
}
}
System.out.println(primGrid.name.get(index1)+">>>"+primGrid.name.get(index2));
visited[index2]=true;
minWeight= 100;
}
show(primGrid);
}
public static void show(PrimGrid primGrid){
for (int[] ints : primGrid.weight) {
System.out.println(Arrays.toString(ints));
}
}
}
class PrimGrid{
int num;
List<String>name;
int[][] weight;
public PrimGrid(List<String> name) {
this.num = name.size();
this.name = name;
weight=new int[num][num];
}
public void addGrid(String name1,String name2,int w){
int index1 = name.indexOf(name1);
int index2 = name.indexOf(name2);
weight[index1][index2]=w;
weight[index2][index1]=w;
}
}
Prim邻接表的延时实现
class LazyPrimMST{
private boolean[] marked;
//获取在生成最小生成树时存路径
Queue<Edge> path;
//存放最小生成树中节点能够直接到达图的节点的边信息
private MinPQ pq;
public LazyPrimMST(EdgeWeightGraph G){
this.marked=new boolean[G.V()];
this.path=new ArrayDeque<Edge>();
pq=new MinPQ();
visit(G,0);
while (!pq.isEmpty()){
Edge e=pq.delMin();//删除最小权重的边
int v=e.either();
int w=e.other(v);
if (marked[v]&&marked[w])continue;//都在最小生成树中那么仅仅删除该节点即可
path.add(e);
if (!marked[v])visit(G,v);//v不在最小生成树,w在最小生成树中
if (!marked[w])visit(G,w);//w不在最小生成树,v在最小生成树中
}
}
private void visit(EdgeWeightGraph G,int v){
marked[v]=true;
for (Edge e:(ArrayList<Edge>) G.adj(v))
if (!marked[e.other(v)])
pq.insert(e);
}
class MinPQ{
ArrayList list;
private MinPQ(){
list=new ArrayList();
}
private boolean isEmpty(){
return list.isEmpty();
}
private void insert(Edge e){
list.add(e);
}
private Edge delMin(){
int index=0;
for (int i=1;i<list.size();i++)
if (((Edge)list.get(index)).getWeight()>((Edge)list.get(i)).getWeight())
index=i;
return (Edge) list.remove(index);
}
}
}
Prim邻接表的即时实现(和Dijkstra思想相似)
class PrimMST {
//是否在最小生成树之中
private boolean[] marked;
//记录每个节点和其他节点的连接
Edge[] edgeTo;
//节点距离最小生成树最短距离,是动态变化的。
private double[] distTo;
//记录未在最小生成树上的的节点,要找到距离最小生成树最小的节点
private MinPQ pq;
public PrimMST(EdgeWeightGraph G) {
this.marked = new boolean[G.V()];
edgeTo = new Edge[G.V()];
distTo = new double[G.V()];
pq = new MinPQ();
//初始化最大值
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
//初始节点距离初始时距离最小生成树的距离为零
distTo[0] = 0.0;
pq.insert(0);
while (!pq.isEmpty())
visit(G, pq.delMin());//删除最小节点,并根据该节点对为进入最小生成树的节点进行最短距离更新
}
private void visit(EdgeWeightGraph g, int v) {
marked[v] = true;
//遍历有联系的节点
for (Edge e : (ArrayList<Edge>) g.adj(v)) {
int w = e.other(v);
if (marked[w]) continue;
if (e.getWeight() < distTo[w]) {//更新附近最短距离
edgeTo[w] = e;
distTo[w] = e.getWeight();
pq.insert(w);
}
}
}
class MinPQ {
ArrayList list;
private MinPQ() {
list = new ArrayList();
}
private boolean isEmpty() {
return list.isEmpty();
}
private void insert(int v) {//存在就不会再次插入
if (!list.contains(v)) list.add(v);
}
private int delMin() {
int index = 0;
for (int i = 1; i < list.size(); i++)
if (distTo[index] > distTo[i])
index = i;
return (int) list.remove(index);
}
}
}
Kruskal邻接矩阵
import java.util.*;
public class twenty_five_Kruskal {
public static void main(String[] args) {
KruskalGrid kruskalGrid = new KruskalGrid(Arrays.asList("A", "B", "C", "D", "E","F"));
kruskalGrid.addGrid("A","C",9);
kruskalGrid.addGrid("A","B",9);
kruskalGrid.addGrid("A","E",3);
kruskalGrid.addGrid("D","B",4);
kruskalGrid.addGrid("D","E",9);
kruskalGrid.addGrid("D","C",5);
kruskalGrid.addGrid("E","C",9);
kruskalGrid.addGrid("E","B",2);
kruskalGrid.addGrid("F","C",5);
List<EdgeObject> kruskal = Kruskal.kruskal(kruskalGrid);
System.out.println(kruskal);
}
}
class Kruskal{
//返回的路径列表
private static List<EdgeObject> listOfEnd=new ArrayList<>();
//传入KruskalGrid返回该算法list结果
public static List<EdgeObject> kruskal(KruskalGrid kruskalGrid){
//转成EdgeObject类对象
List<EdgeObject> list=new ArrayList<>();
for (int i = 0; i < kruskalGrid.num; i++) {
for(int j=i+1;j<kruskalGrid.num;j++){//一半相当于有向图处理
if(kruskalGrid.weight[i][j]!=0){
list.add(new EdgeObject(kruskalGrid.name.get(i),kruskalGrid.name.get(j),kruskalGrid.weight[i][j]));
}
}
}
//按照weight排序
list.sort(Comparator.comparingInt(o -> o.weight));
//创建存储节点的终点数组,索引代表某个节点,则索引下的值代表该节点能链接的下一个节点
int[] ends = new int[list.size()];
Arrays.fill(ends,-1);
//记录个数,在达到节点-1时即可结束循环
int count=0;
for (EdgeObject edgeObject : list) {
//取出节点名称在KruskalGrid中对应的下标,根据该下标唯一确定节点对应的位置
// 实际上这个也可以重新自己定义,不过在创建图时已经有现成的了,所以就直接调用了
int p1 = kruskalGrid.indexOfName(edgeObject.from);
int p2 = kruskalGrid.indexOfName(edgeObject.to);
//获取终点值
int end1 = getEnd(p1, ends);
int end2 = getEnd(p2, ends);
if (end1 != end2) {
ends[end2] = end1;//ends[end1]若是第一次进行存为零,作为根,用于判断是否连通
listOfEnd.add(edgeObject);
if(++count==kruskalGrid.num-1){
break;
}
}
}
return listOfEnd;
}
//对于优先进入ends的节点元素,都会直接或间接的连接上后来的元素
// 对于终点节点坐标会在一直变动,一直为该图中联通的子图中进来的最后一个
// 另外在进行Kruskal算法的过程中,不一定一直进存在一个连通子图,但是最后的结果一定是一个连通的
//ends[i]表示第i个节点对应的上一个连接节点
private static int getEnd(int i,int[]ends){
//若不为-1.则表示能连接到ends[i]对应值的节点,
while (ends[i]!=-1){
i=ends[i];
}
return i;
}
}
//创建一个类用于直接获取其实节点和权值,有利于直接对其排序和数据获取
class EdgeObject{
String from;
String to;
int weight;
public EdgeObject(String from, String to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
@Override
public String toString() {
return "EdgeObject{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", weight=" + weight +
'}';
}
}
class KruskalGrid {
//节点数量
int num;
//节点名称
List<String> name;
//各个节点之间的权重
int[][] weight;
public KruskalGrid(List<String> name) {
this.num = name.size();
this.name = name;
weight = new int[num][num];
}
//获取名称对应的索引
public int indexOfName(String n){
return this.name.indexOf(n);
}
//添加路线,在后来每一个生成一个EdgeObject对象
public void addGrid(String name1, String name2, int w) {
int index1 = name.indexOf(name1);
int index2 = name.indexOf(name2);
weight[index1][index2] = w;
weight[index2][index1] = w;
}
}
Kruskal邻接表
class Kruskal {
//连通节点的根值,通过该值可以判断每个节点的走向路径
int[] ends;
//查看的节点路径,不必要
ArrayList path;
public Kruskal(EdgeWeightGraph G) {
ends=new int[G.V()];
Arrays.fill(ends,-1);
// 获取所有边的信息,注意该方法只返回了一半
// 并且返回的边中either()对应的值必定比other()小
// 保证ends数组的索引具有一定的方向
ArrayList<Edge> list=G.edges();
path =new ArrayList();
list.sort(Comparator.comparingDouble(o->o.getWeight()));
int edgeNum=0;
for (Edge e:list){
int v=e.either();
int w=e.other(v);
int endV=getEnd(v);
int endW=getEnd(w);
if (endV==endW)continue;//已经相连就跳过
ends[w]=v;//这里写的时都指向小的,反过来也可以
path.add(e);//记录边仅仅便于查看
if (++edgeNum==G.V()-1)break;//退出语句为提高效率,没有其他任何意义
}
}
private int getEnd(int i){
//若不为-1.则表示能连接到ends[i]对应值的节点,
while (ends[i]!=-1){
i=ends[i];
}
return i;
}
}
图的最短路径
加权有向图和边数据类型(邻接表)
class DirectedEdge {
private final int v;
private final int w;
private final double weight;
public DirectedEdge(int v, int w, double weight) {
this.v = v;
this.w = w;
this.weight = weight;
}
public double getWeight() {
return this.weight;
}
public int from() {
return v;
}
public int to() {
return w;
}
@Override
public String toString() {
return "DirectedEdge{" +
"v=" + v +
", w=" + w +
", weight=" + weight +
'}';
}
}
class EdgeWeightDigraph {
private final int V;
private int E;
private ArrayList[] adj;//邻接表
public EdgeWeightDigraph(int num) {
this.V = num;
adj = new ArrayList[num];
for (int v = 0; v < V; v++)
adj[v] = new ArrayList();
}
public int V() {
return V;
}
public int E() {
return E;
}
public void addEdge(DirectedEdge edge) {
adj(edge.from()).add(edge);
E++;
}
public ArrayList adj(Integer w) {
return adj[w];
}
public ArrayList<DirectedEdge> edges(){
ArrayList<DirectedEdge> list=new ArrayList<>();
for (int i=0;i<this.V;i++)
for (DirectedEdge e:(ArrayList<DirectedEdge>)adj(i))
list.add(e);
return list;
}
}
Dijkstra(邻接矩阵)
package Test;
import java.util.*;
public class Test {
public static void main(String[] args) {
DijkstraGrid dijkstraGrid = new DijkstraGrid(Arrays.asList("A", "B", "C", "D", "E","F"));
dijkstraGrid.addEdge("A","C",9);
dijkstraGrid.addEdge("A","B",9);
dijkstraGrid.addEdge("A","E",3);
dijkstraGrid.addEdge("D","B",4);
dijkstraGrid.addEdge("D","E",9);
dijkstraGrid.addEdge("D","C",5);
dijkstraGrid.addEdge("E","C",9);
dijkstraGrid.addEdge("E","B",2);
dijkstraGrid.addEdge("F","C",5);
System.out.println(Dijkstra.dijkstra(dijkstraGrid, "A", "D"));
}
}
class Dijkstra{
//存放距离
private static int[] dist;
//存放最短路径中每个节点的最优路径上的上一个节点的下标
private static int[] path;
//是都被存放过
private static boolean[] isVisited;
public static int dijkstra(DijkstraGrid grid,String from,String to){
dist=new int[grid.num];
path=new int[grid.num];
isVisited=new boolean[grid.num];
//起始位置的下标
int startIndex=grid.name.indexOf(from);
//关于起始路径和距离
for (int i=0;i<grid.num;i++){
dist[i]=grid.weight[startIndex][i];
//若不直接联通,就设为-1,直接连通就设为下标,本身设为-1
path[i]=dist[i]==Integer.MAX_VALUE||dist[i]==0?-1:startIndex;
}
//标记访问
isVisited[startIndex]=true;
while (true){
int index=-1;
//查找出没有访问节点中的距离起始节点最短的节点取出,下标为index
//为了保证访问新节点时更新直接和节点链接的值带来影响不会对间接相连的值带来影响
for (int i = 0; i < grid.num; i++) {
if (!isVisited[i]&&(index==-1||dist[index]>dist[i])){
index=i;
}
}
if(index==-1){
break;
}
isVisited[index]=true;
//根据新访问的节点进行距离和路径的更新
for (int i=0;i<grid.num;i++){
//距离不能为新加入节点本身也不影响其他节点
// (这里包括所有与之相连的节点,有已经遍历的和为便利的,因为之前遍历的节点可能在这里是最短路径)
if(grid.weight[index][i]!=0&&grid.weight[index][i]!=Integer.MAX_VALUE){
//如果更新的节点通过更新节点距离比较近就改变距离和路径
//与之相连的借点 + 相连的借点到minLeaf距离 < 之前相连节点已存在的最短距离
//因为这时候多出了一条可以经过minLeaf的路径
if(dist[index]+grid.weight[index][i]<dist[i]){
path[i]=index;
dist[i]=dist[index]+grid.weight[index][i];
}
}
}
}
//获取路径
int p=grid.name.indexOf(to);
System.out.print(to+"-->");
while (path[p]!=-1){
System.out.print(grid.name.get(path[p])+"-->");
p=path[p];
}
//返回距离
return dist[grid.name.indexOf(to)];
}
}
class DijkstraGrid {
//节点数量
int num;
//节点名称
List<String> name;
//各个节点之间的权重
int[][] weight;
public DijkstraGrid(List<String> name) {
this.num = name.size();
this.name = name;
weight = new int[num][num];
for (int[] ints : weight) {
Arrays.fill(ints, Integer.MAX_VALUE);
}
for(int i=0;i<num;i++){
weight[i][i]=0;
}
}
//添加路线,在后来每一个生成一个EdgeObject对象
public void addEdge(String name1, String name2, int w) {
int index1 = name.indexOf(name1);
int index2 = name.indexOf(name2);
weight[index1][index2] = w;
weight[index2][index1] = w;
}
}
Dijkstra(邻接表)
class Dijkstra{
private DirectedEdge[] edgeTo;//每个节点与边相连的关系
private double[] distTo;//每个节点和起始节点的最短距离
private MinPQ pq;//未处理的节点或因处理过但又有了新的最短路径节点
public Dijkstra(EdgeWeightDigraph G,int start){
edgeTo=new DirectedEdge[G.V()];
distTo=new double[G.V()];
pq=new MinPQ();
Arrays.fill(distTo,Double.POSITIVE_INFINITY);
distTo[start]=0.0;
pq.insert(start);//添加起始节点
while (!pq.isEmpty())
relax(G,pq.delMin());//删除最小值并进行节点松弛
}
private void relax(EdgeWeightDigraph G, int delMin) {
for (DirectedEdge e:(ArrayList<DirectedEdge>)G.adj(delMin)){
int to=e.to();
if (distTo[to]>distTo[delMin]+e.getWeight()){
distTo[to]=distTo[delMin]+e.getWeight();
edgeTo[to]=e;
// 若节点在pq中说明还没有对to节点相邻节点更新所以不用添加
// 若没有说明已经更新或未到达,因为路径改变了所以要添加进入
pq.insert(to);
}
}
}
public double pathTo(int to){
return distTo[to];
}
class MinPQ {
ArrayList list;
private MinPQ() {
list = new ArrayList();
}
private boolean isEmpty() {
return list.isEmpty();
}
private void insert(int v) {//存在就不会再次插入
if (!list.contains(v)) list.add(v);
}
private int delMin() {
int index = 0;
for (int i = 1; i < list.size(); i++)
if (distTo[index] > distTo[i])
index = i;
return (int) list.remove(index);
}
}
}
基于Dijkstra思想处理无环加权有向图最短径(不是真的Dijkstra)
- 按照拓扑排序方式进行顶点松弛
不能处理有环图(反例):
无环图通过拓扑排序进行节点松弛,松弛后就是最短路径
线性复杂的能处理负环
class AcyclicSP {
private DirectedEdge[] edgeTo;//记录前驱结点,边的信息
private double[] distTo;//每个节点距离起始节点的距离
private Stack<Integer> order;//记录拓扑排序节点
private boolean[] marked;//用于深度优先时,节点是否被访问过
public AcyclicSP(EdgeWeightDigraph G, int start){
edgeTo=new DirectedEdge[G.V()];
distTo=new double[G.V()];
order=new Stack();
marked=new boolean[G.V()];
//填充距离为正无穷
Arrays.fill(distTo,Double.POSITIVE_INFINITY);
distTo[start]=0.0;//初始节点变成距离0
dfs(G,start);//进行深度搜索将节点拓扑排序放入栈中
for (int i=0;i<order.size();i++){
relax(G,order.pop());//按照拓扑排序进行节点遍历松弛
}
}
private void dfs(EdgeWeightDigraph G,int v){
marked[v]=true;
for (DirectedEdge edge :(ArrayList<DirectedEdge>) G.adj(v))
if (!marked[edge.to()])
dfs(G,edge.to());
order.add(v);
}
private void relax(EdgeWeightDigraph G, int delMin) {
for (DirectedEdge e:(ArrayList<DirectedEdge>)G.adj(delMin)){
int to=e.to();
if (distTo[to]>distTo[delMin]+e.getWeight()){
distTo[to]=distTo[delMin]+e.getWeight();
edgeTo[to]=e;
}
}
}
public double pathTo(int to){
return distTo[to];
}
}
Floyd(邻接矩阵)
class Floyd{
public static void floyd(FloydGrid floydGrid){
//实际上在运行中不仅仅是寻找最小路径的关系,若两个点之间不能直接联通,那么依赖于这两个点的其他点也不能找到最短路径
//思考:若A-->B未连通,C-->D依赖于A-->B才能到达最小距离,若在A-->B通过中间节点连通前运行了C-->D
// 但是此时因为A-->B未有结果,所以C-->D也未能完成最小距离的获取。那么我们又是如何找到C-->D的呢?
// 例如C-->A-->D-->B和C-->D-->A-->B,在C到B时要分别通过中间节点进行最短路径的选取。
// 这时就要比较中间节点的先后顺序,C会根据中间节点先后作为中间节点的顺序选择不同的中间节点
for(int m=0;m<floydGrid.num;m++){//中间节点的循环要放在最外边,两边的无顺序
for (int i=0;i<floydGrid.num;i++){
for(int j=0;j<floydGrid.num;j++){
//如果利用中间节点的短就交换
if(floydGrid.dist[i][m]+floydGrid.dist[m][j]<floydGrid.dist[i][j]){
floydGrid.dist[i][j]=floydGrid.dist[i][m]+floydGrid.dist[m][j];
//并记录节点
floydGrid.med[i][j]=floydGrid.med[m][j];
}
}
}
}
//打印各个节点结果
for (int i=0;i<floydGrid.num;i++){
for (int j=i+1;j<floydGrid.num;j++){
System.out.print(floydGrid.name.get(i)+" through "+floydGrid.med[j][i]+" to "+floydGrid.name.get(j)+" is "+floydGrid.dist[i][j]+" \t");
}
System.out.println();
}
}
}
class FloydGrid{
//节点名
List<String> name;
//节点数量
int num;
//节点之间的路径长度
int[][] dist;
//各个节点的最短路径的中间节点
String[][] med;
public FloydGrid(List<String> name) {
this.num = name.size();
this.name = name;
dist = new int[num][num];
med=new String[num][num];
for (int[] ints : dist) {
Arrays.fill(ints, 100);//不能初始化成Integer.MAX_VALUE,因为会相加
}
for(int i=0;i<num;i++){
dist[i][i]=0;
}
for(int i=0;i<num;i++){
Arrays.fill(med[i],this.name.get(i));
}
}
//添加路线,在后来每一个生成一个EdgeObject对象
public void addEdge(String name1, String name2, int w) {
int index1 = name.indexOf(name1);
int index2 = name.indexOf(name2);
dist[index1][index2] = w;
dist[index2][index1] = w;
}
}
Floyd(邻接表)
字符串
低位优先的字符串排序
class LSD{
//s中数据一定是相同长度的
public static void sort(String[] s){
int N=s.length;
int R=256;
int W=s[0].length();
String[] aux=new String[N];
for (int d=W-1;d>=0;d--){//从后向前索引,这种排序为稳定排序
int[] count=new int[R+1];//有256种字符
for (String value : s) count[value.charAt(d) + 1]++;//计算每种字符有多少种
for (int r=0;r<R;r++)count[r+1]+=count[r];//累加变成每种字符的起始索引
for (String value : s) aux[count[value.charAt(d)]++] = value;//按照索引将数据放在aux中
System.arraycopy(aux, 0, s, 0, N);//将数据放回在s中,继续进行下一轮
}
}
}
高位优先的字符串排序
class MSD{
private static int R=256;//数据种类
private static final int M=15;//最小阈值
private static String[] aux;
public static void sort(String[] s) {
int N = s.length;
aux = new String[N];
sort(s, 0, N - 1, 0);
}
private static void sort(String[] s, int lo, int hi, int d) {
if (hi <= lo + M) insertSort(s, lo, hi, d);//防止创建大量小数组,在小数量时转为插入排序
int[] count = new int[R + 2];//有256种字符
for (String value : s) count[charAt(value,d) + 2]++;//计算每种字符有多少种
for (int r = 0; r < R + 1; r++) count[r + 1] += count[r];//累加变成每种字符的起始索引
for (String value : s) aux[count[charAt(value,d)]++] = value;//按照索引将数据放在aux中
if (hi + 1 - lo >= 0) System.arraycopy(aux, lo, s, lo, hi + 1 - lo);//回写
for (int r = 0; r < R; r++) sort(s, lo + count[r], lo + count[r + 1], d + 1);
}
private static void insertSort(String[] s, int lo, int hi, int d) {
for (int i=lo;i<=hi;i++){
for (int j=i;j>lo&&s[j].substring(d).compareTo(s[j-1].substring(d))<0;j--){
String s1=s[j];
s[j]=s[j-1];
s[j-1]=s1;
}
}
}
private static int charAt(String s,int d){
return d<s.length()?s.charAt(d):-1;
}
}
三项字符串快速排序
class Q3{
public static void sort(String[] s) {
sort(s,1,s.length-1,0);
}
private static void sort(String[] s, int lo, int hi, int d) {
if (hi<=lo)return;
int lt=lo,gt=hi;
int v=charAt(s[lo],d);
int i=lo+1;
while (i<gt){
int t=charAt(s[i],d);
if (t<v) exch(s,lt++,gt++);
else if (t>v) exch(s,i,gt--);
else i++;
}
sort(s,lo,lt-1,d);
if (v>=0)sort(s,lt,gt,d+1);
sort(s,gt+1,hi,d);
}
private static void exch(String[] s, int i, int i1) {
String s1=s[i];
s[i]=s[i1];
s[i1]=s1;
}
private static int charAt(String s,int d){
return d<s.length()?s.charAt(d):-1;
}
}
暴力匹配和KMP
import java.util.Arrays;
public class twenty_two_BMKMP {
public static void main(String[] args) {
System.out.println(KMP.kMP("asf3333ascasdfsd", "asdasd"));
System.out.println(Arrays.toString(KMP.generateNext("aaaaaaaaaaaab")));
System.out.println(Arrays.toString(KMP.generateNextValue("aaaaaaaaaaaab")));
}
}
class BM {
public static int pD(String str,String sub){
int i=0;
int j=0;
while (i<str.length()&&j<sub.length()){
if(sub.charAt(j)==str.charAt(i)){
i++;
j++;
}else {
i=i-j+1;
j=0;
}
}
if(j>=sub.length()){
return i-j;
}
return -1;
}
}
class KMP {
public static int kMP(String str, String sub) {
//生成nextValue数组
int[] next = generateNext(sub);
int i = 0;
int j = 0;
//若str循环完毕,或者sub已经在str中出现就退出循环
while (i < str.length() && j < sub.length()) {
//若j==-1意味着直接从头开始匹配,且当前str.charAt(i)必定不与sub.charAt(j)相同
if (j == -1 || str.charAt(i) == sub.charAt(j)) {
i++;
j++;
} else {
//否则就进行指向sub的j回退,i保持原来位置
j = next[j];
}
}
if (j >= sub.length()) {
return i - j;
}
return -1;
}
public static int[] generateNext(String sub) {
int[] next = new int[sub.length()];
//初始化next数组
next[0] = -1;
next[1] = 0;
//从i=2进行next的计算
int i = 2;
int k = 0;
while (i < sub.length()) {
//比较当前比较的元素前一位是否与k指向的元素相同
if (k == -1 || sub.charAt(k) == sub.charAt(i - 1)) {
//若接下来的值相同,next[i]就在 原来的k基础之上+1
next[i] = k + 1;
//继续遍历下一个
i++;
//将k指向下一个元素
k++;
} else {
k = next[k];
}
}
return next;
}
public static int[] generateNextValue(String sub) {
int[] nextValue = new int[sub.length()];
nextValue[0] = -1;
nextValue[1] = 0;
int i = 2;
int k = 0;
while (i < sub.length()) {
if (k == -1 || sub.charAt(k) == sub.charAt(i - 1)) {
nextValue[i] = k + 1;
i++;
k++;
} else {
k = nextValue[k];
}
}
// 在匹配失败时,若回退的下标对应的回退值和原来匹配时的值相同,则此时回退的值必定也不能匹配成功
// 就会再一次进行下一次回退,这里就直接判断在当前值和回退值相同时就直接显示回退的回退值对应的值
for (int j = 1; j < nextValue.length; j++) {
if (sub.charAt(nextValue[j]) == sub.charAt(j)) {
nextValue[j] = nextValue[nextValue[j]];
}
}
return nextValue;
}
}
最长回文串Manacher O(N)
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println(U.maxLength(new String("03234543234543")));//11
}
}
class U {
public static int maxLength(String s) {
if (s == null || s.length() == 0) return 0;
char[] specialArr = arrToSpecialArr(s.toCharArray());//在数组中添加特殊字符
int[] pArr = new int[specialArr.length];//创建数组存放以每个字符为中心的最长回文字符串半径
Arrays.fill(pArr, 1);//回文至少为1
int med = -1;//上一个较长回文串中间的字符索引
int rightOfRightest = -1;//回文字符串所到达的最右端的右端的第一个元素
int maxLen = 0;//存放max回文长度
for (int i = 0; i < specialArr.length; i++) {
if (rightOfRightest > i) {//如果索引为i的字符没有超过所到的最右端
pArr[i] = Math.min(pArr[2 * med - i], rightOfRightest - i);//根据对称字符将最小值记下来
if (i + pArr[i] != rightOfRightest)//如果刚好取到最右端,就需要继续判断扩充,否则:i为中心的串不可能比之前的maxLen长,进行下一轮循环,
continue;
}
//根据第i个字符想两边扩充。要控制左边右边的值,保证不能越界
while (i + pArr[i] < specialArr.length && i - pArr[i] > -1) {
if (specialArr[i + pArr[i]] == specialArr[i - pArr[i]]) {//成功
pArr[i]++;
} else {//失败
break;
}
}
//i+pArr[i]>=rightOfRightest定成立,所以判断不是必要的,更新最值
if (i + pArr[i] >= rightOfRightest) {
rightOfRightest = i + pArr[i];
med = i;
}
//记录半径最大值
maxLen = Math.max(maxLen, pArr[i]);
}
return maxLen - 1;//返回半径最大值-1就是最长回文长度,因为之前已经处理了string,扩充成了原来的2倍+1
}
private static char[] arrToSpecialArr(char[] arr) {
char[] specialArr = new char[arr.length * 2 + 1];
for (int i = 0; i < specialArr.length; i++) {
if (i % 2 == 0) specialArr[i] = '#';
else specialArr[i] = arr[i / 2];
}
return specialArr;
}
}
动态规划
背包问题
import java.util.Arrays;
public class twenty_three_PD {
public static void main(String[] args) {
int[] w={1,4,3};
int[] v={1500,3000,2000};
String[] name={"aa","bb","cc"};
int num=5;
PD pd = new PD(w, v,name, num);
pd.generateBag();
pd.show();
}
}
class PD{
//每一个物品的重量
private int[] eachWeight;
//每一个物品的价值
private int[] eachValue;
//每一个物品名字
private String[] objectName;
//包中元素总价值
private int[][] pDBag;
//pDBag包中价值对应的名字
private String[][] objectBag;
public PD(int[] weight, int[] value, String[]objectName,int bagMaxValue) {
this.eachWeight = weight;
this.eachValue = value;
this.objectName = objectName;
pDBag=new int[value.length+1][bagMaxValue+1];
objectBag=new String[value.length+1][bagMaxValue+1];
for (int i = 0; i <pDBag.length ; i++) {
pDBag[i][0]=0;
}
for(int i=0;i<pDBag[0].length;i++){
pDBag[0][i]=0;
}
}
public void generateBag(){
for(int objectIndex=1;objectIndex<pDBag.length;objectIndex++)
for (int bagNum = 1; bagNum < pDBag[0].length; bagNum++) {
//如果此时物品的重量大于包的最大重量,就继承同一列上一个元素
if (eachWeight[objectIndex - 1] > bagNum) {
//记录价值
pDBag[objectIndex][bagNum] = pDBag[objectIndex - 1][bagNum];
//记录名称
objectBag[objectIndex][bagNum]=objectBag[objectIndex - 1][bagNum];
} else {
//能装下新的物品就判断装下该物品总价值高还是不装高
//装下该物品的同时如果有剩余空间就将剩余空间填满,填充用的是同一行中,剩余空间对应的重量的最大价值
if(pDBag[objectIndex - 1][bagNum] >
eachValue[objectIndex - 1] + pDBag[objectIndex - 1][bagNum - eachWeight[objectIndex - 1]]){
//记录价值和名称
pDBag[objectIndex][bagNum]=pDBag[objectIndex - 1][bagNum];
objectBag[objectIndex][bagNum]=objectBag[objectIndex - 1][bagNum];
}else {
//装下该物品价值高:
//记录价值和名称
pDBag[objectIndex][bagNum]=eachValue[objectIndex - 1] + pDBag[objectIndex - 1][bagNum - eachWeight[objectIndex - 1]];
//记录名称,判断是否为null
if(objectBag[objectIndex - 1][bagNum - eachWeight[objectIndex - 1]]!=null)
objectBag[objectIndex][bagNum]=objectBag[objectIndex - 1][bagNum - eachWeight[objectIndex - 1]]+"+"+objectName[objectIndex - 1];
else
objectBag[objectIndex][bagNum]=objectName[objectIndex - 1];
}
}
}
}
public void show(){
for (int[] ints : this.pDBag) {
System.out.println(Arrays.toString(ints));
}
for (String[] strings : objectBag) {
System.out.println(Arrays.toString(strings));
}
}
}
前缀树
class TrieST<V> {
private static final int R = 256;
private Node root;
// 节点类
private static class Node {
private Object val;
// 下标作为索引
private Node[] next = new Node[R];
}
public V get(String key) {
Node x = get(root, key, 0);
if (x == null) {// 不存在该单词
return null;
}
return (V) x.val;
}
/**
* @param root 此时查找的结点
* @param key 查找的单词
* @param d 查找到了key下标索引
* @return key最后一个值对应的节点
*/
private Node get(Node x, String key, int d) {
if (x == null) {// 不存在该单词
return null;
}
if (d == key.length()) {// 查找到了目标单词
return x;
}
char ch = key.charAt(d);// 下一步
return get(x.next[ch], key, d + 1);
}
public void put(String key, V value) {
root = put(root, key, value, 0);
}
private Node put(Node x, String key, V value, int d) {
if (x == null) {
x = new Node();
}
if (d == key.length()) {// 在单词长度出标记
x.val = value;
}
char ch = key.charAt(d);
// 递归形式连接节点
x.next[ch] = put(x.next[ch], key, value, d + 1);
return x;
}
public void delete(String key) {
root = delete(root, key, 0);
}
private Node delete(Node x, String key, int d) {
if (x == null) {
return null;
}
if (d == key.length()) {
// 删除目标值,就进行置空删除
x.val = null;
} else {
// 继续向下寻找
char ch = key.charAt(d);
x.next[ch] = delete(x.next[ch], key, d + 1);
}
// 如果x.val不为空那么节点的连接并不改变
if (x.val != null) {
return x;
}
// 运行在此处说明x.val为空,不存在该值了,
// 现在存在两种情况
// 1.路径下有节点值,该路进不能删除
// 2.该节点下路径不存在单词了,就需要删除完
for (char ch = 0; ch < R; ch++) {
// 若存在next中一个不为空,就返回该节点,意味着保留这个路径
if (x.next[ch] != null) {// 情况2
return x;
}
}
// 情况1
return null;
}
public int size() {
return size(root);
}
// 递归查找存在的單詞种类数
public int size(Node x) {
if (x == null) {
return 0;
}
int thisNum = 0;
if (x.val == null) {
thisNum++;
}
for (char ch = 0; ch < R; ch++) {
thisNum += size(x.next[ch]);
}
return thisNum;
}
// 返回树中所有单词
public Iterable<String> keys() {
return keysWithPrefix("");
}
private Iterable<String> keysWithPrefix(String pre) {
ArrayDeque<String> q = new ArrayDeque<>();
collect(get(root, pre, 0), pre, q);
return q;
}
private void collect(Node x, String pre, ArrayDeque<String> q) {
if (x == null) {
return;
}
if (x.val != null) {// 存在目标值,不一定是叶子节点
q.add(pre);
}
// 便利查找节点单词
for (char ch = 0; ch < R; ch++) {
collect(x.next[ch], pre + ch, q);
}
}
// 正则通配符 . 匹配项
public Iterable<String> keysThatMatch(String pat) {
ArrayDeque<String> deque = new ArrayDeque<String>();
collect(root, "", pat, deque);
return deque;
}
private void collect(Node x, String pre, String pat, ArrayDeque<String> deque) {
int d = pre.length();// 当前已经组成的长度
if (x == null) {
return;
}
if (d == pat.length() && x.val != null) {
deque.add(pre);// 存在结果,进行收集
}
// 查到该长度没但是没有存在
if (d == pat.length()) {
return;
}
char next = pat.charAt(d);
// 进行匹配查找
for (char ch = 0; ch < R; ++ch) {
if (next == '.' || next == ch) {
collect(x.next[ch], pre + ch, pat, deque);
}
}
}
// 返回s中前缀在树中存在的最长前缀串
public String longestPrefixOf(String s) {
int length = search(root, s, 0, 0);
return s.substring(0, length);
}
/**
* @param x 当前查找结点
* @param s 目标最长串
* @param d 当前走到的s的长度位置记录
* @param length 已经找到的最长前缀串的长度
* @return 返回找到的存在的最长距离
*/
private int search(Node x, String s, int d, int length) {
if (x == null) {
return length;
}
if (x.val != null) {
length = d;
}
if (d == s.length()) {
return length;
}
char ch = s.charAt(d);
return search(x.next[ch], s, d + 1, length);
}
}
并查集
class U {
public static class UnionFindSet<V>{
public HashMap<V, Node<V>> elementMap;
public HashMap<Node<V>,Node<V>> fatherMap;
public HashMap<Node<V>,Integer> sizeMap;
public UnionFindSet(List<V>list){
elementMap=new HashMap<>();
fatherMap=new HashMap<>();
sizeMap=new HashMap<>();
for (V v:list){
Node<V> element=new Node<V>(v);
elementMap.put(v,element);//包装元素
fatherMap.put(element,element);//每个节点父类指向自己
sizeMap.put(element,1);//主根节点节点个数
}
}
private Node<V> findHeader(Node<V> element){
Stack<Node<V>> path = new Stack<>();
while (element!=fatherMap.get(element)){//循环寻找根节点
path.push(element);
element=fatherMap.get(element);
}
//此时element变成了父类根节点
//将路径上所有的元素父类节点指向element父类根节点,这样就将链式结构转换成了比较扁平的数据结构。
while (!path.isEmpty()){
fatherMap.put(path.pop(),element);
}
return element;
}
public boolean isSameSet(V a,V b){
if (elementMap.containsKey(a)&&elementMap.containsKey(b)){
return findHeader(fatherMap.get(a))==findHeader(fatherMap.get(b));
}
return false;
}
public void union(V a,V b){
if (elementMap.containsKey(a)&&elementMap.containsKey(b)){
Node<V> aF=findHeader(elementMap.get(a));
Node<V> bF=findHeader(elementMap.get(b));
if (aF!=bF){//说明还没有结合
Node<V> bigF=sizeMap.get(aF)>=sizeMap.get(bF)?aF:bF;//较大的父根节点
Node<V> smallF=bigF==aF?bF:aF;//较小的父根节点
fatherMap.put(smallF,bigF);//将小的放在大的下面
sizeMap.put(bigF,sizeMap.get(smallF)+sizeMap.get(bigF));//更新父根节点size大小
sizeMap.remove(smallF);//删除小的父类根节点
}
}
}
}
private static class Node<T>{
private T num;
public Node(T num){
this.num=num;
}
}
}
一致性哈希
经典哈西的特点是:增加数据种类那么对于数据的迁移就是全量的。
布隆过滤器
用较少的空间标记数据是否已经存在。
只允许添加数据和查询是否存在,不能删除数据。
有一定失误率:只可能将不存在的数据识别成存在的。
失误率的大小跟数据量,过滤器开辟空间和哈希值的个数相关。
失误率,样本量,开辟空间,哈希函数个数之间的关系。
k增加,在添加数据时也会将大量的位标记成1,会将过滤器大部分的位占满,造成失误率上升