记录棋盘的位置 可用用二位数组将它保存 ,但是会发现记录很多没有意义的数据很多空间浪费 可以用稀疏数组进行优化
稀疏数组就是压缩缩多余的冗余数据
第一行的记录原表的行列数和 非0值的个数
package com.luyi.DataStructures.sparse;
import java.util.Date;
/**
* @author 卢意
* @create 2020-12-01 9:26
*/
public class SparseArray {
public static void main(String[] args) {
// 创建一个原始的二维数组 11 * 11
// 0 表示 没有棋子 1表示黑子 2表示白子
int chessArray[][] = new int[11][11];
chessArray[1][2] = 1;
chessArray[2][3] = 2;
// 输出原始的二维数组
System.out.println("输出原始的二维数组");
for(int row[] : chessArray){
for (int data : row){
System.out.print(data + "\t");
}
System.out.println();
}
// 将二维数组转为稀疏数组的思路
// 1 先遍历 二维数组 得到 非0 数据的个数
int sum = 0;
for(int i = 0; i < chessArray.length; i++){
for(int j = 0; j<chessArray.length; j++ ){
if(chessArray[i][j] != 0){
sum++;
}
}
}
System.out.println("非0数据的个数 sum="+ sum);
// 2 创建对应的稀疏数组
int sparseArray[][] = new int[sum+1][3];
// 给稀疏数组赋值
sparseArray[0][0] = 11;
sparseArray[0][1] = 11;
sparseArray[0][2] = sum;
// 遍历二维数组 将非0的值存放到稀疏数组中
int index = 1;
for(int i = 0; i < chessArray.length; i++){
for(int j = 0; j<chessArray.length; j++ ){
if(chessArray[i][j] != 0){
sparseArray[index][0] = i;
sparseArray[index][1] = j;
sparseArray[index][2] = chessArray[i][j];
index++;
}
}
}
// 输出稀疏数组
System.out.println("得到的稀疏数组为:");
for (int i = 0;i<sum+1;i++){
System.out.print(sparseArray[i][0] + "\t");
System.out.print(sparseArray[i][1] + "\t");
System.out.print(sparseArray[i][2] + "\t");
System.out.println();
}
// 稀疏数组恢复成二维数组
// 1 读取稀疏数组的第一行 根据第一行的数据, 创建原始的二维数组
// 2 在读取稀疏数组的后几行 并赋值
int row = sparseArray[0][0];
int cos = sparseArray[0][1];
int newChessArray[][] = new int[row][cos];
for(int i = 0; i < row; i++){
for(int j = 0; j < cos; j++ ){
newChessArray[i][j] = 0;
}
}
for (int i = 1; i < sparseArray.length;i++){
newChessArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
System.out.println("输出恢复的二维数组");
for(int rows[] : chessArray){
for (int data : rows){
System.out.print(data + "\t");
}
System.out.println();
}
}
}
输出原始的二维数组
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
非0数据的个数 sum=2
得到的稀疏数组为:
11 11 2
1 2 1
2 3 2
输出恢复的二维数组
0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
上图的清晰图 rear待表队列的尾部 front代表数据的头部 存入数据时 front不变 rear变化 存数据时 rear位置不变 front 的位置在变化
rear == maxSize-1 为满 front指向队列头部的头一个位置 rear指向队列尾部的具体位置
package com.luyi.DataStructures.queue;
import java.util.Scanner;
/**
* @author 卢意
* @create 2020-12-01 10:08
* 数组模拟队列
*/
public class ArrayQueueDemo {
public static void main(String[] args) {
// 测试
// 创建一个队列
ArrayQueue arrayQueue = new ArrayQueue(3);
char key = ' '; // 接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true; // 控制循环
// 输出一个菜单
while (loop){
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':
arrayQueue.showQueue();
break;
case 'a':
System.out.println("输入一个数");
int value = scanner.nextInt();
arrayQueue.addQueue(value);
break;
case 'g':
try {
System.out.println("取出的数据:"+arrayQueue.getQueue());
} catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
System.out.println("队列头部的数据:"+arrayQueue.headQueue());
} catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default :
break;
}
}
System.out.println("程序退出");
}
}
// 使用数组模拟队列 编写一个ArrayQueue类
class ArrayQueue {
private int maxSize; // 表示数组的最大容量
private int front; // 队列头部
private int rear; // 队列尾部
private int[] arr; // 存放数据 模拟队列
// 创建队列的构造器
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
arr= new int[maxSize];
front = rear = -1; // 队列头部和尾部 front指向队列头部的头一个位置 rear指向队列尾部的具体位置
}
// 判断队列是否满
public Boolean isFull(){
return rear == maxSize - 1;
}
// 判断对列是否为空
public Boolean isEmpty(){
return rear == front;
}
// 添加数据到对列
public void addQueue(int n){
// 判断队列是否满
if(isFull()){
System.out.println("队列满,不能加入数据");
return;
}
// rear 后移
arr[++rear] = n;
}
// 数据出队列
public int getQueue(){
// 判断对垒是否空
if(isEmpty()){
throw new RuntimeException("队列为空不能取数据");
}
return arr[++front];
}
// 显示对列的所有数据
public void showQueue(){
// 判断队列是否为空
if(isEmpty()){
System.out.println("队列为空");
return;
}
for (int i = 0; i < arr.length; i++){
System.out.println("arr["+i+"]="+arr[i]);
}
}
// 显示对列的头部的数据 不是取出数据
public int headQueue(){
// 判断是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,没有头数据");
}
return arr[front + 1];
}
}
控制台结果
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
队列为空
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
arr[0]=10
arr[1]=0
arr[2]=0
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
20
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
arr[0]=10
arr[1]=20
arr[2]=30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
40
队列满,不能加入数据
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
h
队列头部的数据:10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
h
队列头部的数据:20
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:20
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
队列为空不能取数据
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
h
队列为空,没有头数据
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
现在队列经历了加满 也全部取出 当前列表理论上为空
但是任然无法加入数据 这个数组无法复用 只能使用一次 没有到达复用的效果
需要将这个数组由算法改造成一个环形数组(取模)
package com.luyi.DataStructures.queue;
import java.util.Scanner;
/**
* 环形数组对列
* @author 卢意
* @create 2020-12-01 13:54
*/
public class CircleArrayQueue {
public static void main(String[] args) {
// 测试
// 创建一个环形队列
CircleArray arrayQueue = new CircleArray(4); // 设置为4 但是数组的有效空间是3
char key = ' '; // 接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true; // 控制循环
// 输出一个菜单
while (loop){
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':
arrayQueue.showQueue();
break;
case 'a':
System.out.println("输入一个数");
int value = scanner.nextInt();
arrayQueue.addQueue(value);
break;
case 'g':
try {
System.out.println("取出的数据:"+arrayQueue.getQueue());
} catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h':
try {
System.out.println("队列头部的数据:"+arrayQueue.headQueue());
} catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default :
break;
}
}
System.out.println("程序退出");
}
}
// 使用数组模拟队列 编写一个ArrayQueue类
class CircleArray {
private int maxSize; // 表示数组的最大容量
// front 的变量的含义 做调整 就指向队列的第一个元素 也就是 arr[front] 是第一个头元素
// front初始值变为0
private int front; // 队列头部
// rear 变量的含义做一个调整 rear指向队列的最后元素的后一个位置 因为希望空出一个空间做约定 rear的初始值为0
private int rear; // 队列尾部
private int[] arr; // 存放数据 模拟队列
// 创建队列的构造器
public CircleArray(int maxSize){
this.maxSize = maxSize;
arr= new int[maxSize];
//front = rear = 0 ; // 队列头部和尾部 front指向队列头部的头一个位置 rear指向队列尾部的具体位置
}
// 判断队列是否满
public boolean isFull(){
return (rear + 1)% maxSize == front;
}
// 判断对列是否为空
public boolean isEmpty(){
return rear == front;
}
// 添加数据到对列
public void addQueue(int n){
// 判断队列是否满
if(isFull()){
System.out.println("队列满,不能加入数据");
return;
}
// rear指向对位的后一个位置 直接将数据加入 在后移rear指针
arr[rear] = n;
// 要考虑取模 如果rear已经在 数组的最后位置 需要进行取模到数组的开始部分
rear = (rear + 1) % maxSize;
}
// 数据出队列
public int getQueue(){
// 判断对垒是否空
if(isEmpty()){
throw new RuntimeException("队列为空不能取数据");
}
// 这里需要分析出front是 指向队列的第一个元素
// 1 先把 front对应的值 保存到临时变量
// 2 将front 后移
// 3 将临时保存的变量返回
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
// 显示对列的所有数据
public void showQueue(){
// 判断队列是否为空
if(isEmpty()){
System.out.println("队列为空");
return;
}
// 思路: 从front开始遍历, 遍历多少个元素
//
for (int i = front; i < front + size(); i++){
System.out.println("arr["+ i % maxSize +"]="+arr[i % maxSize]);
}
}
// 显示对列的头部的数据 不是取出数据
public int headQueue(){
// 判断是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,没有头数据");
}
return arr[front];
}
// 求出当前队列的有效数据的个数
public int size() {
return (rear + maxSize - front) % maxSize;
}
}
运行结果
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
队列为空
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a 20
输入一个数
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
arr[0]=10
arr[1]=20
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
arr[0]=10
arr[1]=20
arr[2]=30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
输入一个数
40
队列满,不能加入数据
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
arr[1]=20
arr[2]=30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a 10
输入一个数
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
s
arr[1]=20
arr[2]=30
arr[3]=10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a 70
输入一个数
队列满,不能加入数据
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
h
队列头部的数据:20
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:20
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:30
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
取出的数据:10
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
g
队列为空不能取数据
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a 50
输入一个数
s(show): 显示对列
e(exit): 退出程序
a(add): 添加数据到对列
g(get): 从对列取出数据
h(head): 查看对列头的数据
a
小结上图:
- 链表是以节点的方式来存储, 是链式存储
- 每个节点包含 data 域, next 域:指向下一个节点.
- 如图:发现 链表的各个节点不一定是连续存储.
- 链表分 带头节点的链表和 没有头节点的链表,根据实际的需求来确定
思路(1) 先找到该节点,通过遍历,(2) temp.name = newHeroNode.name ; temp.nickname= newHeroNode.nickname
package com.luyi.DataStructures.linkedlist;
/**
* @author 卢意
* @create 2020-12-01 21:10
*/
public class SingleLinkedListDemo {
public static void main(String[] args) {
// 先创建节点
HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨");
HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星");
HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头");
// 创建一个链表
SingleLinkedList singleLinkedList = new SingleLinkedList();
// 加入
// singleLinkedList.add(heroNode1);
// singleLinkedList.add(heroNode2);
// singleLinkedList.add(heroNode3);
// singleLinkedList.add(heroNode4);
/**
* 结果:
* HeroNode{no=1, name='宋江', nickName='及时雨'}
* HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
* HeroNode{no=3, name='吴用', nickName='智多星'}
* HeroNode{no=4, name='林冲', nickName='豹子头'}
*/
// 加入按照编号的顺序
singleLinkedList.addByOrder(heroNode1);
singleLinkedList.addByOrder(heroNode4);
singleLinkedList.addByOrder(heroNode2);
singleLinkedList.addByOrder(heroNode3);
singleLinkedList.addByOrder(heroNode3);
/**
* 结果
* 准备插入的英雄的编号[3]已存在,不能加入!
* HeroNode{no=1, name='宋江', nickName='及时雨'}
* HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
* HeroNode{no=3, name='吴用', nickName='智多星'}
* HeroNode{no=4, name='林冲', nickName='豹子头'}
*/
// 显示
singleLinkedList.list();
// 测试 修改节点
HeroNode newHeroNode = new HeroNode(2, "小卢", "小麒麟");
HeroNode newHeroNode1 = new HeroNode(5, "小卢", "小麒麟");
singleLinkedList.update(newHeroNode );
singleLinkedList.update(newHeroNode1 );
/**
* 结果
* HeroNode{no=1, name='宋江', nickName='及时雨'}
* HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
* HeroNode{no=3, name='吴用', nickName='智多星'}
* HeroNode{no=4, name='林冲', nickName='豹子头'}
* 没有找到编号为[5]的节点
* 修改后的链表情况
*
* HeroNode{no=1, name='宋江', nickName='及时雨'}
* HeroNode{no=2, name='小卢', nickName='小麒麟'}
* HeroNode{no=3, name='吴用', nickName='智多星'}
* HeroNode{no=4, name='林冲', nickName='豹子头'}
*/
// 修改后的显示
System.out.println("修改后的链表情况");
System.out.println();
singleLinkedList.list();
System.out.println("删除一个节点");
singleLinkedList.delete(1);
singleLinkedList.delete(2);
singleLinkedList.delete(3);
singleLinkedList.delete(4);
singleLinkedList.list();
/**
* 结果
* 删除一个节点
* 链表为空
*/
}
}
// 定义一个单向链表 管理我们的英雄
class SingleLinkedList {
// 初始化一下头结点 头结点不要动 不存放具体的数据
private HeroNode head =new HeroNode(0,null,null);
// 添加方法到单向链表
// 思路 当不考虑编号顺序时
// 1. 找到当前链表的最后节点
// 2. 将最后的这个节点的next 指向 新节点
public void add(HeroNode heroNode){
// 因为 head节点不能动 我们需要一个辅助节点 temp
HeroNode temp = head;
// 遍历链表找到最后
while (true){
// 找到链表的最后
if(temp.next == null){
break;
}
// 如果没有找到 就将temp后移
temp = temp.next;
}
// 当退出white循环时, temp 就指向了链表的最后
temp.next = heroNode;
}
// 第二种添加英雄的方式, 根据排名将英雄插入到指定位置
// 如果有这个排名, 则添加失败 并给出提示
public void addByOrder(HeroNode heroNode){
// 因为头结点不能动 因此我们任然通过一个辅助指针(变量) 来帮助找到添加的位置
// 因为是单列表 temp是要找到添加节点的前一个节点, 否则插入不了
HeroNode temp = head;
boolean flag = false; // 标志添加的编号是否存在 默认为false
while(true) {
if(temp.next == null) { // 说明已经到了链表的最后了
break;
}
if (temp.next.no > heroNode.no) { // 位置找到了 在temp和temp.next之间插入该节点
break;
}else if (temp.next.no == heroNode.no) { // 说明希望添加的heroNode的编号已经存在 不能添加
flag = true; // 说明编号存在
break;
}
temp = temp.next;
}
// 判断 flag的值
if (flag){ // 不能添加 说明编号存在
System.out.println("准备插入的英雄的编号["+heroNode.no+"]已存在,不能加入!");
}else {
// 插入链表中 temp 中
heroNode.next = temp.next;
temp.next = heroNode;
}
}
// 显示链表 遍历
public void list(){
// 判断链表为空
if(head.next == null){
System.out.println("链表为空");
return;
}
// 因为头结点不能动我们需要一个复制节点 temp
HeroNode temp = head.next;
while(true){
// 输出这个节点的信息
System.out.println(temp);
if(temp.next == null){
break;
}
// 将temp 后移 不然是一个死循环
temp = temp.next;
}
}
// 修改节点的信息 根据编号来修改 编号不能修改
public void update (HeroNode newHeroNode){
if(head.next == null){
System.out.println("链表为空");
return;
}
// 找到需要修改的节点 很具no查询
HeroNode temp = head.next;
boolean flag = false; // 表示是否找到该节点
while (true) {
if (temp == null) {
break; // 表示链表已经遍历结束了 (不是最后一个节点 已经出到外面去了)
}
if (temp.no == newHeroNode.no){
// 找到要修改的节点
flag = true;
break;
}
temp = temp.next;
}
// 根据flag判断是否找到要修改的节点
if(!flag){
System.out.println("没有找到编号为["+newHeroNode.no+ "]的节点");
}else {
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}
}
// 删除节点
//思路 1.head节点不能动 因此我们需要一个temp辅助节点找到删除节点的前一个节点
// 2. 我们需要以temp节点的后一个节点的标号进行比较
public void delete(int no){
HeroNode temp = head;
boolean flag = false; // 标志是否找到待删除节点的
while (true) {
if (temp.next == null){ // temp已经到链表的最后
break;
}
if (temp.next.no == no){
// 找到了待删除节点的前一个节点
flag = true;
break;
}
temp = temp.next;
}
if (flag){
// 可以删除
HeroNode t = temp.next;
temp.next = t.next;
t.next = null;
}else {
System.out.println("没有找到该no=["+no+"]节点,无法删除");
}
}
}
// 定义一个HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickName;
public HeroNode next; // 指向下一个节点
// 构造器
public HeroNode(int hNo, String hName, String hNickName){
this.no = hNo;
this.name = hName;
this.nickName = hNickName;
}
// 为了显示方便 重写toString方法
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
/**
*
* @param head 链表的头节点
* @return 返回的是有效节点个数
*/
public Integer getLength(HeroNode head){
if (head.next == null){
// 空链表
return 0;
}
int length = 0;
// 定义一个辅助变量
HeroNode cur =head.next;
while (cur != null) {
length++;
cur = cur.next;
}
return length;
}
// 求单链表的倒数第K个节点
// 思路 1 编写一个方法接收head节点 接收index(倒数第index节点)
// 2 先把链表变历 求出长度 length 去找到length-index的节点
public HeroNode findNodeStartLast(HeroNode head ,int index){
// 如果链表为空 返回null
if (head.next == null){
return null;
}
// 变历得到链表的长度
int length = this.getLength(head);
// 再一次遍历 返回 第length - index
// 先做一个index的校验
if(index <= 0 || index > length){
return null;
}
// 定义一个辅助节点
HeroNode temp = head.next;
for(int i = 0 ;i < length-index ;i++ ){
temp = temp.next;
}
return temp;
}
// 单链表的反转
public HeroNode reverseNode(HeroNode head){
if (head.next == null){
return head;
}
HeroNode reverseHeroNode = new HeroNode(0, null, null);
while (head.next != null){
if (reverseHeroNode.next == null){
reverseHeroNode.next = head.next;
head.next = head.next.next;
reverseHeroNode.next.next = null;
}else {
HeroNode temp = reverseHeroNode.next;
reverseHeroNode.next = head.next;
head.next = head.next.next;
reverseHeroNode.next.next = temp;
}
}
head.next = reverseHeroNode.next;
return head;
}
// 使用栈的方式 实现逆序打印单链表
public static void reversePrint(HeroNode head){
if(head.next == null) {
System.out.println("队列为空,无法逆序打印单链表");
return;
}
HeroNode temp = head;
Stack<HeroNode> heroNodeStack = new Stack<>();
while (temp.next != null){
heroNodeStack.add(temp.next);
temp = temp.next;
}
while (heroNodeStack.size() > 0){
System.out.println(heroNodeStack.pop());
}
}
package com.luyi.DataStructures.linkedlist;
/**
* @author 卢意
* @create 2020-12-02 22:06
*/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
System.out.println("双向链表的一个测试");
// 先创建节点
HeroNode2 heroNode1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 heroNode2 = new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 heroNode3 = new HeroNode2(3, "吴用", "智多星");
HeroNode2 heroNode4 = new HeroNode2(4, "林冲", "豹子头");
//创建一个双向链表对象
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.add(heroNode1);
doubleLinkedList.add(heroNode2);
doubleLinkedList.add(heroNode3);
doubleLinkedList.add(heroNode4);
doubleLinkedList.list();
// 修改测试
System.out.println();
System.out.println("修改测试");
HeroNode2 newHeroNode = new HeroNode2(4, "公孙胜", "如云龙");
doubleLinkedList.update(newHeroNode);
doubleLinkedList.list();
// 测试删除
System.out.println("删除测试");
doubleLinkedList.delete(3);
doubleLinkedList.list();
// 有序添加测试
System.out.println();
System.out.println("有序添加测试");
HeroNode2 newHeroNode2 = new HeroNode2(0, "小工", "公公");
doubleLinkedList.addOrder(newHeroNode2);
doubleLinkedList.list();
}
}
// 创建一个双向链表的类
class DoubleLinkedList {
// 初始化一下头结点 头结点不要动 不存放具体的数据
private HeroNode2 head =new HeroNode2(0,null,null);
public HeroNode2 getHead() {
return head;
}
public void setHead(HeroNode2 head) {
this.head = head;
}
// 遍历双向链表的方法和单链表的方法一样
// 显示链表 遍历
public void list(){
// 判断链表为空
if(head.next == null){
System.out.println("链表为空");
return;
}
// 因为头结点不能动我们需要一个复制节点 temp
HeroNode2 temp = head.next;
while(true){
// 输出这个节点的信息
System.out.println(temp);
if(temp.next == null){
break;
}
// 将temp 后移 不然是一个死循环
temp = temp.next;
}
}
// 这个方法默认添加到双向链表的尾部
public void add(HeroNode2 heroNode){
// 因为 head节点不能动 我们需要一个辅助节点 temp
HeroNode2 temp = head;
// 遍历链表找到最后
while (true){
// 找到链表的最后
if(temp.next == null){
break;
}
// 如果没有找到 就将temp后移
temp = temp.next;
}
// 当退出white循环时, temp 就指向了链表的最后
// 形成一个双向链表
temp.next=heroNode;
heroNode.pre = temp;
}
// 按顺序添加双链表
void addOrder(HeroNode2 heroNode) {
HeroNode2 temp = head.next;
boolean flag = false; // 标识是否可以插入
if (head.next == null){
heroNode.pre = head;
head.next = heroNode;
}else {
while (true) {
if (temp.no == heroNode.no){
System.out.println("该编号已经存在 无法添加");
flag = true;
break;
}else if (temp.no > heroNode.no){
break;
}
if(temp.next == null){
break;
}
temp = temp.next;
}
if (!flag){
heroNode.next = temp;
heroNode.pre = temp.pre;
temp.pre.next=heroNode;
temp.pre=heroNode;
}
}
}
// 修改一个双向链表的接单的内容 和单向链表一样 只是节点的类型发生了改变
public void update (HeroNode2 newHeroNode){
if(head.next == null){
System.out.println("链表为空");
return;
}
// 找到需要修改的节点 很具no查询
HeroNode2 temp = head.next;
boolean flag = false; // 表示是否找到该节点
while (true) {
if (temp == null) {
break; // 表示链表已经遍历结束了 (不是最后一个节点 已经出到外面去了)
}
if (temp.no == newHeroNode.no){
// 找到要修改的节点
flag = true;
break;
}
temp = temp.next;
}
// 根据flag判断是否找到要修改的节点
if(!flag){
System.out.println("没有找到编号为["+newHeroNode.no+ "]的节点");
}else {
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}
}
// 从双向链表中删除一个节点
// 对于双向链表可以直接找到要删除的节点 不用像单链表要找到要删除节点的前一个节点
// 找到后 自我删除即可
public void delete(int no){
HeroNode2 temp = head.next;
// 判断当前链表是否为空
if (temp == null) {
System.out.println("当前链表为空 无法删除");
}
boolean flag = false; // 标志是否找到待删除节点的
while (true) {
if (temp.no == no){
// 找到了待删除节点的前一个节点
flag = true;
break;
}
temp = temp.next;
}
if (flag){
// 可以删除
// temp.next = temp.next.next; [单链表]
temp.pre.next = temp.next;
if (temp.next != null) {
// 如果删除的不是最后一个节点
temp.next.pre = temp.pre;
}
}else {
System.out.println("没有找到该no=["+no+"]节点,无法删除");
}
}
}
// 定义一个HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode2 {
public int no;
public String name;
public String nickName;
public HeroNode2 next; // 指向下一个节点 ,默认为null
public HeroNode2 pre; // 指向上一个节点 ,默认为null
// 构造器
public HeroNode2(int hNo, String hName, String hNickName){
this.no = hNo;
this.name = hName;
this.nickName = hNickName;
}
// 为了显示方便 重写toString方法
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
package com.luyi.DataStructures.linkedlist;
import com.sun.xml.internal.ws.wsdl.writer.document.soap.Body;
import jdk.nashorn.internal.ir.IfNode;
/**
* 约瑟夫问题
* @author 卢意
* @create 2020-12-03 16:49
*/
public class Joseph {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(25);
circleSingleLinkedList.solveJoseph(1, 2, 25);
//circleSingleLinkedList.list();
}
}
// 创建一个环形的单项链表
class CircleSingleLinkedList {
// 创建一个first节点, 当前没有编号 等等在设置编号
private Boy first = new Boy(-1);
// 添加小孩的节点 构建成一个环形链表
public void addBoy(int nums) {
// nums 做一个数据校验
if (nums < 1 ) {
System.out.println("nums的值不正确");
return;
}
Boy currentBoy = null; // 辅助指针 帮助构建环形链表
// 使用一个for循环创建环形链表
for (int i =1; i <= nums; i++) {
// 根据编号 创建小孩节点
Boy newBoy = new Boy(i);
// 如果是第一个小孩
if (i == 1){
first = newBoy;
first.setNext(first); // 构成一个环
currentBoy = first; // 让currentBoy指向第一个小孩 first节点不能动
}else {
currentBoy.setNext(newBoy);
newBoy.setNext(first);
currentBoy = newBoy;
}
}
}
// 遍历当前环形链表
public void list(){
Boy currentBoy = first;
if (currentBoy.getNo() == -1){
System.out.println("当前环形链表为空,无法遍历");
return;
}
while (true) {
System.out.println("小孩编号"+currentBoy.getNo());
currentBoy = currentBoy.getNext();
if (currentBoy == first){
break;
}
}
}
// 根据用户的输入 计算小孩出圈的顺序
/**
*
* @param startNo 表示从第几个小孩开始数数
* @param countNum 表示数几下
* @param num 表示最初有多少个小孩
* @return
*/
public void solveJoseph(int startNo, int countNum, int num){
// 先对数据进行校验
if (first.getNo() == -1 || startNo < 1 || startNo > num){
System.out.println("参数输入有误,请重新输入");
return;
}
// 创建辅助指针 helper 帮助完成小孩出圈
Boy helper = first;
// 先让helper 指向环形队列的最后一个节点
while (true) {
if (helper.getNext() == first) {
break;
}
helper = helper.getNext();
}
//当小孩报数之前 先让first 和helper 移动 startNo-1次
for (int j = 0; j < startNo-1; j++) {
first = first.getNext();
helper = helper.getNext();
}
// 当小孩报数时, 让first和helper 指针同时的移动 countNum - 1 次, 然后出圈
// 这里是一个循环操作 , 直到圈里只有一个节点
while (true) {
if (helper == first) { // 当前圈中只有一个人
System.out.print("最后出圈: "+first.getNo()+"");
break;
}
for (int i = 0; i < countNum -1; i++ ) {
first = first.getNext();
helper = helper.getNext();
}
// 这是后first指向的小孩 出圈
System.out.print(first.getNo()+" ");
first = first.getNext();
helper.setNext(first);
}
}
}
// 创建一个Boy类 标识一个节点
class Boy {
private int no; // 编号
private Boy next; // 指向下一个节点,默认为null
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;
}
}
package com.luyi.DataStructures.stack;
import java.lang.reflect.Array;
import java.util.LinkedList;
import java.util.Stack;
/**
* @author 卢意
* @create 2020-12-03 19:54
*/
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack =new ArrayStack(4);
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println("遍历栈");
stack.list();
}
}
// 定义一个 ArrayStack 表示栈
class ArrayStack {
private int maxSize; // 栈的大小
private int[] stack; // 数组, 数组模拟栈 将栈的数据放入数组
private int top = -1; //表示栈顶 初始化为-1
// 构造器
public ArrayStack (int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
// 栈满判断
public boolean isFull() {
return top == (maxSize-1);
}
// 栈空
public boolean isEmpty() {
return top == -1;
}
// 入栈的操作
public void push (int value) {
// 判断是否满
if (isFull()){
System.out.println("栈已经满了,无法入栈");
return;
}
top++;
stack[top] = value;
}
// 出栈 将栈顶的数据返回
public int pop () {
// 先判断栈是否空
if (isEmpty()){
throw new RuntimeException("栈空,没有数据");
}
return stack[top--];
}
// 显示栈信息(遍历栈) 遍历时 需要从栈顶开始显示
public void list() {
if (isEmpty()) {
System.out.println("栈空,没有数据");
return;
}
int i = top;
while (i >= 0){
System.out.println(stack[i--]);
}
}
}
package com.luyi.DataStructures.stack;
import java.awt.print.Pageable;
import java.util.Stack;
/**
* @author 卢意
* @create 2020-12-03 20:48
*/
public class Calculator {
public static void main(String[] args) {
// 根据前面的思路 完成 表达式的运算
String expression = "7*2*2-5+1-5+3-4";
// 创建两个栈, 数栈, 一个符号转
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
// 定义需要的相关变量
int index = 0; // 用于扫描
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' '; // 将每次扫描的char 保存到ch
String keepNum = ""; // 用于拼接多位数的
// 开始用 while循环的扫描语句 扫描 Expression
while (true) {
// 依次得到Expression 的每一个字符
ch = expression.substring(index, index + 1).charAt(0);
// 判断是否是数字和符号 做相应的处理
if (operStack.isOper(ch)) { // 如果是运算符
// 判断当前的符号栈 是否为空
if (!operStack.isEmpty()) {
// 如果符号栈有操作符 就进行比较 如果当前的操作符的优先级小于等于栈中的操作符,就需要从数栈中pop出两个数
// 再从符号栈中pop出一个符号 ,进行运算,将得到的结果,入数栈 然后将 当前的操作符入符号栈
if (operStack.priority(ch) <= operStack.priority(operStack.peak())) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
// 把运算的结果 入数栈
numStack.push(res);
// 把当前的操作符入符号栈
operStack.push(ch);
}else {
// 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈
operStack.push(ch);
}
}else {
operStack.push(ch);
}
}else { // 如果是数字 则直接入数栈 注意这是字符 不是真正的数字
// numStack.push(ch - 48); 这样子有问题 13 + 1 => '1' '3' '+' '1'
// 在处理多位数时, 不能发现是一个数就立即入栈 因为他可能是多位数
// 在处理数,需要向expression的表达式的index 后再看一位 , 如果是数 就进行扫描, 如果是符号才入栈
// 因此我们需要定义一个变量 keepNum字符串 ,用于拼接多位数
//处理多为数
keepNum += ch;
// 如果ch是expression的最后一位 直接入栈
if (index == expression.length()-1){
numStack.push(ch - 48);
}else {
// 判断下一个字符 是不是数组 ,就继续扫描 ,如果是运算符 则入栈
// 只看后一位 不是index++
if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
// 如果后一位是运算符 则数字入栈
numStack.push(Integer.parseInt(keepNum));
// 重要的!!!!!! 清空keepNum
keepNum = "";
}
}
}
// index +1 并判断是否扫描到最后
index++;
if (index >= expression.length()) {
break;
}
}
// 当表达式扫描完毕, 就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行
while (true) {
// 如果符号栈为空,则计算到最后的结果 数字栈 只有一个结果
if (operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
numStack.push(res);
}
System.out.println(expression+" = "+res);
}
}
// 先创建一个栈 ,直接使用前面创建好的 需要扩展功能
// 定义一个 ArrayStack 表示栈
class ArrayStack2 {
private int maxSize; // 栈的大小
private int[] stack; // 数组, 数组模拟栈 将栈的数据放入数组
private int top = -1; //表示栈顶 初始化为-1
// 构造器
public ArrayStack2 (int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
// 栈满判断
public boolean isFull() {
return top == (maxSize-1);
}
// 栈空
public boolean isEmpty() {
return top == -1;
}
// 入栈的操作
public void push (int value) {
// 判断是否满
if (isFull()){
System.out.println("栈已经满了,无法入栈");
return;
}
top++;
stack[top] = value;
}
// 出栈 将栈顶的数据返回
public int pop () {
// 先判断栈是否空
if (isEmpty()){
throw new RuntimeException("栈空,没有数据");
}
return stack[top--];
}
// 显示栈信息(遍历栈) 遍历时 需要从栈顶开始显示
public void list() {
if (isEmpty()) {
System.out.println("栈空,没有数据");
return;
}
int i = top;
while (i >= 0){
System.out.println(stack[i--]);
}
}
// 查看栈顶的值 不出栈
public int peak() {
if (top == -1) {
System.out.println("为空栈");
return -1;
}
return stack[top];
}
// 返回运算符的有限及 , 假定 返回值的数字越大 优先级越高
public int priority(int oper){
if (oper == '*' || oper == '/') { // java可以数字和字符比
return 1;
}else if(oper == '+' || oper == '-') {
return 0;
}else {
return -1; // 嘉定目前计算式子中 只有加减乘除
}
}
// 判断是不是一个操作符
public boolean isOper(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
// 计算方法
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;
default:break;
}
return res;
}
}
package com.luyi.DataStructures.stack;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
/**
* @author 卢意
* @create 2020-12-07 8:45
*/
public class PolandNotation {
public static void main(String[] args) {
// 先定义一个 逆波兰表达式
//(3+4)*5-6 => 3 4 + 5 * 6 -
// 为了方便 中间用空格隔开
String suffixExpression = "3 4 + 5 * 6 -";
// 1 现将suffixExpression 放到一个ArrayList中
// 2 将ArrayList 传递一个方法 遍历ArrayList 配合栈 完成计算
List<String> rpnList=getListString(suffixExpression);
System.out.println("rpnList:"+rpnList);
System.out.println("结果为: "+calculator(rpnList) );
}
// 将一个逆波兰表达式 一次将数据和运算符放入一个 ArrayList中
public static List<String> getListString(String suffixExpression) {
// 将suffixExpression 按空格分隔
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>(Arrays.asList(split));
return list;
}
// 完成对逆波兰表达式的运算
// 1.从左至右扫描,将 3 和 4 压入堆栈;
// 2.遇到+运算符,因此弹出 4 和 3(4 为栈顶元素,3 为次顶元素),计算出 3+4 的值,得 7,再将 7 入栈;
// 3.将 5 入栈;
// 4.接下来是×运算符,因此弹出 5 和 7,计算出 7×5=35,将 35 入栈;
// 5.将 6 入栈;
// 6.最后是-运算符,计算出 35-6 的值,即 29,由此得出最终结果
public static int calculator(List<String> ls) {
// 创建一个栈 只需要一个
Stack<String> stack = new Stack<>();
// 遍历 ls
for(String item : ls) {
// 使用一个正则表达式取出数
if(item.matches("\\d+")) { // 匹配的是多位数
// 入栈
stack.push(item);
}else { // 如果是运算符
// pop出两个数并运算
int b = Integer.parseInt(stack.pop());
int a = Integer.parseInt(stack.pop());
int res = 0;
switch (item) {
case "+":
res = a+b;
break;
case "-":
res = a-b;
break;
case "*":
res = a*b;
break;
case "/":
res = a/b;
break;
default:
throw new RuntimeException("符号有问题");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}
package com.luyi.DataStructures.stack;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
/**
* @author 卢意
* @create 2020-12-07 8:45
*/
public class PolandNotation {
public static void main(String[] args) {
// 完成将中缀表达式转成后缀代达式的功能
// 1. 1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -
// 2. 因为直接对Str 进行操作不方便 因此 先将 1+((2+3)*4)-5 转成中缀表达式对应的List
// 即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
// 3. 将得到的中缀表达式的list => 后缀表达式的List
// 4. ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] => ArrayList [1,2,3,+,4,*,+,5]
String expression = "1+((2+3)*4)-5";
System.out.println("中缀表达式:"+ toInfixExpression(expression));
List<String> parseSuffixExpressList = parseSuffixExpressionList(toInfixExpression(expression));
System.out.println("逆波兰表达式:" +parseSuffixExpressList );
//结果
System.out.println("expression:"+expression+"="+ calculator(parseSuffixExpressList));
// 先定义一个 逆波兰表达式
//(3+4)*5-6 => 3 4 + 5 * 6 -
//(30+4)*5-6 => 30 4 + 5 * 6 -
//4*5-8+60+8/2 => 4 5 * 8 - 60 + 8 2 / +
// 为了方便 中间用空格隔开
String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
// 1 现将suffixExpression 放到一个ArrayList中
// 2 将ArrayList 传递一个方法 遍历ArrayList 配合栈 完成计算
List<String> rpnList=getListString(suffixExpression);
System.out.println("rpnList:"+rpnList);
System.out.println("结果为: "+calculator(rpnList) );
}
// 将 中缀表达式转成对应的List
public static List<String> toInfixExpression(String s) {
// 定义一个ArrayList 存放中缀表达式的内容
List<String> arrayList = new ArrayList<String>();
int i = 0;
String str; // 做对多位数的拼接
char c; // 每遍历到一个字符放入c中
do {
// 如果c是一个非数字 直接加入到ls
if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) < 57 ){
arrayList.add(c + "");
i++;
}else { // 如果是一个数 需要考虑多位数的问题
str = ""; // str 置空
while (i < s.length() && (s.charAt(i) >= 48 && s.charAt(i) <= 57)) {
str += c;
i++;
}
arrayList.add(str);
}
}while (i < s.length());
return arrayList;
}
// 将得到的中缀表达式的list => 后缀表达式的List ls=ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
public static List<String> parseSuffixExpressionList(List<String> ls) {
// 定义两个栈
Stack<String> s1 = new Stack<String>(); // 符号栈
// 第二个栈 在整个转换过程中 没有pop操作, 而且后面我们号需要逆序输出 还要逆序输出 我们同 List s2 代替
// Stack s2 = new Stack();
List<String> s2 = new ArrayList<String>();
// 遍历ls
for (String item : ls) {
// 如果是一个数 加入s2
if(item.matches("\\d+")) {
s2.add(item);
}else if (item.equals("(")) {
s1.push(item);
}else if (item.equals(")")) {
//如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这一对括号丢弃
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop(); // 弹出 (
}else {
// 当item的优先级 小于 s1栈顶的运算符的优先级 将 s1 栈顶的运算符弹出并加入到 s2 中,再次转到(4.1)与 s1 中新的栈顶运算符相比较
while (s1.size() != 0 && (Operation.getValue(item) <= Operation.getValue(s1.peek()))) {
s2.add(s1.pop());
}
// 还需要将Item入栈
s1.push(item);
}
}
// 将s1的剩余的运算符加入s2中
while (s1.size() !=0){
s2.add(s1.pop());
}
return s2; // 因为是存在list中 不用逆序 正常输出就是逆波兰表达式
}
// 将一个逆波兰表达式 一次将数据和运算符放入一个 ArrayList中
public static List<String> getListString(String suffixExpression) {
// 将suffixExpression 按空格分隔
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>(Arrays.asList(split));
return list;
}
// 完成对逆波兰表达式的运算
// 1.从左至右扫描,将 3 和 4 压入堆栈;
// 2.遇到+运算符,因此弹出 4 和 3(4 为栈顶元素,3 为次顶元素),计算出 3+4 的值,得 7,再将 7 入栈;
// 3.将 5 入栈;
// 4.接下来是×运算符,因此弹出 5 和 7,计算出 7×5=35,将 35 入栈;
// 5.将 6 入栈;
// 6.最后是-运算符,计算出 35-6 的值,即 29,由此得出最终结果
public static int calculator(List<String> ls) {
// 创建一个栈 只需要一个
Stack<String> stack = new Stack<>();
// 遍历 ls
for(String item : ls) {
// 使用一个正则表达式取出数
if(item.matches("\\d+")) { // 匹配的是多位数
// 入栈
stack.push(item);
}else { // 如果是运算符
// pop出两个数并运算
int b = Integer.parseInt(stack.pop());
int a = Integer.parseInt(stack.pop());
int res = 0;
switch (item) {
case "+":
res = a+b;
break;
case "-":
res = a-b;
break;
case "*":
res = a*b;
break;
case "/":
res = a/b;
break;
default:
throw new RuntimeException("符号有问题");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}
// 编写一个类 Operation 返回一个运算符 对应的优先级
class Operation {
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
// 写一个方法 返回一个优先级数字
public static int getValue(String operation) {
int result = 0;
switch (operation){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("不存在该运算符");
break;
}
return result;
}
}
package com.luyi.DataStructures.recursion;
import com.sun.org.apache.xml.internal.resolver.helpers.PublicId;
/**
* @author 卢意
* @create 2020-12-07 16:01
*/
public class MiGong {
public static void main(String[] args) {
// 创建一个二维数组 模拟迷宫
int[][] map = new int[8][7];
// 使用1表示墙
// 上下置位1
for (int i = 0; i < 7; i++ ) {
map[0][i] = 1;
map[7][i] = 1;
}
//左右置位1
for (int i = 0; i < 8; i++ ) {
map[i][0] = 1;
map[i][6] = 1;
}
//
// 设置挡板 1 表示
map[3][1] = 1;
map[3][2] = 1;
//map[1][2] = 1;
//map[2][2] = 1;
// 输出地图
System.out.println("输出地图");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
// 使用递归回溯 找路 map是引用类型 可以动态改变值 等于公共变量
if(setWay(map, 1, 1)){
// 输出新的地图
System.out.println("小球的路径");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}else {
System.out.println("无法找到");
}
}
// 使用递归回溯给小球找路
// 说明 map是地图
// i,j表示从哪个位置开始出发(i,j)
// 如果小球到map[6][5] = 则说明通路找到
// 约定 当map[i][j] 为0时 表示该点没有走过 1为墙 2为通路(小球行径路径) 3为该位置已经走过 但是走不通
// 再走迷宫时 需要确定一个策略(行径方向 当走不通时往策略的下一个方向) 下->右->上->左 ,如果该点都走不通 在回溯
/**
*
* @param map 地图
* @param i 从哪个位置开始找
* @param j
* @return 如果找到通路 返回true 否则为false
*/
public static boolean setWay(int[][] map, int i, int j) {
if (map[6][5] == 2){
// 说明通路已经找到
return true;
}else {
//如果当前的点还没有走过
if (map[i][j] == 0){
// 按照上述策略 下->右->上->左尝试
map[i][j] = 2; // 假的当前点可以走
if (setWay(map, i + 1, j )){ // 向下走
return true;
}else if (setWay(map,i , j + 1)){ // 向右走
return true;
}else if (setWay(map, i - 1, j)){ // 向上走
return true;
}else if (setWay(map, i,j - 1)){ // 向左走
return true;
}else {
// 都没走通 这个点是个思死路
map[i][j] = 3;
return false;
}
}else { // 肯能为 1是墙 2走过 3不能走
return false;
}
}
}
}
目前比较笨的方法
上下左右四个方向 设置所有的策略方法 (上下左右 、上下右左、上左下右 。。。24种) 都走一次 找到最短的那个策略
package com.luyi.DataStructures.recursion;
/**
* @author 卢意
* @create 2020-12-07 18:25
*/
public class Queen {
// 定义一个max表示有多少个皇后
int max = 8;
static int count = 0;
// 定义一个数组 保存皇后放置位置的结果 arr[8] = {0 , 4, 7, 5, 2, 6, 1, 3}
int[] arr = new int[max];
public static void main(String[] args) {
// 测试一把
Queen queen = new Queen();
queen.check(0); // 放到第一个位置
}
// 编写一个方法, 放置第n个皇后
// 注意 进入check时 都会有一个for循环,因此会有回溯
private void check(int n) {
if(n == max){ // max个换后已经放好了
print();
return;
}
// 依次放皇后 判断是否冲突
for (int i = 0; i < max; i++) {
// 先把当前的皇后放到该行的第一列
arr[n] = i;
// 当放置第那个皇后到i列时 是否冲突
if (judge(n)){ // 不冲突
check(n + 1);
}// 如果冲突则继续执行 arr[n] = i ; 本行后移一个位置
}
}
/**
* 查看 当放置第N个皇后时 , 去检测该皇后是否和前面已经摆放的皇后冲突
* @param n 第n个皇后
* @return
*/
private boolean judge(int n) { // 表示放的第n个皇后
for (int i = 0; i < n; i++){
// arr[i] == arr[n] 判断第n个皇后和第i个皇后是否在同一直线上
// Math.abs(n - i) == Math.abs(arr[n] - arr[i])判断第n个皇后和第i个皇后是否在同一斜线上(是因为当前是一维数组,二维数组需要
// 用其他方法)
// n = 1 代表第二个皇后 如果放在第二列的第二行 则 arr[1] = 1
// Math.abs(1-0) == 1 Math.abs(1 - 0) == Math.abs(1 - 0) 成立
// 不需要判断同一行 因为 n在递增
if (arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[n] - arr[i])){ // Math.abs 是绝对值
return false;
}
}
return true;
}
// 写一个方法 将皇后的位置打印
private void print() {
System.out.print("第"+(++count)+"种: ");
for (int i = 0;i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
结论:
- 2n+20 和 2n 随着 n 变大,执行曲线无限接近, 20 可以忽略
- 3n+10 和 3n 随着 n 变大,执行曲线无限接近, 10 可以忽略
结论:
- 2n^2+3n+10 和 2n^2 随着 n 变大, 执行曲线无限接近, 可以忽略 3n+10
- n^2+5n+20 和 n^2 随着 n 变大,执行曲线无限接近, 可以忽略 5n+20
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始), 依次比较
相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
优化:
因为排序的过程中,各元素不断接近自己的位置, 如果一趟比较下来没有进行过交换 , 就说明序列有序,因此要在
排序过程中设置一个标志 flag 判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排
序写好后,在进行)
package com.luyi.sort;
import java.util.ArrayList;
import java.util.Arrays;
/**
* 冒泡排序
* @author 卢意
* @create 2020-12-08 13:46
*/
public class BubbleSort {
public static void main(String[] args) {
int arr[] = {3,9,-1,10,-2};
// 展示冒泡排序的演变过程 比较arr.length-1 次 就可以得出结果
/**
* 第一趟排序后的数组
* [3, -1, 9, -2, 10]
* 第二趟排序后的数组
* [-1, 3, -2, 9, 10]
* 第三趟排序后的数组
* [-1, -2, 3, 9, 10]
* 第四趟排序后的数组
* [-2, -1, 3, 9, 10]
*/
// 第一趟排序 就是将最大的数字放到最后
int temp = 0; // 临时变量
for(int j = 0; j < arr.length - 1; j++ ){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
System.out.println("第一趟排序后的数组");
System.out.println(Arrays.toString(arr));
// 第二趟排序 , 把第二大的数放在倒数第二位 最后一个数字不参与排序
for(int j = 0; j < arr.length - 2; j++ ){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
System.out.println("第二趟排序后的数组");
System.out.println(Arrays.toString(arr));
// 第三趟排序 , 把第三大的数放在倒数第三位 最后两个数字不参与排序
for(int j = 0; j < arr.length - 3; j++ ){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
System.out.println("第三趟排序后的数组");
System.out.println(Arrays.toString(arr));
// 第四趟排序 , 把第四大的数放在倒数第四位 最后三个数字不参与排序
for(int j = 0; j < arr.length - 4; j++ ){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
System.out.println("第四趟排序后的数组");
System.out.println(Arrays.toString(arr));
}
}
int temp = 0; // 临时变量
for (int i = 0; i < arr.length -1; i++){
for (int j = 0; j< arr.length -1 -i;j++){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
}
System.out.println("冒泡排序后的数组");
System.out.println(Arrays.toString(arr));
int arr[] = {3,9,-1,10,20};
int count = 0; // 标记跑了几趟
int temp = 0; // 临时变量
boolean flag = false; // 标识变量 表示是否进行交换
for (int i = 0; i < arr.length -1; i++){
count++;
for (int j = 0; j< arr.length -1 -i;j++){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
flag = true;
}
}
if (!flag){ // 一次交换都没有发生 代表已经排好序了 不用再去进行n-1趟
break;
}else {
flag = false;
}
}
System.out.println("跑了"+count+"趟");
System.out.println("冒泡排序后的数组");
System.out.println(Arrays.toString(arr));
/**
* 跑了3趟
* 冒泡排序后的数组
* [-1, 3, 9, 10, 20]
*/
package com.luyi.sort;
import com.sun.xml.internal.txw2.output.IndentingXMLFilter;
import java.util.Arrays;
/**
* @author 卢意
* @create 2020-12-08 14:27
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
//selectSort(arr); 步骤分析
selectSortAll(arr);
}
// 选择排序
public static void selectSort(int[] arr){
// 使用逐步推导的方式
// 第一轮
// 原始数组 101,34,119,1
// 第一轮排序 1,34,119,101
int minIndex = 0;
int min = arr[0]; // 初始值
for(int j = 1; j < arr.length;j++){
if (min > arr[j]){
minIndex = j;
min = arr[j];
}
}
int temp = arr[0];
arr[0] = arr[minIndex];
arr[minIndex] = temp;
System.out.println("第一轮排序");
System.out.println(Arrays.toString(arr));
// 第二轮
// 第一轮排序 1, 34, 119, 101
// 第二轮排序 1, 34, 119, 101
min = arr[1]; // 初始值
minIndex = 1;
for(int j = 1; j < arr.length;j++){
if (min > arr[j]){
minIndex = j;
min = arr[j];
}
}
temp = arr[1];
arr[1] = arr[minIndex];
arr[minIndex] = temp;
System.out.println("第二轮排序");
System.out.println(Arrays.toString(arr));
// 第三轮
// 第一轮排序 1, 34, 119, 101
// 第二轮排序 1, 34, 119, 101
// 第二轮排序 1, 34, 101, 119
min = arr[2]; // 初始值
minIndex = 2;
for(int j = 2; j < arr.length;j++){
if (min > arr[j]){
minIndex = j;
min = arr[j];
}
}
temp = arr[2];
arr[2] = arr[minIndex];
arr[minIndex] = temp;
System.out.println("第三轮排序");
System.out.println(Arrays.toString(arr));
/**
* 第一轮排序
* [1, 34, 119, 101]
* 第二轮排序
* [1, 34, 119, 101]
* 第三轮排序
* [1, 34, 101, 119]
*/
}
// 完整代码
public static void selectSortAll(int[] arr){
for (int i = 0; i < arr.length -1; i++){
int min = arr[i];
int minIndex = i;
for (int j = i+1; j < arr.length; j++ ){
if (min > arr[j]){
min = arr[j];
minIndex = j;
}
}
if (minIndex != i) {
int temp = arr[i];
arr[i] = min;
arr[minIndex] = temp;
}
}
System.out.println("选择排序");
System.out.println(Arrays.toString(arr));
}
}
有一群小牛, 考试成绩分别是 101, 34, 119, 1 请从小到大排序
package com.luyi.sort;
import java.util.Arrays;
/**
* @author 卢意
* @create 2020-12-08 15:09
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
//insertSort(arr); 分步骤演示
insertSortAll(arr);
}
// 插入排序
public static void insertSort(int[] arr){
// 使用逐步推导的方式来讲解 便于理解
// 第一轮 101,34,119,1 => 34,101,119,1
// 定义一个待插入的数
int insertVal = arr[1]; // 初始化 因为101当做已经在一个有序数组里了 让待插入数
int insertIndex = 1 -1 ; // 初始化 待插入的数的前一个数
// 给insertVal 找到插入的位置
// insertIndex >= 0 保证插入的位置不越界
// insertVal < arr[insertIndex] 待插入数 还没有找到插入的位置 就需要 insertIndex 后移
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex]; // 101 101 119 1
insertIndex--; // insertIndex >= 0 小于零就终止了
}
// 退出while 循环时 插入位置找到 insertIndex + 1 (因为insertIndex初始化的时候是待插入数的前一个数)
arr[insertIndex+1] = insertVal;
System.out.println("第一轮后");
System.out.println(Arrays.toString(arr));
// 第二轮 34, 101, 119, 1 => 34, 101, 119, 1
// 定义一个待插入的数
insertVal = arr[2]; // 初始化 因为101当做已经在一个有序数组里了 让待插入数
insertIndex = 2 -1 ; // 初始化 待插入的数的前一个数
// 给insertVal 找到插入的位置
// insertIndex >= 0 保证插入的位置不越界
// insertVal < arr[insertIndex] 待插入数 还没有找到插入的位置 就需要 insertIndex 后移
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex];
insertIndex--; // insertIndex >= 0 小于零就终止了
}
// 退出while 循环时 插入位置找到 insertIndex + 1 (因为insertIndex初始化的时候是待插入数的前一个数)
arr[insertIndex+1] = insertVal;
System.out.println("第二轮后");
System.out.println(Arrays.toString(arr));
// 第三轮 34, 101, 119, 1 => 1, 34, 101, 119
// 定义一个待插入的数
insertVal = arr[3]; // 初始化 因为101当做已经在一个有序数组里了 让待插入数
insertIndex = 3 -1 ; // 初始化 待插入的数的前一个数
// 给insertVal 找到插入的位置
// insertIndex >= 0 保证插入的位置不越界
// insertVal < arr[insertIndex] 待插入数 还没有找到插入的位置 就需要 insertIndex 后移
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex]; //
insertIndex--; // insertIndex >= 0 小于零就终止了
}
// 退出while 循环时 插入位置找到 insertIndex + 1 (因为insertIndex初始化的时候是待插入数的前一个数)
arr[insertIndex+1] = insertVal;
System.out.println("第三轮后");
System.out.println(Arrays.toString(arr));
/**
* 第一轮后
* [34, 101, 119, 1]
* 第二轮后
* [34, 101, 119, 1]
* 第三轮后
* [1, 34, 101, 119]
*/
}
// 总的插入排序
public static void insertSortAll(int[] arr){
int insertIndex = 0; // 待插入数据的前一个位置
int insertValue = arr[0];
for (int i = 1; i < arr.length; i++) {
insertIndex = i - 1; // 待插入数据的前一个位置
insertValue = arr[i];
while (insertIndex >=0 && insertValue < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];// 待插入数的前一个数后移
insertIndex --; // 待插入位置
}
// 判断是否需要赋值
if (insertIndex + 1 != i){
arr[insertIndex + 1] = insertValue; // 退出while 循环时 插入位置找到 insertIndex + 1 (因为insertIndex初始化的时候是待插入数的前一个数)
}
}
}
System.out.println("插入排序");
System.out.println(Arrays.toString(arr));
}
}
package com.luyi.sort;
import java.util.Arrays;
/**
* @author 卢意
* @create 2020-12-08 19:45
*/
public class HellSort {
public static void main(String[] args) {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
//分步骤
//hellSort(arr);
hellSortAll(arr);
}
// 使用逐步推到的方式编写希尔排序(交换法)
public static void hellSort(int[] arr){
// 希尔数组的第一轮排序
// 因为第一轮排序 将10个数据分成10/2=5组
int temp = 0;
for (int i = 5; i < arr.length; i++){
// 遍历各组中 所有的元素(共五组,每组2个元素) 步场为5 如第一个数字和第5个数字比较
for(int j = i - 5; j >= 0; j -= 5){
// 如果当前的元素大于加上步长的元素,说明交换
if (arr[j] > arr[j + 5]) {
temp = arr[j];
arr[j] = arr[j + 5];
arr[j + 5] = temp;
}
}
}
System.out.println("希尔数组的第一轮排序后");
System.out.println(Arrays.toString(arr));
// 希尔数组的第二轮排序
// 因为第二轮排序 将10个数据分成5/2=2组
for (int i = 2; i < arr.length; i++){
// 遍历各组中 所有的元素(共五组,每组2个元素) 步场为5 如第一个数字和第5个数字比较
for(int j = i - 2; j >= 0; j -= 2){
// 如果当前的元素大于加上步长的元素,说明交换
if (arr[j] > arr[j + 2]) {
temp = arr[j];
arr[j] = arr[j + 2];
arr[j + 2] = temp;
}
}
}
System.out.println("希尔数组的第2轮排序后");
System.out.println(Arrays.toString(arr));
// 希尔数组的第三轮排序
// 因为第三轮排序 将10个数据分成2/2=1组
for (int i = 1; i < arr.length; i++){
// 遍历各组中 所有的元素(共五组,每组2个元素) 步场为5 如第一个数字和第5个数字比较
for(int j = i - 1; j >= 0; j -= 1){
// 如果当前的元素大于加上步长的元素,说明交换
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("希尔数组的第3轮排序后");
System.out.println(Arrays.toString(arr));
/**
* 希尔数组的第一轮排序后
* [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
* 希尔数组的第2轮排序后
* [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
* 希尔数组的第3轮排序后
* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
}
// 总的希尔排序(交换法)
public static void hellSortAll(int[]arr){
int temp = 0;
int group = arr.length/2;
while (group != 0) {
for (int i = group; i < arr.length; i++) {
for (int j = i - group; j >= 0; j -= group) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
group /= 2;
}
System.out.println("希尔排序结果");
System.out.println(Arrays.toString(arr));
}
}
// 对交换式的希尔排序法进行优化 -> 移位法
public static void hellSortBetter(int[] arr){
int group = input.length / 2;
int insertIndex = 0;
int insertValue = 0;
while(group != 0){
for(int i = group; i < input.length; i++){
insertIndex = i - group;
insertValue = input[i];
while(insertIndex >= 0 && insertValue < input[insertIndex]){
input[insertIndex + group] = input[insertIndex];
insertIndex -= group;
}
input[insertIndex + group] = insertValue;
}
group /= 2;
}
System.out.println("希尔排序结果");
System.out.println(Arrays.toString(arr));
}
package com.luyi.sort;
import java.util.Arrays;
/**
* @author 卢意
* @create 2020-12-08 21:06
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = {-9,78,0,23,-567,70};
quickSort(arr, 0,arr.length-1 );
System.out.println("arr: "+ Arrays.toString(arr));
}
/**
*
* @param arr
* @param left 左边的索引
* @param right 右边的索引
*/
public static void quickSort(int[] arr, int left, int right){
int l = left; // 左下标
int r = right; // 右下标
int temp = 0; //临时变量
// pivot 中轴
int pivot = arr[(left + right) / 2];
// 只要右边的索引大于左边的索引就继续循环 目的是 让 比pivot小的值放到左边 比 pivot 值大的放在右边
while (l < r){
// 在pivot的左边 一直找 找到大于等于pivot的值 ,才退出
while (arr[l] < pivot){ //最多找到pivot本身
l += 1;
}
// 在pivot的左边 一直找 找到小于等于pivot的值 ,才退出
while (arr[r] > pivot){ //最多找到pivot本身
r -= 1;
}
// 如果成立 说明 pivot 左边的值, 已经全部小于等于pivot的值, 右边, 已经全部大于等于pivot的值
if(l >= r){
break;
}
// 交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// 判断 如果交换完后 发现arr[l] = pivot r向前移动 l--
if (arr[l] == pivot){
r -= 1;
}else if (arr[r] == pivot){// 判断 如果交换完后 发现arr[r] = pivot l向后移动 l++
l++;
}
}
// 如果 l == r 必须 l++ 和 r--, 否则会出现栈溢出
if (l == r ){
l++;
r--;
}
// 向左递归
if (left < r) {
quickSort(arr,left,r);
}
// 向右递归
if (right > l) {
quickSort(arr, l, right);
}
}
}
package com.luyi.sort;
import java.util.Arrays;
import java.util.TimerTask;
/**
* 归并排序
* @author 卢意
* @create 2020-12-09 9:50
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {8,4,5,7,1,3,6,2};
int[] temp = new int[arr.length]; // 归并排序需要额外的空间
mergeSort(arr, 0, arr.length-1, temp);
System.out.println("归并排序后: "+ Arrays.toString(arr));
}
//分 + 合的方法
public static void mergeSort(int[] arr, int left, int right, int[] temp){
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);
}
}
/**
* 合并的方法
* @param arr 原始的数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 中转数组
*/
public static void merge(int[] arr,int left, int mid, int right, int[] temp){
int i = left; //初始化 i, 左边的有序序列的初始索引
int j = mid+1;// 初始化j 右边的初始索引
int t = 0; //初始化temp数组的索引
// 先把左右两边(有序)的数据 按照规则填充到temp
// 直到左右两边的有序序列有一方处理完毕
while (i <= mid && j <= right){
// 如果左边的有序序列的当前元素 <= 右边有序序列的当前元素 将左边的当前元素放到temp的t的位置
if (arr[i] <= arr[j]){
temp[t++] = arr[i++];// 放入后移
}else {
temp[t++] = arr[j++];
}
}
// 将有剩余的数组的数据一次加入temp中的尾部
while (i <= mid){ //左边的有序序列还有剩余的数据 将剩余的有序数据 依次加入temp数组
temp[t++] = arr[i++];
}
while (j <=right){ //右边的有序序列还有剩余的数据 将剩余的有序数据 依次加入temp数组
temp[t++] = arr[j++];
}
// 将temp数组重新放到arr
// 注意 并不是每次拷贝所有
t = 0;
int tempLeft = left;// 第一次合并 tempLeft=0 right=1 // 第二次合并 tempLeft=2 right=3 // 第三次合并tempLeft=0 right=3
// 最后一次合并 tempLeft=0 right=7
while (tempLeft <= right){
arr[tempLeft++] = temp[t++];
}
}
}
稳定排序 : 如果要排序的数组有两个相同的数字 如a1 =a2 =1 {a1,2,3,a2} 排序后 {a1,a2,2,3} a1还是在a2的前面
package com.luyi.sort;
import java.util.Arrays;
/**
* @author 卢意
* @create 2020-12-09 13:43
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53,3,542,748,14,214};
//radixSort(arr);
radixSortAll(arr);
}
// 基数排序法
public static void radixSort(int[] arr){
// 模拟对一轮(针对每个元素的个位数进行排序处理)
// 定义一个二维数组,表示一个二维数组, 每个桶就是一个一维数组
// 说明 二维数组包含10个一维数组
// 为了防止放入数的时候 数据溢出 ,则每一个一维数组的大小为array.length()
// 很明显 空间换时间
int[][] bucket = new int[10][arr.length];
// 为了记录每个桶中 实际存放了多少个数据 我们定义一个一维数组来记录每个桶的个数
// bucketElementCounts[0] 就是bucket[0] 的放入数据的个数
int[] bucketElementCounts = new int[10];
for (int j = 0; j < arr.length; j++){
// 取数每个元素的个位
int digitOfElement = arr[j] % 10;
// 放入到对应的桶
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
// 将桶里的数据放入
int index = 0;
// 遍历每一个桶 将桶里的数据放入 原来的数组中
for (int k = 0;k < bucket.length; k++){
if (bucketElementCounts[k] != 0){
for (int j = 0; j < bucketElementCounts[k]; j++){
arr[index++] = bucket[k][j];
}
}
// 第一轮需要对bucketElementCounts数组置零
bucketElementCounts[k]= 0;
}
System.out.println("第一轮 对个位数进行处理后的数组:" + Arrays.toString(arr));
// 模拟对二轮(针对每个元素的十位数进行排序处理)
for (int j = 0; j < arr.length; j++){
// 取数每个元素的十位
int digitOfElement = arr[j] / 10 % 10;
// 放入到对应的桶
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
// 将桶里的数据放入
index = 0;
// 遍历每一个桶 将桶里的数据放入 原来的数组中
for (int k = 0;k < bucket.length; k++){
if (bucketElementCounts[k] != 0){
for (int j = 0; j < bucketElementCounts[k]; j++){
arr[index++] = bucket[k][j];
}
}
bucketElementCounts[k]= 0;
}
System.out.println("第二轮 对十位数进行处理后的数组:" + Arrays.toString(arr));
// 模拟对三轮(针对每个元素的百位数进行排序处理)
for (int j = 0; j < arr.length; j++){
// 取数每个元素的百位
int digitOfElement = arr[j] /100 % 10;
// 放入到对应的桶
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
// 将桶里的数据放入
index = 0;
// 遍历每一个桶 将桶里的数据放入 原来的数组中
for (int k = 0;k < bucket.length; k++){
if (bucketElementCounts[k] != 0){
for (int j = 0; j < bucketElementCounts[k]; j++){
arr[index++] = bucket[k][j];
}
}
bucketElementCounts[k]= 0;
}
System.out.println("第三轮 对百位数进行处理后的数组:" + Arrays.toString(arr));
}
// 总的基数排序
public static void radixSortAll(int[] arr){
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts = new int[10];
int index;
// 需要获取最大的数
int max = arr[0];
for (int i = 1; i < arr.length; i++){
if (max < arr[i]){
max = arr[i];
}
}
int maxLength = (max+"").length();
for (int i = 0, n = 1; i < maxLength; i++, n *=10) {
for (int j = 0; j < arr.length; j++){
int digitOfElement = arr[j] / n % 10;
// 放入到对应的桶
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
// 将桶里的数据放入
index = 0;
// 遍历每一个桶 将桶里的数据放入 原来的数组中
for (int k = 0;k < bucket.length; k++){
if (bucketElementCounts[k] != 0){
for (int j = 0; j < bucketElementCounts[k]; j++){
arr[index++] = bucket[k][j];
}
}
bucketElementCounts[k]= 0;
}
}
System.out.println("基数排序的结果" + Arrays.toString(arr));
}
}
堆排序要学了二叉树才能理解