目录
稀疏数组
实际需求
五子棋的存盘和续上盘
当一个数组大部分值为0,或者为同一个值时,可以使用稀疏数组来保存该数组
稀疏数组的处理方式:第一行记录原数组几行几列,有多少个值
把不同的值的行列及值存储再一个小规模的数组中,从而节约资源
举例
代码实现
public class sparsearray {
public static void main(String[] args) {
int array[][] = new int[11][11];
array[1][2]=1;
array[2][3]=2;
System.out.println("原数组");
toPrintln(array);
int[][] sparsearray = toSparsearray(array);
toArray(sparsearray);
}
//遍历数组
public static void toPrintln(int[][] array) {
for (int[] row : array) {
for (int date : row) {
System.out.printf("%d\t",date);
}
System.out.println();
}
}
//将数组转换成稀疏数组
public static int[][] toSparsearray(int[][] array) {
//获取棋子的个数
int sum=0;
for (int[] row : array) {
for (int date : row) {
if (date != 0){
sum++;
}
}
}
System.out.println("sum="+sum);
int[][] sparsearrays=new int[sum+1][3];
sparsearrays[0][0]=array.length;
sparsearrays[0][1]=array[0].length;
sparsearrays[0][2]=sum;
int count=1;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
if (array[i][j] != 0){
sparsearrays[count][0]=i;
sparsearrays[count][1]=j;
sparsearrays[count][2]=array[i][j];
count++;
}
}
}
System.out.println("稀疏数组为");
toPrintln(sparsearrays);
return sparsearrays;
}
//将稀疏数组还原成棋盘
private static void toArray(int[][] sparsearray) {
int array2[][] = new int[sparsearray[0][0]][sparsearray[0][1]];
for (int i = 1; i < sparsearray.length; i++) {
array2[sparsearray[i][0]][sparsearray[i][1]]=sparsearray[i][2];
}
System.out.println("原数组");
toPrintln(array2);
}
}
队列
案例
银行取票
介绍
队列是一个有序列表,可以用数组实现,也可以用链表实现
遵守先入先出的原则
数组模拟队列思路
front指向队列头 rear指向队列尾 maxsize容量
加入元素
rear指向后移 rear+1 rear= front 队列为空 rear=maxsize-1 队列满
import java.util.Scanner;
public class arrayQueueDemo {
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(3);
char key = ' ';//接受用户输入
Scanner scanner =new Scanner(System.in);
boolean bool = true;
//输出一个菜单
while (bool){
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出队列");
System.out.println("a(add):向队列添加数据");
System.out.println("g(get):从队列取数据");
System.out.println("h(head):查看队列头的数据");
key = scanner.next().charAt(0);
switch (key){
case 's':
try {
queue.show();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'a':
System.out.println("请输入一个数");
int value=scanner.nextInt();
try {
queue.add(value);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'g':
try{
int date = queue.getDate();
System.out.println("取出的数据是"+date);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try{
int i = queue.headQueue();
System.out.println("头数据"+i);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
bool = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class ArrayQueue{
private int maxSize; //最大容量
private int rear; //队列尾
private int front; //队列头
private int[] arr;
public ArrayQueue(int arrmaxSize){
maxSize = arrmaxSize;
rear = -1;
front = -1;
arr = new int[maxSize];
}
public boolean isFull(){
return maxSize-1 == rear;
}
public boolean isEmply(){
return front == rear;
}
public void add(int date){
if (isFull()){
throw new RuntimeException("队列满,不能加入数据");
}
rear++;
arr[rear] = date;
}
public int getDate(){
if (isEmply()){
throw new RuntimeException("队列为空,不能取数据");
}
front++;
return arr[front];
}
public void show(){
if (isEmply()){
throw new RuntimeException("队列为空,没有数据");
}
for (int i : arr) {
System.out.println(i);
}
}
//显示队列头数据
public int headQueue(){
if (isEmply()){
throw new RuntimeException("队列为空,没有数据");
}
return arr[front+1];
}
}
问题和优化
目前数组只能使用一次,改进成环形队列
数组模拟环形队列
import java.util.Scanner;
public class CircleArrayQueue {
public static void main(String[] args) {
CircleQueue queue = new CircleQueue(4);
char key = ' ';//接受用户输入
Scanner scanner =new Scanner(System.in);
boolean bool = true;
//输出一个菜单
while (bool){
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出队列");
System.out.println("a(add):向队列添加数据");
System.out.println("g(get):从队列取数据");
System.out.println("h(head):查看队列头的数据");
key = scanner.next().charAt(0);
switch (key){
case 's':
try {
queue.show();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'a':
System.out.println("请输入一个数");
int value=scanner.nextInt();
try {
queue.add(value);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'g':
try{
int date = queue.getDate();
System.out.println("取出的数据是"+date);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try{
int i = queue.headQueue();
System.out.println("头数据"+i);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
bool = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class CircleQueue{
private int maxSize; //最大容量
//调整成指向队列最后一个元素的后一个位置,空一个空间作为约定
private int rear; //队列尾
//调整成指向队列第一个元素
private int front; //队列头
private int[] arr;
public CircleQueue(int arrmaxSize){
maxSize = arrmaxSize;
rear = 0;
front = 0;
arr = new int[maxSize];
}
public boolean isFull(){
return (rear + 1)% maxSize == front;
}
public boolean isEmply(){
return front == rear;
}
public void add(int date){
if (isFull()){
throw new RuntimeException("队列满,不能加入数据");
}
arr[rear] = date;
//保留一格位置,确认队列满
rear = (rear+1) % maxSize;
}
public int getDate(){
if (isEmply()){
throw new RuntimeException("队列为空,不能取数据");
}
// 需要返回arr[front]上的值,需要一个临时变量存储
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
public void show(){
if (isEmply()){
throw new RuntimeException("队列为空,没有数据");
}
for (int i = front; i < front+size(); i++) {
//i%maxSize避免越界
System.out.printf("arr[%d]=%d",i%maxSize,arr[i%maxSize]);
System.out.println();
}
}
//获取有效数据 加上maxsize避免负数
public int size(){
return (rear - front + maxSize) % maxSize;
}
//显示队列头数据
public int headQueue(){
if (isEmply()){
throw new RuntimeException("队列为空,没有数据");
}
return arr[front];
}
}
链表
public class singleLinkedListDemo {
public static void main(String[] args) {
Node node1 = new Node(1,"宋江","及时雨");
Node node2 = new Node(2,"卢俊义","玉麒麟");
Node node3 = new Node(3,"吴用","智多星");
Node node4 = new Node(4,"林冲","豹子头");
LinkedList list =new LinkedList();
// list.add(node1);
// list.add(node2);
// list.add(node3);
// list.add(node4);
list.addByOrder(node1);
list.addByOrder(node4);
list.addByOrder(node3);
list.addByOrder(node2);
Node node5 = new Node(4,"林冲2","豹子头");
list.update(node5);
list.delete(4);
list.show();
}
}
class LinkedList{
public Node head=new Node(0,"","");
//按顺序添加节点 头节点不能动,所有需要temp辅助
public void add(Node node){
if (head.next==null){
head.next=node;
}else {
Node temp=head;
while (true){
if (temp.next==null){
break;
}
temp=temp.next;
}
temp.next=node;
}
}
//通过编号添加
public void addByOrder(Node node){
Node temp = head;
boolean flag = false;
while (true){
if (temp.next == null){
break;
}if (temp.next.no > node.no){
break;
}else if (temp.next.no == node.no){
flag =true;
break;
}
temp=temp.next;
}
if (flag){
System.out.println("编号已存在,请重新确认");
}else {
node.next=temp.next;
temp.next=node;
}
}
//修改节点
public void update(Node node){
if (head.next == null){
System.out.println("链表为空,不能修改");
return;
}
Node temp = head.next;
boolean flag = false;
while (true){
if (temp == null){
break;
}
if (temp.no == node.no){
flag = true;
break;
}
temp=temp.next;
}
if (flag){
temp.name = node.name;
temp.nikename = node.nikename;
}else {
System.out.println("未找到原节点"+node.no);
}
}
//删除节点
public void delete(int no){
if (head.next == null){
System.out.println("链表为空,不能删除");
return;
}
Node temp = head;
boolean flag = false;
while (true){
if (temp.next.no == no){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
temp.next = temp.next.next;
}else {
System.out.println("未找到原节点"+no);
}
}
public void show(){
Node temp = head.next;
while (temp!=null){
System.out.println(temp);
temp=temp.next;
}
}
}
class Node{
public int no;
public String name;
public String nikename;
public Node next;
public Node(int no,String name,String nikename){
this.no=no;
this.name=name;
this.nikename=nikename;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", name='" + name + '\'' +
", nikename='" + nikename + '\'' +
'}';
}
}
单链表的翻转
思路:创建一个新的头节点,遍历原来的单链表,每次遍历插入到下面的新链表中
//单链表的翻转
public static void reverseList(Node head){
if (head.next == null || head.next.next == null){
return;
}
Node reverseHead = new Node(0,"","");
Node temp = head.next;
//保存temp的下一个
Node next = null;
while (temp != null){
next = temp.next;
temp.next = reverseHead.next;
reverseHead.next = temp;
temp=next;
}
head.next=reverseHead.next;
}
单链表的逆序输出(栈)
// 链表从尾到头打印
public static void lastToFirst(Node head){
if (head.next == null){
System.out.println("链表为空,无法打印");
return;
}
Node temp = head.next;
Stack<Node> nodes = new Stack<>();
while (temp != null){
nodes.add(temp);
temp = temp.next;
}
while (nodes.size() > 0){
System.out.println(nodes.pop());
}
}
双向链表
public class doubleLinkedListDemo {
public static void main(String[] args) {
Node2 node1 = new Node2(1,"宋江","及时雨");
Node2 node2 = new Node2(2,"卢俊义","玉麒麟");
Node2 node3 = new Node2(3,"吴用","智多星");
Node2 node4 = new Node2(4,"林冲","豹子头");
DoubleList doubleList = new DoubleList();
// doubleList.add(node1);
// doubleList.add(node2);
// doubleList.add(node3);
// doubleList.add(node4);
System.out.println("按序号加入");
doubleList.addByOrder(node1);
doubleList.addByOrder(node4);
doubleList.addByOrder(node3);
doubleList.addByOrder(node2);
doubleList.show();
Node2 node5 = new Node2(4,"林冲2","豹子头");
System.out.println("修改4号");
doubleList.update(node5);
doubleList.show();
System.out.println("删除4号");
doubleList.delete(4);
doubleList.show();
}
}
class DoubleList{
private Node2 head=new Node2(0,"","");
public Node2 getHead() {
return head;
}
//添加到末尾
public void add(Node2 node2){
if (head.next==null){
head.next=node2;
}else {
Node2 temp=head;
while (true){
if (temp.next==null){
break;
}
temp=temp.next;
}
temp.next=node2;
node2.pre=temp;
}
}
//通过编号添加
public void addByOrder(Node2 node2){
Node2 temp = head;
boolean flag = false;
while (true){
if (temp.next == null){
break;
}if (temp.next.no > node2.no){
break;
}else if (temp.next.no == node2.no){
flag =true;
break;
}
temp=temp.next;
}
if (flag){
System.out.println("编号已存在,请重新确认");
}else {
if (temp.next == null){
temp.next=node2;
node2.pre=temp;
}else{
node2.next = temp.next;
temp.next.pre = node2;
temp.next = node2;
node2.pre = temp;
}
}
}
//修改节点
public void update(Node2 node2){
if (head.next == null){
System.out.println("链表为空,不能修改");
return;
}
Node2 temp = head.next;
boolean flag = false;
while (true){
if (temp == null){
break;
}
if (temp.no == node2.no){
flag = true;
break;
}
temp=temp.next;
}
if (flag){
temp.name = node2.name;
temp.nikename = node2.nikename;
}else {
System.out.println("未找到原节点"+node2.no);
}
}
//删除节点
public void delete(int no){
if (head.next == null){
System.out.println("链表为空,不能删除");
return;
}
Node2 temp = head.next;
boolean flag = false;
while (true){
if (temp.no == no){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
if (temp.next != null){
temp.pre.next = temp.next;
temp.next.pre=temp.pre;
}else {
//当temp.next为空时,temp.next.pre空指针异常
temp.pre.next=null;
temp.pre=null;
}
// temp.pre.next=temp.next;
// if (temp.next != null){
// temp.next.pre = temp.pre;
// }
}else {
System.out.println("未找到原节点"+no);
}
}
public void show(){
Node2 temp = head.next;
while (temp!=null){
System.out.println(temp);
temp=temp.next;
}
}
}
class Node2{
public int no;
public String name;
public String nikename;
public Node2 next;
public Node2 pre;
public Node2(int no,String name,String nikename){
this.no=no;
this.name=name;
this.nikename=nikename;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", name='" + name + '\'' +
", nikename='" + nikename + '\'' +
'}';
}
}
Jasephu 问题
public class josepfu {
public static void main(String[] args) {
CircleSingleLinkedList l = new CircleSingleLinkedList();
l.add(5);
l.show(l.getFirst());
l.countBoy(1,2,5);
}
}
class CircleSingleLinkedList{
private Boy first = null;
public Boy getFirst() {
return first;
}
//添加小孩节点
public void add(int nums){
if (nums < 1){
System.out.println("节点个数错误");
return;
}else {
//辅助指针
Boy curboy = null;
//每次调用都是重新创建链表
for (int i = 1; i <= nums; i++) {
Boy boy = new Boy(i);
if (i == 1){
first = boy;
first.setNext(first);
curboy = boy;
}else {
curboy.setNext(boy);
boy.setNext(first);
curboy = boy;
}
}
}
}
//根据用户的输入,计算小孩的出圈顺序
/**
*
* @param startnums 从第几个开始
* @param countNums 数几下
* @param nums 有几个在圈
*/
public void countBoy(int startnums,int countNums,int nums){
if (first == null || startnums < 1 || countNums > nums){
System.out.println("参数有误");
return;
}
Boy helper = first;
//helper指向最后一个节点 ,因为是单向链表,所以需要辅助指针help帮助节点出队
while (true){
if (helper.getNext() == first){
break;
}
helper = helper.getNext();
}
//移动到开始的节点由first指向
for (int i = 0; i < startnums-1; i++) {
first = first.getNext();
helper = helper.getNext();
}
System.out.println("从"+first.getNo()+"号开始"+"每次数"+countNums+"下");
while (true){
if (first == helper){
//只剩下一个节点
System.out.println("最后一个出圈的节点"+first.getNo());
break;
}
//first 数1,需要countNums - 1 次 到达需要出队的节点上
for (int i = 0; i < countNums-1; i++) {
first = first.getNext();
helper = helper.getNext();
}
//此时first指向出圈的节点
System.out.println("出圈的节点"+first.getNo());
first=first.getNext();
helper.setNext(first);
}
}
public void show(Boy first){
if (first == null){
System.out.println("链表为空,无法遍历");
}
Boy curboy = first;
while (true){
System.out.println(curboy);
if (curboy.getNext() == first){
break;
}
curboy = curboy.getNext();
}
}
}
class Boy{
private int no;
private Boy next;
public Boy(int no){
this.no=no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
@Override
public String toString() {
return "Boy{" +
"no=" + no +
'}';
}
}
栈
数组模拟
//数组实现栈
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(4);
stack.push(14);
stack.push(12);
stack.push(13);
stack.push(16);
while (!stack.isEmply()){
System.out.println(stack.pop());
}
}
}
class ArrayStack{
private int top;
private int size;
private int[] arr;
public ArrayStack(int size){
this.size = size;
arr = new int[size];
top = -1;
}
public boolean isEmply(){
return top == -1;
}
public boolean isFull(){
return top == size - 1;
}
public int getSize(){
int k = 0 ;
for (int i = 0; i < size; i++) {
if (arr[size] != 0){
k++;
}
}
return k;
}
public void push(int n){
if (isFull()){
System.out.println("栈满,无法添加");
}
top++;
arr[top] = n;
}
public int pop(){
if (isEmply()){
System.out.println("栈空,无法弹栈");
}
int value = arr[top];
top--;
return value;
}
}
单链表实现
public class singleListStarkDemo {
public static void main(String[] args) {
Node node1 = new Node(11);
Node node2 = new Node(12);
Node node3 = new Node(13);
Node node4 = new Node(14);
ListStark listStark = new ListStark();
listStark.push(node1);
listStark.push(node2);
listStark.push(node3);
listStark.push(node4);
// System.out.println(listStark.isEmply());
System.out.println("展示站内情况");
listStark.show();
System.out.println("开始出栈");
while (!listStark.isEmply()){
listStark.pop();
}
}
}
class ListStark{
private Node head = new Node(-1);
public Node getHead() {
return head;
}
public boolean isEmply(){
return head.next == null;
}
//压栈
public void push(Node node){
if (isEmply()){
head.next = node;
return;
}
Node temp = head;
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
}
temp.next = node;
}
//弹栈
public void pop(){
if (isEmply()){
System.out.println("栈空,无法弹栈");
return;
}
Node temp = head;
while (true){
if (temp.next.next == null){
break;
}
temp = temp.next;
}
System.out.println(temp.next);
temp.next=null;
}
public void show(){
Node temp = head;
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
System.out.println(temp);
}
}
}
class Node{
public int value;
public Node next;
public Node(int value){
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
栈实现计算器 中缀表达式
//计算器
public class Calculator {
public static void main(String[] args) {
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
int num1 = 0;
int num2 = 0;
int res = 0;
int oper = 0;
//索引
int index = 0;
String s ="701+2*5-4";
char c = ' ';
String keepnum="";
while (true){
c=s.substring(index,index+1).charAt(0);
if (operStack.isOper(c)){
//是运算符
if (operStack.isEmply()){
operStack.push(c);
}else {
//里面有运算符
if (operStack.priority(c) < operStack.priority(operStack.speek())){
//栈里面的运算符优先级高 需要先运算
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res=numStack.cal(num1,num2,oper);
numStack.push(res);
operStack.push(c);
}else {
//运算符大于里面的优先级
operStack.push(c);
}
}
}else {
//是数字 c是字符与数字相差48
// numStack.push(c - 48);
keepnum += c;
if (index == s.length() -1){
//最后一位
numStack.push(Integer.parseInt(keepnum));
}else {
// 先偷看一下下一个是不是数字,是数字就index++,不是就压入栈中
if (operStack.isOper(s.substring(index+1,index+2).charAt(0))){
//扫描完一个数字
numStack.push(Integer.parseInt(keepnum));
//清空 keepnum 在扫描完这个数字时清空keepnum
keepnum="";
}
}
}
index++;
if (index >= s.length()){
break; //扫描完
}
}
while (true){
if (operStack.isEmply()){
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res=numStack.cal(num1,num2,oper);
numStack.push(res);
}
int res2 = numStack.pop();
System.out.println(s+"的结果为"+res2);
}
}
class ArrayStack2{
private int top;
private int size;
private int[] arr;
public ArrayStack2(int size){
this.size = size;
arr = new int[size];
top = -1;
}
public boolean isEmply(){
return top == -1;
}
public boolean isFull(){
return top == size - 1;
}
public int getSize(){
int k = 0 ;
for (int i = 0; i < size; i++) {
if (arr[size] != 0){
k++;
}
}
return k;
}
// 查看栈顶元素
public int speek(){
return arr[top];
}
// 判断是运算符 字符底层也是数字
public boolean isOper(int oper){
return oper == '+' || oper == '-' || oper == '/' || oper == '*';
}
//判断优先级
public int priority(int oper){
if (oper == '+' || oper == '-'){
return 0;
}else if (oper == '*' || oper == '/'){
return 1;
}else {
System.out.println("暂不支持加减乘除以外运算符");
return -1;
}
}
//计算
public int cal(int num1,int num2,int oper){
int res = 0;
switch (oper){
case '+' :
res = num1 + num2 ;
break;
case '-':
res = num2 - num1 ;
break;
case '/':
res = num2 / num1 ;
break;
case '*':
res = num2 * num1 ;
break;
}
return res;
}
public void push(int n){
if (isFull()){
System.out.println("栈满,无法添加");
}
top++;
arr[top] = n;
}
public int pop(){
if (isEmply()){
System.out.println("栈空,无法弹栈");
}
int value = arr[top];
top--;
return value;
}
}
计算器 逆波兰表达式 (后缀表达式)
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
//逆波兰表达式 后缀表达式
public class PolandNotation {
public static void main(String[] args) {
// (30+4)*5-6
String s = "30 4 + 5 * 6 -";
List<String> listString = getListString(s);
int cal = 0;
try {
cal = cal(listString);
}catch (Exception e){
System.out.println(e);
}
System.out.println(s+"="+cal);
}
public static List<String> getListString(String s){
String[] split = s.split(" ");
List<String> list = new ArrayList<>();
for (String item : split) {
list.add(item);
}
return list;
}
public static int cal(List<String> list){
Stack<String> stack = new Stack<>();
for (String s : list) {
//正则判断 多位数
if (s.matches("\\d+")){
stack.push(s);
}else {
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
if (s.equals("+")){
res = num1 + num2;
}else if (s.equals("-")){
res = num2 - num1;
}else if (s.equals("*")){
res = num1 * num2;
}else if (s.equals("/")){
res = num2 / num1;
}else {
throw new RuntimeException("运算符有误");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}
中缀转后缀实现计算器
举例
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
//逆波兰表达式 后缀表达式 输入中缀表达式
public class PolandNotation2 {
public static void main(String[] args) {
// (30+4)*5-6
String s = "(30+4)*5-6";
List<String> list = InfixToList(s);
System.out.println(list);
List<String> toLast = InfixListToLast(list);
System.out.println(toLast);
int cal = cal(toLast);
System.out.println(s+"="+cal);
}
public static List<String> getListString(String s){
String[] split = s.split(" ");
List<String> list = new ArrayList<>();
for (String item : split) {
list.add(item);
}
return list;
}
// 中缀 转 list
public static List<String> InfixToList(String s){
int index = 0;
String str = "";//多位数拼接
char c = ' '; //每次遍历到的字符
List<String> list = new ArrayList<>();
while (true){
if (index >= s.length()){
return list;
}
if ((c=s.charAt(index)) < 48 || (c=s.charAt(index)) > 57){
//不是数字
list.add(c+"");
index++;
}else {
//数字 考虑多位
str="";
while (index < s.length() && ((c=s.charAt(index)) >= 48 && (c=s.charAt(index)) <= 57)){
//没有到末尾,多位数的下一位还是数 赋值给c,刷新c
str += c;
index++;
}
//到末尾或者s.charAt(index)不是数字
list.add(str);
}
}
}
//中缀转后缀
public static List<String> InfixListToLast(List<String> list){
Stack<String> s1 = new Stack<>();
//s2无弹栈,而且栈的倒序输出比较麻烦,所以用ArrayList存储
List<String> s2 = new ArrayList<>();
for (String item : list) {
if (item.matches("\\d+")){
//是数字
s2.add(item);
}else if (item.equals("(")) {
//没有运算符
s1.push(item);
}else if (item.equals(")")){
while (true){
if (s1.peek().equals("(")){
break;
}
s2.add(s1.pop());
}
s1.pop();
}else {
while (s1.size() != 0 && priority(item.charAt(0)) < priority(s1.peek().charAt(0))) {
//带传入的运算符 优先级低
s2.add(s1.pop());
}
s1.push(item);
}
}
//将s1剩下的运算符压入s2
while (s1.size() != 0){
s2.add(s1.pop());
}
return s2;
}
//判断优先级
public static int priority(int oper){
if (oper == '+' || oper == '-'){
return 0;
}else if (oper == '*' || oper == '/'){
return 1;
}else {
char c = (char) oper;
System.out.println("暂不支持加减乘除以外运算符"+c);
return -1;
}
}
//计算方法
public static int cal(List<String> list){
Stack<String> stack = new Stack<>();
for (String s : list) {
//正则判断 多位数
if (s.matches("\\d+")){
stack.push(s);
}else {
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
if (s.equals("+")){
res = num1 + num2;
}else if (s.equals("-")){
res = num2 - num1;
}else if (s.equals("*")){
res = num1 * num2;
}else if (s.equals("/")){
res = num2 / num1;
}else {
throw new RuntimeException("运算符有误");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}
递归
递归就是自己调自己,每次传入不同的参数
迷宫问题:将小球从左上角移动到右下角,寻找一条通路
public class MiGong {
public static void main(String[] args) {
//制作地图
int[][] a = new int[8][7];
for (int i = 0; i < 7; i++) {
a[0][i] = 1;
a[7][i] = 1;
}
for (int j = 0; j < 8; j++) {
a[j][0] = 1;
a[j][6] = 1;
}
a[2][1] = 1;
a[2][2] = 1;
//地图原型
System.out.println("地图原型");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(a[i][j]+" ");
}
System.out.println();
}
setWay(a,1,1);
//通路
System.out.println("通路");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(a[i][j]+" ");
}
System.out.println();
}
}
//1 表示墙 2表示走过的路 3 表示死路 规则 上下左右
//使用的地图是引用类型
public static boolean setWay(int[][] a,int i,int j){
if (a[6][5] == 2){
return true;
}else {
if (a[i][j] == 0){
//假设 a[i][j] =2
a[i][j] = 2;
if (setWay(a,i+1,j)){
return true;
}else if (setWay(a, i-1, j)){
return true;
}else if (setWay(a, i, j+1)){
return true;
}else if (setWay(a, i, j-1)){
return true;
}else {
//上下左右都不行 死路
a[i][j] = 3;
return false;
}
}else {
// if (a[i][i] == 1 || a[i][i] == 2 || a[i][i] == 3 ){
// return false;
// }
return false;
}
}
}
}
八皇后问题
public class Queue8 {
//用一维数组保存8皇后位置,可以不用检测是否同行
static int[] arr =new int[8];
static int count = 0;
public static void main(String[] args) {
cheek(0);
System.out.println("一共有"+count+"种");
}
//放置第n个皇后
public static void cheek(int n){
if (n == 8){
print();
return;
}
for (int i = 0; i < 8; i++) {
//给第n个皇后 遍历寻找合适位置
arr[n] = i ;
//判断第n个皇后位置是否合适
if (judge(n)){
cheek(n+1);
}
}
}
//检测是否冲突
public static boolean judge(int n){
for (int i = 0; i < n; i++) {
//arr[n] == arr[i] 同一例
//Math.abs(n-i) == Math.abs(arr[n] - arr[i]) 同一斜线 用k=1
if (arr[n] == arr[i] || Math.abs(n-i) == Math.abs(arr[n] - arr[i])){
return false;
}
}
return true;
}
//打印数组
public static void print(){
count++;
for (int i : arr) {
System.out.print(i+" ");
}
System.out.println();
}
}
排序算法
冒泡排序
每次找到一个最大值,然后在剩下的数字中找最大值。每次比较两个数,将数值大的放后面,每一趟就可以找到该趟的最大值。五个数找到四个最大值,就相当于完成了五个数的排序,即只需要arr.length - 1 次。每趟剔除一个最大值 arr.length - 1- i
public class Maopao {
public static void main(String[] args) {
int[] arr = {-1,9,-2,20,10}; // 五个数 需要找四个最大值
//每趟不交换的次数
int count = 0 ;
//保存j
int a = 0;
// 找出 arr.length-1 的每趟最大值
for (int i = 0; i < arr.length-1; i++) {
count = 0;
// 每次找到的最大值不用参与排序 arr.length -1 -i
for (int j = 0; j < arr.length -1 -i; j++) {
a=j+1; //比较的次数
if (arr[j] > arr[j+1]){
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}else {
count++;
}
}
System.out.println("第"+i+"趟的结果"+ Arrays.toString(arr));
//一趟中没有一次交换,排序已完成 可以用Boolean 确定
if (count== a){
break;
}
}
System.out.println("最后的结果为:");
for (int i : arr) {
System.out.print(i+" ");
}
}
}
选择排序
找到最小值后交换位置放入合适位置,在找最小值前不发生交换。
public class xuanze {
public static void main(String[] args) {
int[] arr = {-1,9,-2,20,10};
int min = 0;
int a =0;
boolean flag = false;
for (int i = 0; i < arr.length-1; i++) {
//假设min
min = arr[i];
for (int j = i+1; j < arr.length; j++) {
if (arr[i]>arr[j]){
//如果比min小,保存更小的arr[j]
min = arr[j];
a=j;
flag =true;
}
}
if (flag){
//交换 min != arr[i];
arr[a] = arr[i];
arr[i] = min;
}else {
flag = false;
}
}
System.out.println(Arrays.toString(arr));
}
}
插入排序
将一个无序数组看成两个数组,前面的有序,后面的无序,每次从无序数组中取一个数,插入到有序数组中的合适位置
数组实现
import java.util.Arrays;
//插入排序 看成两个数组,前面的有序,后面的无序,每次从无序数组中取一个数,插入到有序数组中的合适位置
public class InsertSort {
public static void main(String[] args) {
int[] arr = {-1,9,-2,20,10};
insertsort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertsort(int[] arr){
int insertVal = 0;
int inserIndex = 0;
//五个数 需要插入四次 第一个数默认有序,从第二个数开始
for (int i = 1; i < arr.length; i++) {
insertVal = arr[i];
// i-1 是已排序的数组 假设插入的合适位置是i-1
inserIndex = i-1;
//寻找插入的合适位置
while (inserIndex >= 0 && insertVal < arr[inserIndex]){
//insertVal < arr[inserIndex] 说明 arr[inserIndex] 占据了错误位置,后移,让位
arr[inserIndex+1] = arr[inserIndex];
//寻找合适位置,再次比较
inserIndex--;
}
// insertVal > arr[inserIndex] 所以放在insertIndex+1 的位置上
arr[inserIndex+1] = insertVal;
}
}
}
单链表实现
public class InsertSort2 {
public static void main(String[] args) {
// int[] arr = {-1,9,-2,20,10,-1};
//
// LinkedList linkedList = new LinkedList();
// insertsort(arr,linkedList);
//
// linkedList.show();
int[] arr = new int[100000];
for (int i = 0; i < 100000; i++) {
//随机生成0到8000000的数
arr[i] = (int) (Math.random() * 8000000);
}
System.out.print("排序前 ");
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String format1 = simpleDateFormat.format(date1);
System.out.println(format1);
LinkedList linkedList = new LinkedList();
insertsort(arr,linkedList);
System.out.print("排序后 "); //29s
Date date2 = new Date();
String format2 = simpleDateFormat.format(date2);
System.out.println(format2);
}
//用链表实现插入排序
public static void insertsort(int[] arr,LinkedList list){
for (int i = 0; i < arr.length; i++) {
Node node = new Node(i,arr[i]);
list.addByOrder(node);
}
}
}
class LinkedList {
private Node head = new Node(0,-1);
public Node getHead() {
return head;
}
//通过编号添加
public void addByOrder(Node node){
Node temp = head;
boolean flag = false;
while (true){
if (temp.next == null){
break;
}if (temp.next.val >= node.val){
break;
}else if (temp.next.no == node.no){
flag =true;
break;
}
temp=temp.next;
}
if (flag){
System.out.println("编号已存在,请重新确认");
}else {
node.next=temp.next;
temp.next=node;
}
}
public void show(){
Node temp = head.next;
while (temp!=null){
System.out.println(temp);
temp=temp.next;
}
}
}
class Node{
public int no;
public int val;
public Node next;
public Node(int no,int value){
this.no = no;
this.val = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + val +
'}';
}
}
单链表插入更容易,以为插入排序会更快,测试了一下,100000的排序,单链表需要29秒,800000数组只需要19秒,望君解惑!!!不胜感激
希尔排序
希尔排序 在直接插入排序前加入步长分组,简化,减少排序时间
如下图中假设使用原来的直接插入排序,0在找到自己的合适位置需要比较很多次
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
//希尔排序 在直接插入排序前加入步长分组,简化
public class shellsort {
public static void main(String[] args) {
// int[] arr = {8,9,1,7,2,3,5,4,6,0};
// shell(arr);
// System.out.println(Arrays.toString(arr));
int[] arr = new int[800000];
for (int i = 0; i < 800000; i++) {
//随机生成0到8000000的数
arr[i] = (int) (Math.random() * 8000000);
}
System.out.print("排序前 ");
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String format1 = simpleDateFormat.format(date1);
System.out.println(format1);
shell(arr);
System.out.print("排序后 ");
Date date2 = new Date();
String format2 = simpleDateFormat.format(date2);
System.out.println(format2);
}
//交换法 每次比较,如果不符合排序规则就交换位置,太耗费资源和时间 80000数据排序13s
public static void shell(int[] arr){
int temp = 0;
//i 是每次的步长 分成i组
for (int i = arr.length/2; i > 0 ; i /=2) {
// 分组 从j到arr.length 因为分成了i组,每组的首位可以看成有序组 所以从i开始
for (int j = i; j < arr.length; j++) {
//每组进行比较 每次插入的数字在k>=0时,继续向前比较,寻找合适位置
for (int k = j; k >= 0; k -= i) {
// 先判断k-i >= 0 再arr[k-i] 否则报错
if (k-i >= 0 && arr[k] < arr[k-i] ){
temp = arr[k-i];
arr[k-i] = arr[k];
arr[k] = temp;
}
}
}
}
}
//位移法 将不符合排序规则的后移,将需要插入的数据插入到合适的位置 800000数据排序不到一秒
public static void shell2(int[] arr){
int insertVal = 0;
int inserIndex = 0 ;
//i 是每次的步长 分成i组
for (int i = arr.length/2; i > 0 ; i /=2) {
// 分组 从j到arr.length 因为分成了i组,每组的首位可以看成有序组 所以从i开始
for (int j = i; j < arr.length; j++) {
//如果arr[inserIndex] > arr[inserIndex-i] 插入的位置就是原来的位置
inserIndex = j;
insertVal = arr[inserIndex];
if (arr[inserIndex] < arr[inserIndex-i]){
while (inserIndex-i >= 0 && insertVal < arr[inserIndex-i]){
arr[inserIndex] = arr[inserIndex-i];
inserIndex -= i;
}
arr[inserIndex] = insertVal;
}
}
// System.out.println(Arrays.toString(arr));
}
}
//位移法
public static void shell3(int[] arr){
int insertVal = 0;
int inserIndex = 0 ;
//i 是每次的步长 分成i组
for (int i = arr.length/2; i > 0 ; i /=2) {
// 分组 从j到arr.length 因为分成了i组,每组的首位可以看成有序组 所以从i开始
for (int j = i; j < arr.length; j++) {
// 因为之后可能要后移,先将要插入的值保留
insertVal = arr[j];
//假设插入的位置就是j-i
inserIndex = j-i;
while (inserIndex >=0 && insertVal < arr[inserIndex]){
arr[inserIndex+i] = arr[inserIndex];
inserIndex -= i;
}
arr[inserIndex+i] = insertVal;
}
// System.out.println(Arrays.toString(arr));
}
}
}
快速排序
找一个数做基准值,将比他大的数放右边,比他小的放左边,就相当于找到了这个基准值的正确位置,在持续递归,为两边的数找自己的位置
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
//快速排序 找一个数做基准值,将比他大的数放右边,比他小的放左边,就相当于找到了这个基准值的正确位置,在持续递归,为两边的数找自己的位置
public class quicksort {
public static void main(String[] args) {
// int[] arr = {8,9,1,7,2,3,5,4,6,0};
// quick(arr,0, arr.length-1);
// System.out.println(Arrays.toString(arr));
int[] arr = new int[800000];
for (int i = 0; i < 800000; i++) {
//随机生成0到8000000的数
arr[i] = (int) (Math.random() * 8000000);
}
System.out.print("排序前 ");
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String format1 = simpleDateFormat.format(date1);
System.out.println(format1);
quick(arr,0, arr.length-1);
System.out.print("排序后 "); //不足1s
Date date2 = new Date();
String format2 = simpleDateFormat.format(date2);
System.out.println(format2);
}
public static void quick(int[] arr,int left,int right){
if (left < right){
int index = getIndex(arr, left, right);
//递归 查找index左边的数 的正确位置
quick(arr, left, index-1);
quick(arr, index+1, right);
}
}
public static int getIndex(int[] arr,int left,int right){
//给arr[left]寻找正确位置
int temp = arr[left];
while (left < right){
//保留了left的值,就从右边开始寻找,替换left的位置。否则覆盖右边right的值
while (left < right && arr[right] >= temp){
right--;
}
//右边发现比temp小的数
if (left < right){
//出代表 arr[left] > temp
arr[left] = arr[right];
}else{
break;
}
while (left < right && arr[left] <= temp){
left++;
}
//左边发现比temp大的数
if (left < right){
arr[right] = arr[left];
}else{
break;
}
}
//left==right
arr[left]=temp;
return left;
}
}
归并排序
//归并排序 分治思想
public class MergetSort {
public static void main(String[] args) {
// int[] arr = {8,9,1,7,2,3,5,4,6,0};
// int[] temp = new int[arr.length];
// mergesort(arr,0, arr.length-1,temp);
// System.out.println(Arrays.toString(arr));
int[] arr = new int[800000];
int[] temp = new int[arr.length];
for (int i = 0; i < 800000; i++) {
//随机生成0到8000000的数
arr[i] = (int) (Math.random() * 8000000);
}
System.out.print("排序前 ");
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String format1 = simpleDateFormat.format(date1);
System.out.println(format1);
mergesort(arr,0, arr.length-1,temp);
System.out.print("排序后 "); //不足1s
Date date2 = new Date();
String format2 = simpleDateFormat.format(date2);
System.out.println(format2);
}
// 拆分
public static void mergesort(int[] arr,int left,int right,int[] temp){
//还可拆分 left == right 只有一个数,不可拆分
if (left < right){
int mid = (left+right)/2;
//向左分解
mergesort(arr, left, mid, temp);
//向右分解
mergesort(arr, mid+1, right, temp);
merge(arr, left, mid, right, temp);
}
}
private static void merge(int[] arr,int left,int mid,int right,int[] temp) {
//第一个数组的起始索引
int i = left;
//第二个数组的起始索引
int j = mid+1;
//临时数组的索引
int k = 0;
while (i <= mid && j <= right){
//将数值小的数拷贝到temp中
if (arr[i] <= arr[j]){
temp[k] = arr[i];
i++;
k++;
}else {
temp[k] = arr[j];
j++;
k++;
}
}
//将剩余的数保存到temp中
while (i <= mid){
temp[k] = arr[i];
k++;
i++;
}
while (j <= right){
temp[k] = arr[j];
k++;
j++;
}
//将temp拷贝回arr 并不是每次都拷贝所有的 而且需要将数据放回原来的位置需要left right
int tempLeft = left;
k=0;
while (tempLeft <= right){
arr[tempLeft] = temp[k];
k++;
tempLeft++;
}
}
}
基数排序
将数字按个十百位放入桶中,取出,完成排序 空间换时间
一个数取各个位
123 / 1 % 10 = 3
123 / 10 % 10 = 2
123 / 100 % 10 = 1
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
//基数排序,将数字按个十百位放入桶中,取出 空间换时间
public class RadixSort {
public static void main(String[] args) {
// int[] arr = {8,9,1,7,2,3,5,4,6,0};
// radix(arr);
// System.out.println(Arrays.toString(arr));
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; i++) {
//随机生成0到8000000的数
arr[i] = (int) (Math.random() * 8000000);
}
System.out.print("排序前 ");
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
String format1 = simpleDateFormat.format(date1);
System.out.println(format1);
radix(arr);
System.out.print("排序后 "); //800w 1s
Date date2 = new Date();
String format2 = simpleDateFormat.format(date2);
System.out.println(format2);
}
public static void radix(int[] arr){
// 得到最大数的位数
int max = arr[0];
for (int i : arr) {
if (i>max){
max = i;
}
}
int maxLength = (max+"").length();
// 桶
int[][] bucket = new int[10][arr.length];
//记录各个桶中 数据个数
int[] bucketElementCounts = new int[10];
//几位数比较几次
for (int i = 0, n =1; i < maxLength; i++,n *= 10) {
for (int j = 0; j < arr.length; j++) {
//取位
int t = arr[j] / n % 10 ;
//向对应桶中存放数据
bucket[t][bucketElementCounts[t]] = arr[j];
//记录对应桶中数据个数
bucketElementCounts[t]++;
}
// 遍历每个桶重新放回arr
int temp = 0; //指向arr的索引
for (int j = 0; j < bucket.length; j++) {
if (bucketElementCounts[j] != 0){
//有数据
for (int k = 0; k < bucketElementCounts[j]; k++) {
arr[temp] = bucket[j][k];
temp++;
}
}
//将桶内数据个数至0 虽然数据还在桶内
bucketElementCounts[j] = 0;
}
}
}
}
查找
线性查找
按顺序查找,放回下标
//线性查找
public class SeqSearch {
public static void main(String[] args) {
int[] arr = {1,4,5,7,1};
List seq = seq(arr, 1);
System.out.println(seq); // [0, 4]
}
public static List seq(int[] arr,int val) {
List list = new ArrayList();
for (int i = 0; i < arr.length; i++) {
if (arr[i] == val){
list.add(i);
}
}
return list;
}
}
二分查找
条件:数组有序
import java.util.ArrayList;
import java.util.List;
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1,2,4,5,5,7,9};
int seq = binary(arr, 0,arr.length-1,5);
System.out.println(seq); //
List list = binary2(arr, 0, arr.length - 1, 5);
// List list = binary2(arr, 4, 5, 5);
System.out.println(list);
}
public static int binary(int[] arr,int left,int right,int val) {
if (left <= right){
int mid = (left+right)/2;
if (val == arr[mid]){
return mid;
}else if (val < arr[mid]){
return binary(arr, left, mid-1, val);
}else {
return binary(arr, mid+1, right, val);
}
}
return -1;
}
//查找所有符合条件的下标 用集合保存
public static List binary2(int[] arr,int left,int right,int val) {
if (left <= right){
int mid = (left+right)/2;
if (val == arr[mid]){
List<Integer> list = new ArrayList<Integer>();
int temp = mid;
while (true){
//向左查找是否有符合条件的数
if (temp-1 >= left && arr[temp-1] == val){
list.add(temp-1);
temp--;
}
break;
}
list.add(mid);
while (true){
//向右查找是否有符合条件的数
if (temp+1 <= right && arr[temp+1] == val){
list.add(temp+1);
temp++;
}
break;
}
return list;
}else if (val < arr[mid]){
return binary2(arr, left, mid-1, val);
}else {
return binary2(arr, mid+1, right, val);
}
}
//未找到放回一个空的集合
return new ArrayList();
}
}
插值查找
与二分法查找类似,不同的是mid通过一个公式具有自适应性,可以更快定位到要找的位置
后面两张图解释公式的由来,摘自插值查找 - 知乎
public class insertValueSearch {
public static void main(String[] args) {
int[] arr = {1,2,4,5,5,7,9};
int seq = binary(arr, 0,arr.length-1,5);
System.out.println(seq);
}
public static int insert(int[] arr,int left,int right,int val) {
//val >= arr[0] && val <= arr[arr.length-1] 优化,也可以避免mid越界 因为可能查找一个数组中没有的数,
// 而且这个是很大,导致mid 很大,导致arr[mid]越界 所有这两句必须写
if (left <= right && val >= arr[0] && val <= arr[arr.length-1]){
//(val - arr[left])/(arr[right] - arr[left]) 比val小的数在数组中占的比例,乘以数组的长度
// 当right 和 left重合时 (val - arr[left])/(arr[right] - arr[left])*(right-left) = 0 所有加上left
int mid =left + (val - arr[left])/(arr[right] - arr[left])*(right-left);
if (val == arr[mid]){
return mid;
}else if (val < arr[mid]){
return binary(arr, left, mid-1, val);
}else {
return binary(arr, mid+1, right, val);
}
}
return -1;
}
}
斐波那契查找
斐波那契搜索就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为F[n](如果要补充元素,则补充重复最后一个元素,直到满足F[n]个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。
简介
斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=F(k)-1;开始将k值与第F(k-1)位置的记录进行比较(及mid=low+F(k-1)-1),比较结果也分为三种:
(1)相等,则mid位置的元素即为所求;
(2)>,则low=mid+1,k-=2;
说明:low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))=Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找。
(3))<,则high=mid-1,k-=1。
说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1个,所以可以递归的应用斐波那契查找。
import java.util.Arrays;
//斐波那契数列查找法和二分法、插入数值法类似,不同的是mid的值用不同的方法确定
public class FibonacciSearch {
public static void main(String[] args) {
int[] arr = {1,2,4,5,5,7,9};
int fibonacci = fibonacci(arr, 1);
System.out.println(fibonacci);
}
public static int[] fib(int size){
int[] arr = new int[size];
arr[0] = 1;
arr[1] = 1;
for (int k = 2; k < arr.length; k++) {
arr[k] = arr[k-1] + arr[k-2];
}
return arr;
}
public static int fibonacci(int[] arr,int val){
//指向数组左右两边
int low = 0;
int high = arr.length - 1;
//保存斐波那契数列分割数值的下标
int k = 0;
int[] f = fib(20);
//获取斐波那契数列分割数值的下标
while (high > f[k]-1){
k++;
}
//数组的长度可能和f[k]不一样,补全 工具类是自动用0补全
int[] temp = Arrays.copyOf(arr,f[k]);
//使用最后一个数,补全
for (int i = high+1; i < temp.length; i++) {
temp[i] = temp[high];
}
while (low <= high){
//可分割
int mid = low + f[k-1] - 1;
if (val < temp[mid]){
high = mid - 1;
//f[k] = f[k-1] + f[k-2]
//f[k] 全部元素 f[k-1] mid左边的元素 f[k-2] mid右边的元素
// 第一轮 k=5 ,此时 low到high 是下标0-3共四个数, 等于 f[5-1]-1=4满足斐波那契查找条件
//val < temp[mid] 向左遍历 及k-1 重新确定mid进入循环
k--;
}else if (val > temp[mid]){
low = mid + 1;
//val > temp[mid] 向右遍历 及k-2
k -= 2;
}else {
//val == temp[mid]
return mid;
}
}
return -1;
}
}
哈希表
数组加链表,可做缓存层
public class HashTabDemo {
public static void main(String[] args) {
HashTab hashTab = new HashTab(7);
hashTab.add(new Emp(1,"张三"));
hashTab.add(new Emp(2,"李四"));
hashTab.show();
hashTab.findEmpById(2);
}
}
class HashTab{
private EmpList[] empLists;
private int size ;
public HashTab(int size){
this.size = size;
empLists = new EmpList[size];
//初始化每一个链表 否则报错 空指针
for (int i = 0; i < size; i++) {
empLists[i] = new EmpList();
}
}
public void add(Emp emp){
int empListNo = HashFun(emp.id);
empLists[empListNo].add(emp);
}
public Emp findEmpById(int id){
int empListNo = HashFun(id);
Emp emp = empLists[empListNo].find(id);
if (emp != null){
System.out.println(emp);
}else {
System.out.println("未找到该员工");
}
return emp;
}
public void show(){
for (int i = 0; i < size; i++) {
empLists[i].show(i);
}
}
public int HashFun(int id){
return id % size;
}
}
class EmpList{
private Emp head;
public void add(Emp emp){
if (head == null){
head = emp;
}else {
Emp temp = head;
while (true){
if (temp.next == null){
break;
}
temp = temp.next;
}
temp.next = emp;
}
}
public Emp find(int id){
if (head == null){
System.out.println("条链表为空");
return null;
}else {
Emp temp = head;
while (true){
if (temp.id == id){
break;
}
if (temp.next == null){
temp = null;
break;
}
temp = temp.next;
}
return temp;
}
}
public void show(int no){
if (head == null){
System.out.println("第"+(no+1)+"条链表为空");
}else {
Emp temp = head;
while (true){
System.out.println(temp);
if (temp.next == null){
break;
}
temp = temp.next;
}
}
}
}
class Emp{
public int id;
public String name;
public Emp next;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
二叉树
遍历树
public class BinaryTreeDemo {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
Node node1 = new Node(1,"宋江");
Node node2 = new Node(2,"卢俊义");
Node node3 = new Node(3,"吴用");
Node node4 = new Node(4,"林冲");
Node node5 = new Node(5,"关胜");
node1.setLeft(node2);
node1.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binaryTree.setHead(node1);
System.out.println("前序遍历"); //1,2,3,5,4
binaryTree.preOrder();
System.out.println("中序遍历"); //2,1,5,3,4
binaryTree.midOrder();
System.out.println("后序遍历"); //2,5,4,3,1
binaryTree.postOrder();
}
}
class BinaryTree{
private Node root;
public void setHead(Node root) {
this.root = root;
}
//前序遍历 根左右
public void preOrder(){
if (root != null){
root.preOrder();
}else {
System.out.println("二叉树为空");
}
}
//中序遍历 左根右
public void midOrder(){
if (root != null){
root.midOrder();
}else {
System.out.println("二叉树为空");
}
}
//后序遍历 左右根
public void postOrder(){
if (root != null){
root.postOrder();
}else {
System.out.println("二叉树为空");
}
}
}
class Node{
private int id;
private String name;
private Node left;
private Node right;
public Node() {
}
public Node(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRinght() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
//前序遍历 根左右
public void preOrder(){
System.out.println(this);
if (this.left != null){
this.left.preOrder();
}
if (this.right != null){
this.right.preOrder();
}
}
//中序遍历 左根右
public void midOrder(){
if (this.left != null){
this.left.midOrder();
}
System.out.println(this);
if (this.right != null){
this.right.midOrder();
}
}
//后序遍历 左右根
public void postOrder(){
if (this.left != null){
this.left.postOrder();
}
if (this.right != null){
this.right.postOrder();
}
System.out.println(this);
}
}
查找节点
public class BinaryTreeDemo {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
Node node1 = new Node(1,"宋江");
Node node2 = new Node(2,"卢俊义");
Node node3 = new Node(3,"吴用");
Node node4 = new Node(4,"林冲");
Node node5 = new Node(5,"关胜");
node1.setLeft(node2);
node1.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binaryTree.setHead(node1);
System.out.println("前序遍历"); //1,2,3,5,4
binaryTree.preOrder();
System.out.println("中序遍历"); //2,1,5,3,4
binaryTree.midOrder();
System.out.println("后序遍历"); //2,5,4,3,1
binaryTree.postOrder();
System.out.println("5号");
Node prefind = binaryTree.psotfind(5);
System.out.println(prefind);
}
}
class BinaryTree{
private Node root;
public void setHead(Node root) {
this.root = root;
}
//前序遍历 根左右
public void preOrder(){
if (root != null){
root.preOrder();
}else {
System.out.println("二叉树为空");
}
}
//中序遍历 左根右
public void midOrder(){
if (root != null){
root.midOrder();
}else {
System.out.println("二叉树为空");
}
}
//后序遍历 左右根
public void postOrder(){
if (root != null){
root.postOrder();
}else {
System.out.println("二叉树为空");
}
}
//前序查找 根左右
public Node prefind(int no){
if (root != null){
return root.prefind(no);
}else {
return null;
}
}
//中序查找 根左右
public Node midfind(int no){
if (root != null){
return root.midfind(no);
}else {
return null;
}
}
//后序查找 根左右
public Node psotfind(int no){
if (root != null){
return root.postfind(no);
}else {
return null;
}
}
}
class Node{
private int id;
private String name;
private Node left;
private Node right;
public Node() {
}
public Node(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRinght() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
//前序遍历 根左右
public void preOrder(){
System.out.println(this);
if (this.left != null){
this.left.preOrder();
}
if (this.right != null){
this.right.preOrder();
}
}
//中序遍历 左根右
public void midOrder(){
if (this.left != null){
this.left.midOrder();
}
System.out.println(this);
if (this.right != null){
this.right.midOrder();
}
}
//后序遍历 左右根
public void postOrder(){
if (this.left != null){
this.left.postOrder();
}
if (this.right != null){
this.right.postOrder();
}
System.out.println(this);
}
//前序查找
public Node prefind(int no){
//与根节点比较
if (this.id == no){
return this;
}
//递归向左查找
Node resNode = null;
if (this.left != null){
resNode = this.left.prefind(no);
}
if (resNode != null){
//找到了
return resNode;
}
//递归向右查找
if (this.right != null){
resNode = this.right.prefind(no);
}
return resNode;
}
//中序查找
public Node midfind(int no){
Node resNode = null;
if (this.left != null){
resNode = this.left.midfind(no);
}
if (resNode != null){
return resNode;
}
if (this.id == no){
return this;
}
if (this.right != null){
resNode = this.right.midfind(no);
}
return resNode;
}
//后序查找
public Node postfind(int no){
Node resNode = null;
if (this.left != null){
resNode = this.left.postfind(no);
}
if (resNode != null){
return resNode;
}
if (this.right != null){
resNode = this.right.postfind(no);
}
if (this.id == no){
return this;
}
return resNode;
}
}
删除加点
规则
//1.如果删除的节点是叶子节点,则删除该节点
//2.如果删除的节点是非叶子节点,则删除该子树
class Node
//1.如果删除的节点是叶子节点,则删除该节点
//2.如果删除的节点是非叶子节点,则删除该子树
public void delNode(int id){
if (this.left != null && this.left.id == id){
this.left = null;
}
if (this.right != null && this.right.id == id){
this.right = null;
}
if (this.left != null){
this.left.delNode(id);
}
if (this.right != null){
this.right.delNode(id);
}
}
class tree
public void delNode(int id){
if (root == null){
System.out.println("空树无法删除");
return;
}else if (root.getId() == id){
root = null;
return ;
}else {
root.delNode(id);
}
}
顺序存储二叉树
和数组可以相互转换的一种树,可以使数组完成二叉树的前中后序遍历,在堆排序中有所使用
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7};
ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
System.out.println("前序");
arrBinaryTree.preOrder(); // 1,2,4,5,3,6,7
System.out.println("中序");
arrBinaryTree.midOrder(); //4,2,5,1,6,3,7
System.out.println("后序");
arrBinaryTree.postOrder(); //4,5,2,6,7,3,1
}
}
class ArrBinaryTree{
private int[] arr;
public ArrBinaryTree(int[] arr){
this.arr = arr;
}
public void preOrder(){
preOrder(0);
System.out.println();
}
public void midOrder(){
midOrder(0);
System.out.println();
}
public void postOrder(){
postOrder(0);
System.out.println();
}
// 从数组的那个下标开始遍历 前序遍历
public void preOrder(int index){
System.out.print(arr[index]+" ");
if (index * 2 + 1 < arr.length){
preOrder(index * 2 + 1);
}
if (index * 2 + 2 < arr.length){
preOrder(index * 2 + 2);
}
}
//中序 左根右
public void midOrder(int index){
if (index * 2 + 1 < arr.length){
midOrder(index * 2 + 1);
}
System.out.print(arr[index]+" ");
if (index * 2 + 2 < arr.length){
midOrder(index * 2 + 2);
}
}
//后序 左右根
public void postOrder(int index){
if (index * 2 + 1 < arr.length){
postOrder(index * 2 + 1);
}
if (index * 2 + 2 < arr.length){
postOrder(index * 2 + 2);
}
System.out.print(arr[index]+" ");
}
}
线索二叉树
线索二叉树就是充分利用原来的空指针,因为原先的二叉树的叶子节点会有一到两个的空指针,将这些空指针指向该节点的前驱或者后继节点,就完成了线索二叉树
线索二叉树又可以安装线索的方式分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
//线索化二叉树实现及遍历
public class ThreadedBinaryTreeDemo {
public static void main(String[] args) {
Node root = new Node(1, "tom");
Node node2 = new Node(3, "jack");
Node node3 = new Node(6, "smith");
Node node4 = new Node(8, "mary");
Node node5 = new Node(10, "king");
Node node6 = new Node(14, "dim");
Node node7 = new Node(7, "dim2");
//二叉树,后面我们要递归创建, 现在简单处理使用手动创建
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
node3.setRight(node7);
//测试中序线索化
BinaryTree threadedBinaryTree = new BinaryTree();
threadedBinaryTree.setHead(root);
//线索化二叉树
threadedBinaryTree.threadedNodes();
System.out.println("node5的前驱节点"+node5.getLeft());
System.out.println("node5的后继节点"+node5.getRight());
System.out.println("递归方式遍历");
threadedBinaryTree.midOrder();
System.out.println("线性方式遍历");
threadedBinaryTree.threadedList();
}
}
class BinaryTree{
private Node root;
//需要一个变量保留前一个节点
private Node pre = null;
public void setHead(Node root) {
this.root = root;
}
public void threadedNodes(){
threadedNodes(root);
}
//中序线索化二叉树 只适合从头检索
public void threadedNodes(Node node){
if (node == null){
//不能线索化
return;
}
//线索化左子树
threadedNodes(node.getLeft());
//线索化该节点
// 每次进来node的值是按中序遍历依次出现{8, 3, 10, 1, 14, 6}
// 第一次pre=null 即pre在8的前面 node=8
if (node.getLeft() == null){
//该节点的左指针为空
node.setLeft(pre);
// 表示已线索化
node.setLeftType(1);
}
// {8, 3, 10, 1, 14, 6}
// 第一轮pre=null跳过 第二轮 因为pre=node=8 node=3,
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setRightType(1);
}
// if (pre != null && node.getRight() == null){
// node.setRight(pre); 错误写法,node的右边不知道指向谁
// 如果node=10,pre只有一个,左右都指向了3,
// 但可以用两个指针指向node的前后,感觉不是很好操作
// node.setRightType(1);
// }
// pre = node;
//因为是按中序遍历样式写的,所有node按中序遍历输出的顺序
pre = node;
//线索化右子树
threadedNodes(node.getRight());
//为最后一个数加线索化 到这的都是右子树为空的数 说明其右子树可以线索化
//pre.getRight() == null && node.getRight() == null 包含pre==node和pre!=node两种情况
if (pre.getRight() == null && node.getRight() == null){
//当pre != node 时设置rightType 所以用node好一些 ,因为pre有时指向前一个node
node.setRightType(1);
}
}
//线性的遍历二叉树 线索化后才可以使用
public void threadedList(){
Node node = root;
while (node != null){
//遍历左子树
while (node.getLeftType() == 0){
//循环找到lefttype=1的数
node = node.getLeft();
}
System.out.println(node);
while (node.getRightType() == 1 && node.getRight() != null){
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
//中序遍历 左根右
public void midOrder(){
if (root != null){
root.midOrder();
}else {
System.out.println("二叉树为空");
}
}
//中序查找 根左右
public Node midfind(int no){
if (root != null){
return root.midfind(no);
}else {
return null;
}
}
}
class Node{
private int id;
private String name;
private Node left;
private Node right;
// {8, 3, 10, 1, 14, 6} 8.leftType和6.rightType应该为1,都已线索化,只是指向null
//1. 如果 leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点
//2. 如果 rightType == 0 表示指向是右子树, 如果 1 表示指向后继结点
private int leftType;
private int rightType;
public Node() {
}
public Node(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
public int getLeftType() {
return leftType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public int getRightType() {
return rightType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
", leftType=" + leftType +
", rightType=" + rightType +
'}';
}
//中序遍历 左根右 递归的方式
public void midOrder(){
if (this.left != null && this.leftType != 1){
this.left.midOrder();
}
System.out.println(this);
if (this.right != null && this.rightType != 1){
this.right.midOrder();
}
}
//中序查找
public Node midfind(int no){
Node resNode = null;
if (this.left != null){
resNode = this.left.midfind(no);
}
if (resNode != null){
return resNode;
}
if (this.id == no){
return this;
}
if (this.right != null){
resNode = this.right.midfind(no);
}
return resNode;
}
}
堆排序
思路:将无序的数组按大堆顶构建树,树顶为最大的数,将该数和最后一个数换位置,重构除最后一个数的数组为大堆顶
import java.util.Arrays;
//堆排序 将无序的数组按大堆顶构建树,树顶为最大的数,将该数和最后一个数换位置,重构除最后一个数的数组为大堆顶
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4, 6, 8, 5, 9};
heap(arr);
System.out.println(Arrays.toString(arr));
}
public static void heap(int[] arr){
int temp ;
//非叶子节点个数 arr.length/2 最后一个非叶子节点在数组中的位置arr.length/2-1
for (int i = arr.length/2-1 ; i >= 0 ; i--) {
//构建大堆顶
bigTop(arr, i, arr.length);
}
// 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
// 3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换
// 步骤,直到整个序列有序。
// 调整次数 arr.length-1
for (int j = arr.length-1 ; j > 0 ; j--) {
//交换首位元素
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
//重构大堆顶
bigTop(arr,0,j);
}
}
//将数组(二叉树)转换成大堆顶
/**
*
* @param arr 待调整数组
* @param i 非叶子节点在数组中的索引
* @param length 表示还要多少个待调整的数,length是在减少的
*/
public static void bigTop(int[] arr,int i,int length){
//temp保留arr[i]
int temp = arr[i];
// i * 2 + 1 节点i的左节点
for (int k = i * 2 + 1; k < length; k = k * 2 + 1 ) {
if (k+1 < length && arr[k] < arr[k+1] ){
//k指向更大的数
k++;
}
if (arr[k] > temp){
//数值大的替换数顶位置
arr[i] = arr[k];
// 调整底层树结构,寻找temp合适位置
i = k;
}else {
// 因为从最后一个非叶子节点开始调整树结构,当arr[k] < temp时 arr[k]的左右节点要不是null要不就是已经调整好的树
break;
}
}
//找到temp合适位置
arr[i] = temp;
}
}
哈夫曼树
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Huffuman {
public static void main(String[] args) {
int[] arr = {13, 7, 8, 3, 29, 6, 1};
Node node = huffuman(arr);
persort(node);
}
public static void persort(Node root){
if (root != null){
root.persort();
}else {
System.out.println("空数");
}
}
public static Node huffuman(int[] arr){
List<Node> nodes = new ArrayList<Node>();
//加入列表,方便排序
for (int i : arr) {
nodes.add(new Node(i));
}
// nodes 里面只有一个node时
while (nodes.size() > 1){
Collections.sort(nodes);
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
Node parents = new Node(leftNode.value + rightNode.value);
parents.left = leftNode;
parents.right = rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parents);
}
return nodes.get(0);
}
}
class Node implements Comparable<Node>{
public int value;
public Node left;
public Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
//从小到大排序
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
public void persort(){
System.out.println(this);
if (this.left != null){
this.left.persort();
}
if (this.right != null){
this.right.persort();
}
}
}
哈夫曼编码实现文件压缩解压
import java.io.*;
import java.util.*;
//哈夫曼编码 压缩 解压缩文件
//压缩 按照字符出现的次数体现权值,所以字符构建一个哈夫曼树,通过哈夫曼编码表对数据进行压缩
public class HuffumanCode {
public static void main(String[] args) {
String s = "i like like like java do you like a java";
byte[] bytes = s.getBytes();
byte[] zip = huffmanZip(bytes);
byte[] decode = decode(map, zip);
System.out.println(new String(decode));
}
//压缩文件
public static void zipFile(String srcFile, String dstFile) {
//创建输出流
OutputStream os = null;
ObjectOutputStream oos = null;
//创建文件的输入流
FileInputStream is = null;
try {
//创建文件的输入流
is = new FileInputStream(srcFile);
//创建一个和源文件大小一样的 byte[]
byte[] b = new byte[is.available()];
//读取文件
is.read(b);
//直接对源文件压缩
byte[] huffmanBytes = huffmanZip(b);
//创建文件的输出流, 存放压缩文件
os = new FileOutputStream(dstFile);
//创建一个和文件输出流关联的 ObjectOutputStream
oos = new ObjectOutputStream(os);
//把 赫夫曼编码后的字节数组写入压缩文件
oos.writeObject(huffmanBytes); //我们是把
//这里我们以对象流的方式写入 赫夫曼编码,是为了以后我们恢复源文件时使用
//注意一定要把赫夫曼编码 写入压缩文件
oos.writeObject(map);
}catch (Exception e) {
System.out.println(e.getMessage());
}finally {
try {
is.close();
oos.close();
os.close();
}catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
/*
* @param zipFile 准备解压的文件
* @param dstFile 将文件解压到哪个路径
*/
public static void unZipFile(String zipFile, String dstFile) {
//定义文件输入流
InputStream is = null;
//定义一个对象输入流
ObjectInputStream ois = null;
//定义文件的输出流
OutputStream os = null;
try {
//创建文件输入流
is = new FileInputStream(zipFile);
//创建一个和 is 关联的对象输入流
ois = new ObjectInputStream(is);
//读取 byte 数组 huffmanBytes
byte[] huffmanBytes = (byte[])ois.readObject();
//读取赫夫曼编码表
Map<Byte,String> huffmanCodes = (Map<Byte,String>)ois.readObject();
//解码
byte[] bytes = decode(huffmanCodes, huffmanBytes);
//将 bytes 数组写入到目标文件
os = new FileOutputStream(dstFile);
//写数据到 dstFile 文件
os.write(bytes);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
os.close();
ois.close();
is.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
//将压缩封装
public static byte[] huffmanZip(byte[] bytes){
List list = getList(bytes);
//创建哈夫曼树
Node2 node2 = HufuManTree(list);
//node2.presort();
getCodes(node2,"",stringBuilder);
//System.out.println("哈夫曼编码表 "+map);
byte[] zip = zip(bytes, map);
//System.out.println("压缩后 "+Arrays.toString(zip));
return zip;
}
/**
*
* @param flag 标志是否补高位,如果为true表示需要补高位,如果是 false 表示不补, 如果是最后一个
* 字节,无需补高位
* @param b 该 b 对应的二进制的字符串
* @return
*/
public static String byteToBitString(boolean flag,byte b){
//将 b 转成 int
int temp = b;
if (flag){
//补高位
temp |= 256;
}
//toBinaryString 将int转二进制码的补码 如果是正数转的二进制码会不足八位,所以需要 |256 补位
// 1 0000 0000 | 0000 0001 => 1 0000 0001 取后八位就还是原来的数 需要补码
String s = Integer.toBinaryString(temp);
if (flag){
//取后八位
return s.substring(s.length() - 8);
}else {
return s;
}
}
/**
* 解码
* @param map 哈夫曼编码表
* @param bytes 被压缩的数据
* @return
*/
public static byte[] decode(Map<Byte,String> map,byte[] bytes){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
//除最后一位,无序补高位
boolean flag = (i == bytes.length - 1 );
stringBuilder.append(byteToBitString(!flag,bytes[i]));
}
Map<String,Byte> stringByteMap = new HashMap<>();
//将map的key和val互换
for (Map.Entry<Byte,String> entry : map.entrySet()){
stringByteMap.put(entry.getValue(),entry.getKey());
}
ArrayList<Byte> list = new ArrayList<>();
for (int i = 0; i < stringBuilder.length(); ) {
int index = 2;
Byte b = null;
boolean flag = true;
while (flag){
if (i+index > stringBuilder.length()) {
String key =stringBuilder.substring(i);
break;
}
// 因为哈夫曼编码压缩文件 每个数据的编码值是不一样的且不存在多义性,单个 0 1 存在多义性,不可能存在 所以直接取两位
String key = stringBuilder.substring(i,i+index);
b = stringByteMap.get(key);
if (null == b){
index++;
}else {
flag = false;
}
}
list.add(b);
//substring 左闭右开
i += index;
}
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
public static List getList(byte[] bytes){
//将数据和数据个数存入map中
HashMap<Byte, Integer> map = new HashMap<Byte, Integer>();
for (byte b : bytes) {
Integer count = map.get(b);
if (count == null){
map.put(b,1);
}else {
map.put(b,count + 1);
}
}
ArrayList<Node2> list = new ArrayList<>();
for (Map.Entry<Byte,Integer> entry : map.entrySet()){
list.add(new Node2(entry.getKey(), entry.getValue()));
}
return list;
}
public static Node2 HufuManTree(List<Node2> list){
while (list.size() > 1){
Collections.sort(list);
Node2 leftNode = list.get(0);
Node2 rightNode = list.get(1);
Node2 parents = new Node2(null, leftNode.count + rightNode.count);
parents.left = leftNode;
parents.right = rightNode;
list.remove(leftNode);
list.remove(rightNode);
list.add(parents);
}
return list.get(0);
}
public static void presort(Node2 root){
if (root != null){
root.presort();
}else {
System.out.println("空树");
}
}
//保存哈夫曼编码
static StringBuilder stringBuilder = new StringBuilder();
//哈夫曼编码表
static Map<Byte,String> map = new HashMap<>();
//得到各个字符的哈夫曼编码并保存到map中
public static void getCodes(Node2 node2,String code,StringBuilder stringBuilder){
StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
stringBuilder1.append(code);
if (node2 != null){
if (node2.date == null){
//非叶子节点
getCodes(node2.left, "0", stringBuilder1);
getCodes(node2.right, "1", stringBuilder1);
}else {
map.put(node2.date,stringBuilder1.toString());
}
}
}
//按哈夫曼编码表压缩数据
public static byte[] zip(byte[] bytes,Map<Byte,String> map){
StringBuilder stringBuilder = new StringBuilder();
for (byte aByte : bytes) {
//经过哈夫曼编码的二进制
stringBuilder.append(map.get(aByte));
}
//stringBuilder转byte数组
//创建byte数组需要知道长度
int len;
if (stringBuilder.length() % 8 == 0){
len = stringBuilder.length() / 8;
}else {
len = stringBuilder.length() / 8 + 1;
}
byte[] b = new byte[len];
int index = 0 ;
for (int i = 0; i < stringBuilder.length(); i += 8) {
if (i+8 > stringBuilder.length()){
//最后一个字符未必有八位
b[index] = (byte) Integer.parseInt(stringBuilder.substring(i),2);
}else {
b[index] = (byte) Integer.parseInt(stringBuilder.substring(i, i + 8),2);
}
index++;
}
return b;
}
}
class Node2 implements Comparable<Node2>{
//存储数据
public Byte date;
//出现次数
public int count;
public Node2 left;
public Node2 right;
public Node2(Byte date,int count){
this.date = date;
this.count = count;
}
@Override
public String toString() {
return "Node2{" +
"date=" + date +
", count=" + count +
'}';
}
@Override
public int compareTo(Node2 o) {
return this.count - o.count;
}
public void presort(){
System.out.println(this);
if (this.left != null){
this.left.presort();
}
if (this.right != null){
this.right.presort();
}
}
}
二叉排序树
增删改查速度快
//二叉排序树 其中序遍历就是升序遍历
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7,3,10,12,5,1,9,2};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i < arr.length; i++) {
binarySortTree.add(new Node(arr[i]));
}
binarySortTree.deleNode(2);
binarySortTree.deleNode(5);
binarySortTree.deleNode(9);
binarySortTree.deleNode(12);
binarySortTree.deleNode(7);
binarySortTree.deleNode(3);
binarySortTree.deleNode(10);
binarySortTree.deleNode(1);
System.out.println(binarySortTree.getRoot());
binarySortTree.midsort();
}
}
class BinarySortTree{
public Node root;
public Node getRoot() {
return root;
}
public void add(Node node){
if (root == null){
root = node;
}else {
root.add(node);
}
}
public void midsort(){
if (root == null){
System.out.println("空树");
}else {
root.midsort();
}
}
//寻找节点
public Node search(int value){
if(root == null){
System.out.println("空树");
return null;
}else {
return root.search(value);
}
}
//寻找父节点
public Node Parentsearch(int value){
if (root.value == value){
return null;
}else {
return root.Parentsearch(value);
}
}
public void deleNode(int value){
if (root == null){
return;
}else {
//要删除的元素
Node search = search(value);
if (search == null){
//没有该数
return;
}else {
if (root.left == null && root.right == null){
//找到了要删除的数 而且 根节点没有子树 说明要删的就是根节点
root = null;
return;
}else {
Node parentsearch = Parentsearch(value);
if (search.right == null && search.left == null){
//删除叶子节点
if (parentsearch.left != null && parentsearch.left.value == value){
parentsearch.left = null;
}else if (parentsearch.right != null && parentsearch.right.value == value){
parentsearch.right = null;
}
}else if (search.right != null && search.left != null){
//有两个子树的数 找该数的右子树上最小值 或者左子树的最大值
int minfind = minfind(search.right);
search.value = minfind;
}else {
//单子树的数
if (search.left != null){
//左子树不为空
if (parentsearch != null){
if (parentsearch.left != null && parentsearch.left.value == value){
parentsearch.left = search.left;
}else if (parentsearch.right != null && parentsearch.right.value == value){
parentsearch.right = search.left;
}
}else {
//删除root
root = search.left;
}
}else {
//右子树不为空
if (parentsearch != null){
if (parentsearch.left != null && parentsearch.left.value == value){
parentsearch.left = search.right;
}else if (parentsearch.right != null && parentsearch.right.value == value){
parentsearch.right = search.right;
}
}else {
root = search.right;
}
}
}
}
}
}
}
public int minfind(Node node){
Node tar = node;
while (tar.left != null){
tar = tar.left;
}
deleNode(tar.value);
return tar.value;
}
}
class Node{
public int value;
public Node left;
public Node right;
public Node(int value){
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
public void add(Node node){
if (node == null){
System.out.println("node为空");
return;
}else {
if (node.value > this.value){
if (this.right == null){
this.right = node;
}else {
this.right.add(node);
}
}else {
if (this.left == null){
this.left = node;
}else {
this.left.add(node);
}
}
}
}
public void midsort(){
if (this.left != null){
this.left.midsort();
}
System.out.println(this);
if (this.right != null){
this.right.midsort();
}
}
//寻找节点
public Node search(int value){
if (value == this.value){
return this;
}else if (value < this.value){
if (this.left == null){
return null;
}
return this.left.search(value);
}else {
if (this.right == null){
return null;
}
return this.right.search(value);
}
}
//寻找父节点
public Node Parentsearch(int value){
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
}else if (value < this.value){
if (this.left != null){
return this.left.Parentsearch(value);
}
}else if (value >= this.value){
if (this.right != null){
return this.right.Parentsearch(value);
}
}else {
return null;
}
return null;
}
}
平衡二叉树(AVL)
//左旋转
public void leftRotate(){
Node newNode = new Node(value);
newNode.left = left;
newNode.right = right.left;
value = right.value;
left = newNode;
right = right.right;
}
//右旋转
public void rightRotate(){
Node newNode = new Node(value);
newNode.right = right;
newNode.left = left.right;
value = left.value;
right = newNode;
left = left.left;
}
双向旋转
public class Test{
public static void main(String[] args) {
// int[] arr = {4,3,6,5,7,8};
// int[] arr = {10,12,8,9,7,6};
int[] arr = { 10, 11, 7, 6, 8, 9 };
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
Node root = avlTree.getRoot();
// System.out.println(root.height());
// System.out.println(root.leftHeight());
// System.out.println(root.rightHeight());
System.out.println(avlTree.height());
System.out.println(avlTree.leftHeight(root));
System.out.println(avlTree.rightHeight(root));
}
}
class AVLTree{
public Node root;
public Node getRoot() {
return root;
}
public void add(Node node){
if (root == null){
root = node;
}else {
root.add(node);
}
}
public void midsort(){
if (root == null){
System.out.println("空树");
}else {
root.midsort();
}
}
//寻找节点
public Node search(int value){
if(root == null){
System.out.println("空树");
return null;
}else {
return root.search(value);
}
}
//寻找父节点
public Node Parentsearch(int value){
if (root.value == value){
return null;
}else {
return root.Parentsearch(value);
}
}
//删除节点
public void deleNode(int value){
if (root == null){
return;
}else {
//要删除的元素
Node search = search(value);
if (search == null){
//没有该数
return;
}else {
if (root.left == null && root.right == null){
//找到了要删除的数 而且 根节点没有子树 说明要删的就是根节点
root = null;
return;
}else {
Node parentsearch = Parentsearch(value);
if (search.right == null && search.left == null){
//删除叶子节点
if (parentsearch.left != null && parentsearch.left.value == value){
parentsearch.left = null;
}else if (parentsearch.right != null && parentsearch.right.value == value){
parentsearch.right = null;
}
}else if (search.right != null && search.left != null){
//有两个子树的数 找该数的右子树上最小值 或者左子树的最大值
int minfind = minfind(search.right);
search.value = minfind;
}else {
//单子树的数
if (search.left != null){
//左子树不为空
if (parentsearch != null){
if (parentsearch.left != null && parentsearch.left.value == value){
parentsearch.left = search.left;
}else if (parentsearch.right != null && parentsearch.right.value == value){
parentsearch.right = search.left;
}
}else {
//删除root
root = search.left;
}
}else {
//右子树不为空
if (parentsearch != null){
if (parentsearch.left != null && parentsearch.left.value == value){
parentsearch.left = search.right;
}else if (parentsearch.right != null && parentsearch.right.value == value){
parentsearch.right = search.right;
}
}else {
root = search.right;
}
}
}
}
}
}
}
public int minfind(Node node){
Node tar = node;
while (tar.left != null){
tar = tar.left;
}
deleNode(tar.value);
return tar.value;
}
//树高度
public int height(){
if (root == null){
return 0;
}else {
return root.height();
}
}
//左树高度
public int leftHeight(Node node){
if (node == null){
return 0;
}else {
return node.left.height();
}
}
//右子树高度
public int rightHeight(Node node){
if (node == null){
return 0;
}else {
return node.right.height();
}
}
}
class Node{
public int value;
public Node left;
public Node right;
public Node(int value){
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
public int height(){
return Math.max((this.left == null) ? 0 : this.left.height(),(this.right == null) ? 0 : this.right.height()) + 1;
}
public int leftHeight(){
if (left == null){
return 0;
}
return this.left.height();
}
public int rightHeight(){
if (right == null){
return 0;
}
return this.right.height();
}
public void add(Node node){
if (node == null){
System.out.println("node为空");
return;
}else {
if (node.value > this.value){
if (this.right == null){
this.right = node;
}else {
this.right.add(node);
}
}else {
if (this.left == null){
this.left = node;
}else {
this.left.add(node);
}
}
//右子树高度 - 左子树高度 大于1 左旋转
if (rightHeight() - leftHeight() > 1){
//如果它的右子树的左子树的高度大于它的右子树的右子树的高度
if (right != null && right.rightHeight() < right.leftHeight()){
// 先对右子结点进行右旋转
right.rightRotate();
// 然后在对当前结点进行左旋转
leftRotate();
}else {
// 对当前结点进行左旋转
leftRotate();
}
//避免再次进入判断
return;
}
//左子树高度 - 右子树高度 大于1 右旋转
if (leftHeight() - rightHeight() > 1){
if (left != null && left.leftHeight() < left.rightHeight()){
left.leftRotate();
rightRotate();
}else {
rightHeight();
}
}
}
}
//左旋转
public void leftRotate(){
Node newNode = new Node(value);
newNode.left = left;
newNode.right = right.left;
value = right.value;
left = newNode;
right = right.right;
}
//右旋转
public void rightRotate(){
Node newNode = new Node(value);
newNode.right = right;
newNode.left = left.right;
value = left.value;
right = newNode;
left = left.left;
}
public void midsort(){
if (this.left != null){
this.left.midsort();
}
System.out.println(this);
if (this.right != null){
this.right.midsort();
}
}
//寻找节点
public Node search(int value){
if (value == this.value){
return this;
}else if (value < this.value){
if (this.left == null){
return null;
}
return this.left.search(value);
}else {
if (this.right == null){
return null;
}
return this.right.search(value);
}
}
//寻找父节点
public Node Parentsearch(int value){
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
}else if (value < this.value){
if (this.left != null){
return this.left.Parentsearch(value);
}
}else if (value >= this.value){
if (this.right != null){
return this.right.Parentsearch(value);
}
}else {
return null;
}
return null;
}
}
多路查找树
二叉树的问题:虽然操作效率较高,但是在面对大数据量的时候需要多次操作IO并且节点海量
为了解决该问题提出了多叉树
允许每个节点有多个子节点(大于两个)就是多叉树 2-3树 2-3-4树
图
//图
public class Graph {
private ArrayList<String> verList;
private int[][] arr;
private int num;
public static void main(String[] args) {
int n = 5; //结点的个数
String Vertexs[] = {"A", "B", "C", "D", "E"};
// String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
//创建图对象
Graph graph = new Graph(n);
//循环的添加顶点
for(String vertex: Vertexs) {
graph.add(vertex);
}
// 添加边
// A-BA-C B-C B-D B-E
graph.addarr(0, 1, 1); //A-B
graph.addarr(0, 2, 1); //
graph.addarr(1, 2, 1); //
graph.addarr(1, 3, 1); //
graph.addarr(1, 4, 1); //
graph.showarr();
}
public Graph(int n){
verList = new ArrayList<>(n);
arr = new int[n][n];
num = 0;
}
//添加节点
public void add(String val){
verList.add(val);
}
//添加路径
public void addarr(int i,int j,int weight){
arr[i][j] = weight;
arr[j][i] = weight;
num++;
}
public int getNum(){
return num;
}
public String getval(int index){
return verList.get(index);
}
public int getweight(int i,int j){
return arr[i][j];
}
public void showarr(){
for (int[] item: arr) {
System.out.println(Arrays.toString(item));
}
}
}
图的深度优先遍历
每次遍历该节点的相邻节点,在遍历该相邻节点的相邻节点,找不到该相邻节点时返回上一节点找下一个相邻节点
//遍历一个节点的所以路径 深度优先遍历
private void dfs(boolean[] isVisit,int i){
//输出得到的节点
System.out.print(getval(i));
isVisit[i] = true;
int w = getFirstNeighbor(i);
while (w > 0) {
//有下一个连接节点
if (!isVisit[w]){
//没有被访问过 还没有找到合适路径 没有输出 遍历该节点的所有路径
dfs(isVisit, w);
}
w = getNextNeighbor(i,w);
}
}
//遍历所有节点
public void dfs(){
for (int i = 0; i < verList.size(); i++) {
if (!isVisit[i]){
//未输出
dfs(isVisit,i);
}
}
//将isvisit重新至false
isVisit = new boolean[verList.size()];
}
图的广度优先遍历
一次遍历一个节点的全部路径,遍历完一个节点后,从队列中取出下一个节点再次遍历
//单节点的广度优先遍历
public void bfs(boolean[] isVisit,int i){
LinkedList<Object> objects = new LinkedList<>();
//将下标加入队列中
objects.addLast(i);
//输出节点
System.out.print(getval(i));
isVisit[i] = true;
int first;
int next;
//队列不为空 取一为首 进行广度优先遍历
while (!objects.isEmpty()){
first = (Integer) objects.removeFirst();
next = getFirstNeighbor(first);
while (next != -1){
if (!isVisit[next]){
// 未访问
System.out.print(getval(next));
isVisit[next] = true;
objects.addLast(next);
}
//找first这一层 next的下一个节点
next = getNextNeighbor(first,next);
}
}
}
public void bfs(){
for (int i = 0; i < verList.size(); i++) {
if (!isVisit[i]){
bfs(isVisit,i);
}
}
//将isvisit重新至false
isVisit = new boolean[verList.size()];
}
代码汇总
//图
public class Graph {
//节点
private ArrayList<String> verList;
//连接的边
private int[][] arr;
//是否遍历过
private boolean[] isVisit;
//路径数
private int num;
public static void main(String[] args) {
int n = 8; //结点的个数
// String Vertexs[] = {"A", "B", "C", "D", "E"};
String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
//创建图对象
Graph graph = new Graph(n);
//循环的添加顶点
for(String vertex: Vertexs) {
graph.add(vertex);
}
// 添加边
// A-BA-C B-C B-D B-E
// graph.addarr(0, 1, 1); //A-B
// graph.addarr(0, 2, 1); //
// graph.addarr(1, 2, 1); //
// graph.addarr(1, 3, 1); //
// graph.addarr(1, 4, 1); //
graph.addarr(0, 1, 1);
graph.addarr(0, 2, 1);
graph.addarr(1, 3, 1);
graph.addarr(1, 4, 1);
graph.addarr(3, 7, 1);
graph.addarr(4, 7, 1);
graph.addarr(2, 5, 1);
graph.addarr(2, 6, 1);
graph.addarr(5, 6, 1);
graph.showarr();
System.out.println("深度优先遍历");
graph.dfs();
System.out.println();
System.out.println("广度优先遍历");
graph.bfs();
}
public Graph(int n){
verList = new ArrayList<>(n);
arr = new int[n][n];
isVisit = new boolean[n];
num = 0;
}
//添加节点
public void add(String val){
verList.add(val);
}
//添加路径
public void addarr(int i,int j,int weight){
arr[i][j] = weight;
arr[j][i] = weight;
num++;
}
/**
* 寻找第一个连接节点的下标
* @param index 那个字母的下标
* @return
*/
public int getFirstNeighbor(int index){
for (int i = 0; i < verList.size(); i++) {
if (arr[index][i] > 0 && isVisit[i] != true){
return i;
}
}
return -1;
}
// 根据前一个节点的下标 寻找下一个节点的下标
public int getNextNeighbor(int v1,int v2){
for (int i = v2 + 1; i < verList.size(); i++) {
if (arr[v1][i] > 0){
return i;
}
}
return -1;
}
//遍历一个节点的所以路径 深度优先遍历
private void dfs(boolean[] isVisit,int i){
//输出得到的节点
System.out.print(getval(i));
isVisit[i] = true;
int w = getFirstNeighbor(i);
while (w > 0) {
//有下一个连接节点
if (!isVisit[w]){
//没有被访问过 还没有找到合适路径 没有输出 遍历该节点的所有路径
dfs(isVisit, w);
}
w = getNextNeighbor(i,w);
}
}
//遍历所有节点
public void dfs(){
for (int i = 0; i < verList.size(); i++) {
if (!isVisit[i]){
//未输出
dfs(isVisit,i);
}
}
//将isvisit重新至false
isVisit = new boolean[verList.size()];
}
//单节点的广度优先遍历
public void bfs(boolean[] isVisit,int i){
LinkedList<Object> objects = new LinkedList<>();
//将下标加入队列中
objects.addLast(i);
//输出节点
System.out.print(getval(i));
isVisit[i] = true;
int first;
int next;
//队列不为空 取一为首 进行广度优先遍历
while (!objects.isEmpty()){
first = (Integer) objects.removeFirst();
next = getFirstNeighbor(first);
while (next != -1){
if (!isVisit[next]){
// 未访问
System.out.print(getval(next));
isVisit[next] = true;
objects.addLast(next);
}
//找first这一层 next的下一个节点
next = getNextNeighbor(first,next);
}
}
}
public void bfs(){
for (int i = 0; i < verList.size(); i++) {
if (!isVisit[i]){
bfs(isVisit,i);
}
}
//将isvisit重新至false
isVisit = new boolean[verList.size()];
}
public int getNum(){
return num;
}
public String getval(int index){
return verList.get(index);
}
public int getweight(int i,int j){
return arr[i][j];
}
public void showarr(){
for (int[] item: arr) {
System.out.println(Arrays.toString(item));
}
}
}
程序员的十大常用算法
二分查找的非递归
//二分查找的非递归
public class Binarysearch {
public static void main(String[] args) {
int[] arr = {1,4,5,7,8,9,100};
System.out.println(binarysearch(arr,8));
}
public static int binarysearch(int[] arr, int key){
int left = 0;
int right = arr.length - 1;
while (left <= right){
int mid = (left + right) / 2;
if (arr[mid] == key){
return mid;
}else if (arr[mid] > key){
right = mid - 1;
}else if (key > arr[mid]){
left = mid + 1;
}
}
return -1;
}
}
分治算法
//分治算法 汉罗塔
public class dac {
public static void main(String[] args) {
hanluota(2,'A','B','C');
}
/**
* 用递归将复杂问题拆分最小问题求解
* @param nums 盘总数
* @param a 开始盘
* @param b 中间盘
* @param c 目的盘
*/
public static void hanluota(int nums,char a,char b,char c){
if (nums == 1){
System.out.println("第1个盘子从"+"从"+a+"到"+c);
}else {
//将除最大的盘子 从a移动到b
hanluota(nums - 1, a, c, b);
//将最大的盘子从a移动到c
System.out.println("第"+nums+"个盘子从"+"从"+a+"到"+c);
//将剩下的盘子从b移动到c
hanluota(nums - 1, b, a, c);
}
}
}
动态规划
import java.util.Arrays;
//动态规划 0-1 背包算法 依赖上一次的结果
public class Dynamic {
static int[] val = {1500,3000,2000,2500};
static int[] wei = {1,4,3,2};
//背包表
static int[][] arr ;
//物品表
static int[][] path;
public static void main(String[] args) {
//n=5 背包大小 0,1,2,3,4
arr = new int[val.length + 1][5];
path = new int[val.length + 1][5];
//填表
for (int i = 1; i < arr.length; i++) {
for (int j = 1; j < arr[0].length; j++) {
if (wei[i-1] > j){
//新增物品重量大于总背包重量
arr[i][j] = arr[i-1][j];
}else {
//新增物品重量小于等于总背包重量
// arr[i][j] = max(i,j);
//为了记录存放物品情况
if (arr[i-1][j] > val[i-1] + arr[i-1][j-wei[i-1]]){
arr[i][j] = arr[i-1][j];
}else {
arr[i][j] = val[i-1] + arr[i-1][j-wei[i-1]];
//记录存放
path[i][j] = 1;
}
}
}
}
for (int[] ints : arr) {
System.out.println(Arrays.toString(ints));
}
//0-1背包右下角即最优解
System.out.println("==============================");
int i = path.length - 1;
int j = path[0].length - 1;
while (i > 0 && j > 0){
if (path[i][j] == 1){
System.out.println("放入"+(i));
j -= wei[i-1];
}
i--;
}
}
public static int max(int i ,int j ){
return arr[i-1][j] > val[i-1] + arr[i-1][j-wei[i-1]] ? arr[i-1][j] : val[i-1] + arr[i-1][j-wei[i-1]];
}
}
KMP
暴力匹配
//暴力匹配
public static int violenceMatch(String s1,String s2){
char[] char1 = s1.toCharArray();
char[] char2 = s2.toCharArray();
int s1Len = s1.length();
int s2Len = s2.length();
int i = 0;
int j = 0;
while (i < s1Len && j < s2Len){
if (char1[i] == char2[j]){
i++;
j++;
}else {
// 返回原来位置的前一位
i = i - (j - 1);
j = 0;
}
}
if (j == s2Len){
return i - j;
}else {
return -1;
}
}
KMP 方法算法就利用之前判断过信息,通过一个 next 数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过 next 数组找到,前面匹配过的位置,省去了大量的计算时间
参考资料:很详尽KMP算法(厉害) - ZzUuOo666 - 博客园
import java.util.Arrays;
public class KMP {
public static void main(String[] args) {
String s1 = "BBC ABCDAB ABCDABCDABDE";
String s2 = "ABCDABD";
int[] ints = kmpNext(s2);
int i = kmpSearch(s1, s2, ints);
System.out.println(Arrays.toString(ints));
System.out.println(s1.substring(i));
// int kmp = violenceMatch(s1, s2);
// System.out.println(kmp);
}
public static int kmpSearch(String s1,String s2,int[] ints){
for (int i = 0,j = 0; i < s1.length(); i++) {
while (j > 0 && s1.charAt(i) != s2.charAt(j)){
//公式 匹配失败时按公式将j找到合适位置重新开始
j = ints[j - 1];
}
if (s1.charAt(i) == s2.charAt(j)){
j++;
}
if (j == s2.length()){
//找到了 返回的位置从1开始
return i - j + 1;
}
}
return -1;
}
//获取部分匹配值
public static int[] kmpNext(String s2){
int[] next = new int[s2.length()];
//一个字符的部分匹配值为0
next[0] = 0;
for (int i = 1,j = 0 ; i < next.length; i++) {
//j>0 已经有一个字符匹配成功
while (j > 0 && s2.charAt(i) != s2.charAt(j)){
//公式 匹配失败时按公式找合适位置重新开始
j = next[j-1];
}
if (s2.charAt(i) == s2.charAt(j)){
//所有的前缀以j=0即a开始
j++;
}
next[i] = j;
}
return next;
}
//暴力匹配
public static int violenceMatch(String s1,String s2){
char[] char1 = s1.toCharArray();
char[] char2 = s2.toCharArray();
int s1Len = s1.length();
int s2Len = s2.length();
int i = 0;
int j = 0;
while (i < s1Len && j < s2Len){
if (char1[i] == char2[j]){
i++;
j++;
}else {
// 返回原来位置的前一位
i = i - (j - 1);
j = 0;
}
}
if (j == s2Len){
return i - j;
}else {
return -1;
}
}
}
贪心算法
public class GreedyAlgorithm {
public static void main(String[] args) {
//创建广播电台
HashMap<String, HashSet<String>> broadcasts = new HashMap<>();
//将各个电台放入到 broadcasts
HashSet<String> hashSet1 = new HashSet<String>();
hashSet1.add("北京");
hashSet1.add("上海");
hashSet1.add("天津");
HashSet<String> hashSet2 = new HashSet<String>();
hashSet2.add("广州");
hashSet2.add("北京");
hashSet2.add("深圳");
HashSet<String> hashSet3 = new HashSet<String>();
hashSet3.add("成都");
hashSet3.add("上海");
hashSet3.add("杭州");
HashSet<String> hashSet4 = new HashSet<String>();
hashSet4.add("上海");
hashSet4.add("天津");
HashSet<String> hashSet5 = new HashSet<String>();
hashSet5.add("杭州");
hashSet5.add("大连");
//加入到 map
broadcasts.put("K1", hashSet1);
broadcasts.put("K2", hashSet2);
broadcasts.put("K3", hashSet3);
broadcasts.put("K4", hashSet4);
broadcasts.put("K5", hashSet5);
//allAreas 存放所有的地区
HashSet<String> allAreas = new HashSet<String>();
allAreas.add("北京");
allAreas.add("上海");
allAreas.add("天津");
allAreas.add("广州");
allAreas.add("深圳");
allAreas.add("成都");
allAreas.add("杭州");
allAreas.add("大连");
//创建 ArrayList, 存放选择的电台集合
ArrayList<String> strings = new ArrayList<>();
//定义一个临时的集合, 在遍历的过程中,存放遍历过程中的电台覆盖的地区和当前还没有覆盖的地区的交集
HashSet<String> tempSet = new HashSet<>();
//每次比较的最大值
int max = 0;
String maxKey = null;
while (allAreas.size() > 0){
// 循环后制空
max = 0;
//遍历广播电台
for (String key : broadcasts.keySet()) {
//清空临时储存的广播
tempSet.clear();
//获取广播
HashSet<String> areas = broadcasts.get(key);
//加入临时set
tempSet.addAll(areas);
//可纳入广播数
tempSet.retainAll(allAreas);
//tempSet.size() > 0 有未纳入的广播
if (tempSet.size() > 0 && (maxKey == null || tempSet.size() > max)){
max = tempSet.size();
maxKey = key;
}
}
if (maxKey != null){
strings.add(maxKey);
allAreas.removeAll(broadcasts.get(maxKey));
maxKey = null;
}
}
System.out.println(strings);
}
}
普利姆算法
将线路问题替换成图,通过访问过和未访问过遍历双节点比较权值,找到最短路径
//普利姆算法 将线路问题替换成图,通过访问过和未访问过遍历双节点比较权值,找到最短路径
public class prim {
public static void main(String[] args) {
//测试看看图是否创建 ok
char[] data = new char[]{'A','B','C','D','E','F','G'};
int verxs = data.length;
//邻接矩阵的关系使用二维数组表示,10000 这个大数,表示两个点不联通
int [][]weight=new int[][]{
{10000,5,7,10000,10000,10000,2},
{5,10000,10000,9,10000,10000,3},
{7,10000,10000,10000,8,10000,10000},
{10000,9,10000,10000,10000,4,10000},
{10000,10000,8,10000,10000,5,4},
{10000,10000,10000,4,5,10000,6},
{2,3,10000,10000,4,6,10000},};
//创建 MGraph 对象
MGraph graph = new MGraph(verxs);
//创建一个 MinTree 对象
MinTree minTree = new MinTree();
minTree.creatGraph(graph, verxs, data, weight);
//输出
minTree.show(graph);
minTree.prim(graph,0);
}
}
class MinTree{
//初始化图
public void creatGraph(MGraph mGraph,int size,char[] date,int[][] weight ){
for (int i = 0; i < size; i++) {
mGraph.date[i] = date[i];
for (int j = 0; j < size; j++) {
mGraph.weight[i][j] = weight[i][j];
}
}
}
public void show(MGraph mGraph){
for (int[] ints : mGraph.weight) {
System.out.println(Arrays.toString(ints));
}
}
//从那个顶点开始
public void prim(MGraph mGraph,int v){
int[] visit = new int[mGraph.size];
visit[v] = 1;
//保存最小权值
int min = 1000;
//记录要连接的两个节点
int k1 = 0;
int k2 = 0;
// 七个顶点需要6条边
for (int k = 1; k < mGraph.size; k++) {
for (int i = 0; i < mGraph.size; i++) {
for (int j = 0; j < mGraph.size; j++) {
//遍历已访问的节点i 寻找周围未访问的节点j 两者最小的路径 并保存下标
if (visit[i] == 1 && visit[j] == 0 && min > mGraph.weight[i][j]){
k1 = i;
k2 = j;
min = mGraph.weight[i][j];
}
}
}
//找到一条边
System.out.println("边 <"+mGraph.date[k1]+","+mGraph.date[k2]+"> 权值 "+min);
//设置已访问
visit[k2] = 1;
//重置为一个大数
min = 1000;
}
}
}
class MGraph{
//存放边
int[][] weight;
//存放数据
char[] date;
int size;
public MGraph(int size){
this.size = size;
date = new char[size];
weight = new int[size][size];
}
}
克鲁斯卡尔算法
按权值从小到大在不产生回路的情况下,将结果加入结果集
import java.util.Arrays;
//克鲁斯卡尔算法
public class Kruskal {
private int edgeNum; //边的个数
private char[] vertexs; //顶点数组
private int[][] matrix; //邻接矩阵
//使用 INF 表示两个顶点不能连通
private static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
//克鲁斯卡尔算法的邻接矩阵
int matrix[][] = {
/*A*//*B*//*C*//*D*//*E*//*F*//*G*/
/*A*/ { 0, 12, INF, INF, INF, 16, 14},
/*B*/ { 12, 0, 10, INF, INF, 7, INF},
/*C*/ { INF, 10, 0, 3, 5, 6, INF},
/*D*/ { INF, INF, 3, 0, 4, INF, INF},
/*E*/ { INF, INF, 5, 4, 0, 2, 8},
/*F*/ { 16, 7, 6, INF, 2, 0, 9},
/*G*/ { 14, INF, INF, INF, 8, 9, 0}};
//大家可以在去测试其它的邻接矩阵,结果都可以得到最小生成树.
//创建 KruskalCase 对象实例
Kruskal kruskalCase = new Kruskal(vertexs, matrix);
//输出构建的
kruskalCase.print();
kruskalCase.kruskal();
}
//构造器
public Kruskal(char[] vertexs, int[][] matrix) {
//初始化顶点数和边的个数
int vlen = vertexs.length;
//初始化顶点, 复制拷贝的方式
this.vertexs = new char[vlen];
for(int i = 0; i < vertexs.length; i++) {
this.vertexs[i] = vertexs[i];
}
//初始化边, 使用的是复制拷贝的方式 与原来的数组区别开
this.matrix = new int[vlen][vlen];
for(int i = 0; i < vlen; i++) {
for(int j= 0; j < vlen; j++) {
this.matrix[i][j] = matrix[i][j];
}
}
//统计边的条数 数一半
for(int i =0; i < vlen; i++) {
for(int j = i+1; j < vlen; j++) {
if(this.matrix[i][j] != INF) {
edgeNum++;
}
}
}
}
public void kruskal() {
int index = 0; //表示最后结果数组的索引
int[] ends = new int[edgeNum]; //用于保存"已有最小生成树" 中的每个顶点在最小生成树中的终点
//创建结果数组, 保存最后的最小生成树
EData[] rets = new EData[edgeNum];
//获取图中 所有的边的集合 , 一共有 12 边
EData[] edges = getEdges();
System.out.println("图的边的集合=" + Arrays.toString(edges) + " 共"+ edges.length); //12
//按照边的权值大小进行排序(从小到大)
sortEdges(edges);
//遍历 edges 数组,将边添加到最小生成树中时,判断是准备加入的边否形成了回路,如果没有,就加入 rets,否则不能加入
for(int i=0; i < edgeNum; i++) {
//获取到第 i 条边的第一个顶点(起点)
int p1 = getPosition(edges[i].start); //p1=4
//获取到第 i 条边的第 2 个顶点
int p2 = getPosition(edges[i].end); //p2 = 5
//获取 p1 这个顶点在已有最小生成树中的终点
int m = getEnd(ends, p1); //m = 4
//获取 p2 这个顶点在已有最小生成树中的终点
int n = getEnd(ends, p2); // n = 5
//是否构成回路
if(m != n) { //没有构成回路
ends[m] = n; // 设置 m 在"已有最小生成树"中的终点 <E,F> [0,0,0,0,5,0,0,0,0,0,0,0]
rets[index++] = edges[i]; //有一条边加入到 rets 数组
}
}
//<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。
//统计并打印 "最小生成树", 输出 rets
System.out.println("最小生成树为");
for(int i = 0; i < index; i++) {
System.out.println(rets[i]);
}
}
//打印邻接矩阵
public void print() {
System.out.println("邻接矩阵为: \n");
for(int i = 0; i < vertexs.length; i++) {
for(int j=0; j < vertexs.length; j++) {
System.out.printf("%12d", matrix[i][j]);
}
System.out.println();//换行
}
}
/**
* 功能:对边进行排序处理, 冒泡排序
* @param edges 边的集合
*/
private void sortEdges(EData[] edges) {
for(int i = 0; i < edges.length - 1; i++) {
for(int j = 0; j < edges.length - 1 - i; j++) {
if(edges[j].weight > edges[j+1].weight) {//交换
EData tmp = edges[j];
edges[j] = edges[j+1];
edges[j+1] = tmp;
}
}
}
}
/**
*
* @param ch 顶点的值,比如'A','B'
* @return 返回 ch 顶点对应的下标,如果找不到,返回-1
*/
private int getPosition(char ch) {
for(int i = 0; i < vertexs.length; i++) {
if(vertexs[i] == ch) {//找到
return i;
}
}
//找不到,返回-1
return -1;
}
/**
* 功能: 获取图中边,放到 EData[] 数组中,后面我们需要遍历该数组
* 是通过 matrix 邻接矩阵来获取
* EData[] 形式 [['A','B', 12], ['B','F',7], .....]
* @return
*/
private EData[] getEdges() {
int index = 0;
EData[] edges = new EData[edgeNum];
for(int i = 0; i < vertexs.length; i++) {
for(int j=i+1; j <vertexs.length; j++) {
if(matrix[i][j] != INF) {
edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
}
}
}
return edges;
}
/**
* 功能: 获取下标为 i 的顶点的终点(), 用于后面判断两个顶点的终点是否相同
* @param ends : 数组就是记录了各个顶点对应的终点是哪个,ends 数组是在遍历过程中,逐步形成
* @param i : 表示传入的顶点对应的下标
* @return 返回的就是 下标为 i 的这个顶点对应的终点的下标, 一会回头还有来理解
*/
private int getEnd(int[] ends, int i) { // i = 4 [0,0,0,0,5,0,0,0,0,0,0,0]
while(ends[i] != 0) {
i = ends[i];
}
return i;
}
}
class EData {
char start; //边的一个点
char end; //边的另外一个点
int weight; //边的权值
//构造器
public EData(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
//重写 toString, 便于输出边信息
@Override
public String toString() {
return "EData [<" + start + ", " + end + ">= " + weight + "]";
}
}
迪杰斯特拉算法
//迪杰斯特拉算法
public class Dijkstra {
public static void main(String[] args) {
char[] chars = {'A','B','C','D','E','F','G'};
int[][] arr = new int[chars.length][chars.length];
final int N = 65535;
arr[0]=new int[]{N,5,7,N,N,N,2};
arr[1]=new int[]{5,N,N,9,N,N,3};
arr[2]=new int[]{7,N,N,N,8,N,N};
arr[3]=new int[]{N,9,N,N,N,4,N};
arr[4]=new int[]{N,N,8,N,N,5,4};
arr[5]=new int[]{N,N,N,4,5,N,6};
arr[6]=new int[]{2,3,N,N,4,6,N};
Graph graph = new Graph(arr,chars);
// graph.show();
graph.dsj(6);
graph.showDijkstra();
}
}
//图
class Graph{
//顶点
public char[] chars;
//临接矩阵
public int[][] arr;
public VisitedVertex vv;
public Graph(int[][] arr, char[] chars){
this.chars = chars;
this.arr = arr;
}
public void show(){
for (int[] ints : arr) {
System.out.println(Arrays.toString(ints));
}
}
//显示结果
public void showDijkstra() {
vv.show();
}
public void dsj(int index){
vv =new VisitedVertex(index,arr.length);
update(index);
for (int i = 1; i < arr.length; i++) {
index = vv.updateArr();
update(index);
}
}
//更新 index 下标顶点到周围顶点的距离和周围顶点的前驱顶点,
public void update(int index){
int len = 0;
for (int i = 0; i < arr[index].length; i++) {
len = vv.getDis(index) + arr[index][i];
if (!vv.in(i) && len < vv.getDis(i)){
vv.updateDis(i,len);
vv.updatePre(i,index);
}
}
}
}
class VisitedVertex{
// 记录各个顶点是否访问过 1 表示访问过,0 未访问,会动态更新
public int[] already_arr;
// 每个下标对应的值为前一个顶点下标, 会动态更新
public int[] pre_visited;
// 记录出发顶点到其他所有顶点的距离,比如 G 为出发顶点,就会记录 G 到其它顶点的距离,会动态更新,求的最短距离就会存放到 dis
public int[] dis;
/**
*
* @param index 出发的下标
* @param len 顶点的个数
*/
public VisitedVertex(int index,int len){
already_arr = new int[len];
pre_visited = new int[len];
dis = new int[len];
Arrays.fill(dis,65535);
this.dis[index] = 0;
this.already_arr[index] = 1;
}
//判断是否访问过
public boolean in(int index){
return already_arr[index] == 1;
}
//更新出发顶点到 index 顶点的距离
public void updateDis(int index,int len){
dis[index] = len;
}
//更新 pre 这个顶点的前驱顶点为 index 顶点
public void updatePre(int pre,int index){
pre_visited[pre] = index;
}
public int getDis(int index){
return dis[index];
}
//找到已经访问过但未更新的节点
public int updateArr(){
int min = 65535,index = 0;
//循环遍历已经访问过的数组
for (int i = 0; i < already_arr.length; i++) {
if (already_arr[i] == 0 && dis[i] < min){
//dis[i] != min已经访问过的数组但未更新的节点
min = dis[index];
index = i;
}
}
already_arr[index] = 1;
return index;
}
//显示最后的结果
//即将三个数组的情况输出
public void show() {
System.out.println("==========================");
//输出 already_arr
for(int i : already_arr) {
System.out.print(i + " ");
}
System.out.println();
//输出 pre_visited
for(int i : pre_visited) {
System.out.print(i + " ");
}
System.out.println();
//输出 dis
for(int i : dis) {
System.out.print(i + " ");
}
System.out.println();
//为了好看最后的最短距离,我们处理
char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
int count = 0;
for (int i : dis) {
if (i != 65535) {
System.out.print(vertex[count] + "("+i+") ");
} else {
System.out.println("N ");
}
count++;
}
System.out.println();
}
}
弗洛伊德算法
//弗洛伊德算法
public class Floyd {
public static void main(String[] args) {
// 测试看看图是否创建成功
char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
//创建邻接矩阵
int[][] matrix = new int[vertex.length][vertex.length];
final int N = 65535;
matrix[0] = new int[] { 0, 5, 7, N, N, N, 2 };
matrix[1] = new int[] { 5, 0, N, 9, N, N, 3 };
matrix[2] = new int[] { 7, N, 0, N, 8, N, N };
matrix[3] = new int[] { N, 9, N, 0, N, 4, N };
matrix[4] = new int[] { N, N, 8, N, 0, 5, 4 };
matrix[5] = new int[] { N, N, N, 4, 5, 0, 6 };
matrix[6] = new int[] { 2, 3, N, N, 4, 6, 0 };
//创建 Graph 对象
Graph2 graph = new Graph2(vertex.length, matrix, vertex);
//调用弗洛伊德算法
graph.floyd();
graph.show();
}
}
class Graph2{
//保存节点的数组
private char[] vertex;
// 保存,从各个顶点出发到其它顶点的距离,最后的结果,也是保留在该数组
private int[][] dis;
//保存目的节点的前驱节点 也就是中间节点
private int[][] pre;
public Graph2(int len,int[][] dis,char[] vertex){
this.vertex = vertex;
this.dis = dis;
pre = new int[len][len];
for (int i = 0; i < len; i++) {
Arrays.fill(pre[i],i);
}
}
// 显示 pre 数组和 dis 数组
public void show() {
//为了显示便于阅读,我们优化一下输出
char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
for (int k = 0; k < dis.length; k++) {
// 先将 pre 数组输出的一行
for (int i = 0; i < dis.length; i++) {
System.out.print(vertex[pre[k][i]] + " ");
}
System.out.println();
// 输出 dis 数组的一行数据
for (int i = 0; i < dis.length; i++) {
System.out.print("("+vertex[k]+"到"+vertex[i]+"的最短路径是" + dis[k][i] + ") ");
}
System.out.println();
System.out.println();
}
}
public void floyd(){
int len = 0;
//中间节点
for (int i = 0; i < vertex.length; i++) {
//开始
for (int j = 0; j < vertex.length; j++) {
//目的
for (int k = 0; k < vertex.length; k++) {
len = dis[j][i] + dis[i][k];
if (len < dis[j][k]){
dis[j][k] = len;
pre[j][k] = pre[i][k];
}
}
}
}
}
}
马踏棋盘算法
//马踏棋盘算法
public class horse {
public static int X;
public static int Y;
//判断每个位置是否访问过
public static boolean visited[];
//最后结果
public static boolean finished;
public static void main(String[] args) {
System.out.println("骑士周游算法,开始运行~~");
X = 8;
Y = 8;
int row = 1;
int col = 1;
int[][] chess = new int[X][Y];
visited = new boolean[X * Y];
//测试一下耗时
long start = System.currentTimeMillis();
traverborad(chess, row - 1, col - 1, 1);
long end = System.currentTimeMillis();
System.out.println("共耗时: " + (end - start) + " 毫秒");
//输出棋盘的最后情况
for(int[] rows : chess) {
for(int step: rows) {
System.out.print(step + "\t");
}
System.out.println();
}
}
/**
*
* @param chess 棋盘
* @param row 行
* @param col 列
* @param step 步数 从1开始
*/
public static void traverborad(int[][] chess,int row,int col,int step){
//记录步数
chess[row][col] = step;
visited[row * X + col] = true;
ArrayList<Point> points = next(new Point(row,col));
while (!points.isEmpty()){
//取出一个点
sort(points);
Point remove = points.remove(0);
if (!visited[remove.y * X + remove.x]){
traverborad(chess, remove.y, remove.x, step + 1);
}
}
//finished=true一路绿灯回溯到头,不会陷入死递归
if (step < X * Y && !finished){
//未完成
chess[row][col] = 0;
visited[row * X + col] = false;
}else {
finished = true;
}
}
//返回下一个位置的集合
public static ArrayList<Point> next(Point nowPoint){
ArrayList<Point> points = new ArrayList<>();
Point p1 = new Point();
// 5
if ((p1.x = nowPoint.x - 2) >= 0 && (p1.y = nowPoint.y - 1) >= 0){
points.add(new Point(p1));
}
//6
if ((p1.x = nowPoint.x - 1) >= 0 && (p1.y = nowPoint.y - 2) >= 0){
points.add(new Point(p1));
}
//7
if ((p1.x = nowPoint.x + 1) < X && (p1.y = nowPoint.y - 2) >= 0){
points.add(new Point(p1));
}
//0
if ((p1.x = nowPoint.x + 2) < X && (p1.y = nowPoint.y - 1) >= 0){
points.add(new Point(p1));
}
//1
if ((p1.x = nowPoint.x + 2) < X && (p1.y = nowPoint.y + 1) < Y){
points.add(new Point(p1));
}
//2
if ((p1.x = nowPoint.x + 1) < X && (p1.y = nowPoint.y + 2) < Y){
points.add(new Point(p1));
}
//3
if ((p1.x = nowPoint.x - 1) >= 0 && (p1.y = nowPoint.y + 2) < Y){
points.add(new Point(p1));
}
//4
if ((p1.x = nowPoint.x - 2) >= 0 && (p1.y = nowPoint.y + 1) < Y){
points.add(new Point(p1));
}
return points;
}
//根据当前这个一步的所有的下一步的选择位置,进行非递减排序, 减少回溯的次数
public static void sort(ArrayList<Point> ps) {
ps.sort(new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
// TODOAuto-generated method stub
//获取到 o1 的下一步的所有位置个数
int count1 = next(o1).size();
//获取到 o2 的下一步的所有位置个数
int count2 = next(o2).size();
if(count1 < count2) {
return -1;
} else if (count1 == count2) {
return 0;
} else {
return 1;
}
}
});
}
}
需要老师文档的可以自行提取
链接:https://pan.baidu.com/s/1HbH-e76Ki3ADYqWBnaQk8g
提取码:z213
也可以观看韩老师视频