稀疏数组概述
/*
稀疏数组:
如果一个数组大部分元素为0,或者为同一个值数组时,可以考虑稀疏数组
应用场景有棋盘,地图等
稀疏数组的处理方案:
记录数组一共有几行几列,多少不同的值
把具有不同值的行和列记录在小规模的数组中,从而缩小程序的规模
就是每个元素只记录行,列,值
第一行记录的是原始数组行,列,非0数据的数量
二维数组转稀疏数组的思路
遍历原始二维数组,获取有效数据的个数,让这个数等于sum
根据sum就可以创建稀疏数组 int[sum + 1][3]
将二维数组有效数据存储到稀疏数组中即可
稀疏数组恢复成二维数组
读取稀疏数组的第1行,根据第一行提供的行和列创建原始二维数组,int[][] array = array[11][11]
再读取后几行的数据,再赋给原始二维数组即可
稀疏数组最终还要保存到磁盘中去
*/
稀疏数组代码实现
package dc;
/**
* @author 404小恐龙
* @version 1.8
* @date 2021/10/4 17:15
*/
public class d15 {
public static void main(String[] args) {
/*
创建原始的二维数组
以棋盘为例
0表示没子
1表示黑子
2表示白子
初始化数据
*/
int[][] chess = new int[11][11];
chess[1][2] = 1;
chess[2][3] = 2;
/*
二维数组转换稀疏数组
得到非0数据的个数
*/
int sum = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (chess[i][j] != 0){
sum++;
}
}
}
// System.out.println(sum); // 2
/**
原始二维数组转稀疏数组
*/
int[][] sparseArray = new int[sum + 1][3];
/*
给稀疏数组赋值
*/
sparseArray[0][0] = 11;
sparseArray[0][1] = 11;
sparseArray[0][2] = sum;
/*
遍历二维数组
非0的值放入
*/
int count = 0;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (chess[i][j] != 0){
count++;
sparseArray[count][0] = i;
sparseArray[count][1] = j;
sparseArray[count][2] = chess[i][j];
}
}
}
/*
输出稀疏数组
*/
for (int i = 0; i < sparseArray.length; i++) {
System.out.printf("%d\t%d\t%d\t\n",sparseArray[i][0],sparseArray[i][1],sparseArray[i][2]);
}
/**
稀疏数组恢复成二维数组
*/
/*
先读取第1行
得到原始二维数组
*/
int[][] initArray = new int[sparseArray[0][0]][sparseArray[0][1]];
/*
从第2行开始遍历
*/
for (int i = 1; i < sparseArray.length; i++) {
initArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
for (int[] ints : initArray) {
for (int anInt : ints) {
/*
遍历输出二维数组
*/
System.out.printf("%d\t",anInt);
}
System.out.println();
}
}
}
稀疏数组存储到磁盘
package dc;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author 404小恐龙
* @version 1.8
* @date 2021/10/5 10:49
*/
public class d16 {
public static void main(String[] args) {
/*
课后练习:
在前面的基础上,将稀疏数组保存到磁盘上,如chess.data
读取磁盘上的稀疏数组,恢复成二维数组
*/
/**
* 将稀疏数组保存到硬盘上
*/
File dest = new File("sparseArray.data");
/*
这就相当于自动关闭了BufferedWriter
就不用手写bw.close();了
*/
try (BufferedWriter bw = new BufferedWriter(new FileWriter(dest));) {
for (int[] row : sparseArray) {
for (int data : row) {
bw.write(data + "\t");
}
bw.write("\n");
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
/**
* 从硬盘读取稀疏数组并进行恢复
*/
File src = new File("sparseArray.data");
BufferedReader br = null;
List<Integer> list = new ArrayList<>();
try {
br = new BufferedReader(new FileReader(src));
String line;
while ((line = br.readLine()) != null) {
String[] str = line.split("\t");
for (int i = 0; i < str.length; i++) {
list.add(Integer.parseInt(str[i]));
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
遍历稀疏数组
*/
/*
先拿到行数和列数
list.get(2)+1 行数就是第3个元素+1
*/
int sparseArr2[][]=new int[list.get(2)+1][3];
int j=0;
for(int i=0;i<list.size();i=i+3) {
sparseArr2[j][0]=list.get(i);
sparseArr2[j][1]=list.get(i+1);
sparseArr2[j][2]=list.get(i+2);
j++;
}
for (int[] ints : sparseArr2) {
for (int anInt : ints) {
System.out.printf("%d\t",anInt);
}
System.out.println();
}
}
}
双向链表代码实现
package dc;
/**
* @author 404小恐龙
* @version 1.8
* @date 2021/10/5 10:49
*/
public class d16 {
public static void main(String[] args) {
/*
双向链表代码实现
*/
Node node1 = new Node(1,"MySQL");
Node node2 = new Node(2,"Java");
Node node3 = new Node(3,"Tomcat");
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.addNode(node1);
doubleLinkedList.addNode(node2);
doubleLinkedList.addNode(node3);
doubleLinkedList.deleteNode(2);
doubleLinkedList.ergodic();
}
}
/*
双向链表类
*/
class DoubleLinkedList{
/*
初始化头节点
这里的头节点一直存在
*/
Node head;
public DoubleLinkedList(){
head = new Node(0,"01");
}
/*
返回头节点
*/
public Node getHead(){
return head;
}
/**
* 遍历双向链表
*/
public void ergodic(){
if (head.next == null){
System.out.println("empty");
return;
}
Node tmp = head.next;
while (true){
if (tmp == null){
break;
}
System.out.println(tmp.toString());
tmp = tmp.next;
}
}
/**
* 添加节点
* @param node 需要添加的节点
*/
public void addNode(Node node){
Node tmp = head;
while (true){
if (tmp.next == null){
break;
}
tmp = tmp.next;
}
tmp.next = node;
node.prev = tmp;
}
public void deleteNode(int number){
if (head.next == null){
System.out.println("empty");
return;
}
Node tmp = head.next; // 辅助节点
boolean flag = false; // 标记是否找到需要删除的节点
while(true){
if (tmp == null){
break;
}
if (tmp.number == number){
flag = true;
break;
}
tmp = tmp.next;
}
/*
删除操作
*/
if (flag){
tmp.prev.next = tmp.next; // tmp前面的节点的next指针指向tmp下一个节点
/*
tmp后面的节点的prev指针指向tmp前一个节点,
如果是最后一个节点,就不需要执行下面这句话了
否则会出现空指针异常
需要加一层判断
*/
if (tmp.next != null){
tmp.next.prev = tmp.prev;
}
}else{
System.out.println("operator fail");
}
}
}
/*
节点类
*/
class Node{
public int number;
public String nickName;
public Node prev;
public Node next;
public Node(int number, String nickName) {
this.number = number;
this.nickName = nickName;
}
@Override
public String toString() {
return "Node{" +
"number=" + this.number +
", nickName='" + this.nickName + '\'' +
'}';
}
/*
这里注意:
重写toString方法不能加上prev和next属性
否则会出现java.lang.StackOverflow
构造函数里面也不能体现出来
暂时不清楚是什么原因
*/
}
单向环形链表代码实现
package dc;
/**
* @author 404小恐龙
* @version 1.8
* @date 2021/10/5 15:39
*/
public class d17 {
public static void main(String[] args) {
/*
单向环形链表实现约瑟夫问题
约瑟夫问题描述:
设1-n的人围坐一圈,约定编号为k的人(1<=k<=n)从1开始报数
数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人出列
以此类推,直到所有人出列为止,列出出队编号
假设:
n = 5 即有5个人
k = 1 即从1开始报数
m = 2 即数2下
则出队的序列:
2 4 1 5 3
构建单向环形链表的思路
first指针指向第一个节点,并形成环
current指针指向当前创建最新的节点
每创建一个节点
让这个节点的next指向第一个节点
这样就形成了一个环
遍历环形链表的思路
创建辅助节点指向first节点
while循环遍历该环形链表即可
current.next指向first就代表结束循环
*/
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addNode1(6);
circleSingleLinkedList.ergodic();
}
}
/*
单向链表类
*/
class CircleSingleLinkedList{
/*
创建一个没有编号的first节点
*/
private Node1 first = null;
/**
* 构建单向环形链表的方法
* @param number 环形链表的个数
*/
public void addNode1(int number){
/*
数据校验
*/
if (number < 1){
System.out.println("operator fail number must larger than 1");
return;
}
/*
辅助节点
指向新创建的节点
需要初始化为-1
否则会出现空指针异常
*/
Node1 current = new Node1(-1);
for (int i = 0; i <= number; i++) {
Node1 node11 = new Node1(i);
/*
头指针
*/
if (i == 1){
first = node11;
/*
构成环
辅助节点指向新创建的节点
可以理解为帮助最后一个节点加入新节点
*/
first.setNext(first);
current = first;
}else{
current.setNext(node11);
node11.setNext(first);
current = node11;
}
}
}
/**
* 遍历单向环形链表
*/
public void ergodic(){
if (first == null){
System.out.println("empty");
return;
}
/*
由于first不能动
所以利用辅助指针进行遍历
*/
Node1 current = first;
while(true){
/*
输出编号
*/
System.out.printf("%d\n",current.getNumber());
if (current.getNext() == first){
break;
}
/*
程序运行到此处
说明还没有遍历完毕
current后移
*/
current = current.getNext();
}
}
}
/*
节点类
一般来说
这样的成员写成内部类比较好
*/
class Node1{
private int number;
private Node1 next;
public Node1(int number){
this.number = number;
}
/*
私有属性
生成set get方法
*/
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Node1 getNext() {
return next;
}
public void setNext(Node1 next) {
this.next = next;
}
}
单向环形链表解决约瑟夫问题
package dc;
/**
* @author 404小恐龙
* @version 1.8
* @date 2021/10/5 15:39
*/
public class d17 {
public static void main(String[] args) {
/*
单向环形链表实现约瑟夫问题
约瑟夫问题描述:
设1-n的人围坐一圈,约定编号为k的人(1<=k<=n)从1开始报数
数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人出列
以此类推,直到所有人出列为止,列出出队编号
假设:
n = 5 即有5个人
k = 1 即从1开始报数
m = 2 即数2下
则出队的序列:
2 4 1 5 3
构建单向环形链表的思路
first指针指向第一个节点,并形成环
current指针指向当前创建最新的节点
每创建一个节点
让这个节点的next指向第一个节点
这样就形成了一个环
遍历环形链表的思路
创建辅助节点指向first节点
while循环遍历该环形链表即可
current.next指向first就代表结束循环
*/
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addNode1(6);
circleSingleLinkedList.ergodic();
/*
根据用户输入
生成节点出圈序列
思路:
创建辅助指针auxiliary
指向最后一个节点
辅助节点的作用是帮助节点出圈
因为出圈的节点是first指向的节点
画个图就知道了
节点报数之前first指针先移动到k这个位置
节点报数时
first和auxiliary同时移动m-1位
这时让first指向的节点出圈
first = first.next;
auxiliary.next = first;
此时中间隔着的节点没有任何引用指向
会被垃圾回收
*/
circleSingleLinkedList.outOfCircle(1,2,5);
/*
2 out of circle
4 out of circle
6 out of circle
3 out of circle
1 out of circle
5 is the last Node
*/
}
}
/*
单向链表类
*/
class CircleSingleLinkedList{
/*
创建一个没有编号的first节点
*/
private Node1 first = null;
/**
* 构建单向环形链表的方法
* @param number 环形链表的个数
*/
public void addNode1(int number){
/*
数据校验
*/
if (number < 1){
System.out.println("operator fail number must larger than 1");
return;
}
/*
辅助节点
指向新创建的节点
需要初始化为-1
否则会出现空指针异常
*/
Node1 current = new Node1(-1);
for (int i = 0; i <= number; i++) {
Node1 node11 = new Node1(i);
/*
头指针
*/
if (i == 1){
first = node11;
/*
构成环
辅助节点指向新创建的节点
可以理解为帮助最后一个节点加入新节点
*/
first.setNext(first);
current = first;
}else{
current.setNext(node11);
node11.setNext(first);
current = node11;
}
}
}
/**
* 遍历单向环形链表
*/
public void ergodic(){
if (first == null){
System.out.println("empty");
return;
}
/*
由于first不能动
所以利用辅助指针进行遍历
*/
Node1 current = first;
while(true){
/*
输出编号
*/
System.out.printf("%d\n",current.getNumber());
if (current.getNext() == first){
break;
}
/*
程序运行到此处
说明还没有遍历完毕
current后移
*/
current = current.getNext();
}
}
/**
* 输出约瑟夫问题出圈序列
* @param startNode 从哪一节点开始
* @param countNumber 报数个数
* @param number 最初有多少个节点
*/
public void outOfCircle(int startNode,int countNumber,int number){
if (first == null || startNode < 1 || startNode > number){
System.out.println("parameter error");
return;
}
Node1 auxiliary = first;
/*
让辅助指针事先指向环形链表最后一个节点
*/
while(true){
if (auxiliary.getNext() == first){
break;
}
auxiliary = auxiliary.getNext();
}
/*
节点报数前
先将first指针和auxiliary指针向后移动k-1次
*/
for (int i = 0; i < startNode - 1; i++) {
first = first.getNext();
auxiliary = auxiliary.getNext();
}
/*
节点开始报数
指针同时移动m-1次
然后出圈
这里是一个循环操作
直到圈中只有一个人
*/
while (true){
if (auxiliary == first){
/*
此时圈中只有1人
为什么是auxiliary == first判断
画图就知道了
当圈中只有两个人并将要删除一个人时
两个指针立刻重叠
而且是first主动和auxiliary重叠(first = first.getNext();)
接着auxiliary指针清除对需要删除节点的引用(auxiliary.setNext(first);)
删除的节点被垃圾回收
此时圈中唯一一个节点自成环
两个指针的getNext()会永远指向该节点本身
*/
break;
}
/*
程序运行到此处
说明圈中当前的人数大于1
同时移动两个指针
*/
for (int i = 0; i < countNumber - 1; i++) {
first = first.getNext();
auxiliary = auxiliary.getNext();
}
/*
程序运行到此处
first指向的节点可以进行出圈操作
这时可以利用数组记录出圈的序列
*/
System.out.println(first.getNumber() + " out of circle");
first = first.getNext();
auxiliary.setNext(first);
}
/*
程序运行到此处
需要对最后一位出圈者进行记录
这是唯一一个没有被垃圾回收的节点
我们称作幸存节点
*/
System.out.println(first.getNumber() + " is the last Node");
}
}
/*
节点类
一般来说
这样的成员写成内部类比较好
*/
class Node1{
private int number;
private Node1 next;
public Node1(int number){
this.number = number;
}
/*
私有属性
生成set get方法
*/
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Node1 getNext() {
return next;
}
public void setNext(Node1 next) {
this.next = next;
}
}