目录
2021年8月2日16:41:25,天气晴,今天开始复习数据结构与算法,其实就是从头开始,大二没好好学QAQ,此笔记是在学习B站尚硅谷Java数据结构与java算法视频时所做记录。
一、稀疏数组
作用
当一个二维数组中的大多数数据默认值为0,或者为同一个数值时,记录了很多无用数据,可以转化为稀疏数组用以保存,
例如:
左侧为初始数组(8*8的二维数组),右侧为稀疏数组。稀疏数组行数为初始数组有效值的数量+1,列数固定为3,即是个7*3的二维数组。稀疏数组记录初始数组的行列数,有效值,用以缩小数组规模
- 第一行保存的是初始数组的行数、列数、有效值的个数
- 第二行开始保存的是有效值的行数、列数、有效值
转换思路
初始二维数组转换为稀疏数组:
- 定义一个sum,遍历初始数组,得到有效值个数
- 创建稀疏数组:行数为sum+1,列数为固定值3
- 将初始数组有效值及其行数列数存入稀疏数组
稀疏数组转化为二维数组:
- 定义一个二维数组,行数为稀疏数组第一行第一列,列数为第一行第二列
- 从第二列开始遍历稀疏数组,将有效值还原至二维数组对应位置
下面是转换demo:
package SparseArrayAndQueues;
import java.io.*;
public class SparseArray {
public static void main(String[] args) {
//定义数组
int arr1[][] = new int[10][10];
//给数组赋值
arr1[0][0] = 1;
arr1[0][8] = 3;
arr1[2][3] = 2;
arr1[4][6] = 4;
arr1[5][5] = 5;
arr1[8][8] = 6;
arr1[9][7] = 7;
arr1[6][0] = 9;
//遍历初始数组、 定义sum:有意义数据的个数
int sum = 0;
System.out.println("初始数组:");
for(int i = 0; i < arr1.length; i++){
for (int j = 0 ; j < arr1[i].length; j++){
System.out.printf("%d\t",arr1[i][j]);
if (arr1[i][j] != 0){
sum++;
}
}
System.out.println();
}
//定义稀疏数组sparsearray,共有sum+1行,固定3列
int sparsearray[][] = new int[sum+1][3];
//第一行第一列:二维数组总行数、第二列:二维数组总列数、第三列:初始数组有效值个数
sparsearray[0][0] = arr1.length;
sparsearray[0][1] = arr1[0].length;
sparsearray[0][2] = sum;
//为稀疏数组赋值,定义count为稀疏数组的行数,赋值后count+1换行
int count = 1;
for(int i = 0; i < arr1.length; i++){
for (int j = 0 ; j < arr1[i].length; j++){
if (arr1[i][j] != 0){
sparsearray[count][0] = i;
sparsearray[count][1] = j;
sparsearray[count][2] = arr1[i][j];
count++;
}
}
}
//遍历稀疏数组
System.out.println("稀疏数组:");
for (int i = 0; i < sparsearray.length; i++){
for (int j = 0; j < sparsearray[i].length; j++){
System.out.printf("%d\t",sparsearray[i][j]);
}
System.out.println();
}
//将稀疏数组还原,定义一个新的二维数组,行数为稀疏数组第一行第一列,列数为第一行第二列
int arr2[][] = new int[sparsearray[0][0]][sparsearray[0][1]];
//从第二行开始遍历稀疏数组为二维数组赋值
for (int i = 1; i < sparsearray.length; i++){
for (int j = 0; j < sparsearray[i].length; j++){
//arr2[稀疏数组有效值所在行][稀疏数组有效值所在列] = 稀疏数组有效值
arr2[sparsearray[i][0]][sparsearray[i][1]] = sparsearray[i][2];
}
}
//遍历还原后的稀疏数组
System.out.println("稀疏数组还原为二维数组");
for(int i = 0; i < arr2.length; i++){
for (int j = 0 ; j < arr2[i].length; j++){
System.out.printf("%d\t",arr2[i][j]);
}
System.out.println();
}
System.out.println("以下扩展,将稀疏数组通过IO流存取——————————————————————————————————————————————————————————————————————————");
//将稀疏数组存入磁盘
writeArray(sparsearray);
//读取磁盘中的稀疏数组
System.out.println("读取磁盘稀疏数组");
int[][] sarr2 = readsparsearray();
for(int i = 0; i < sarr2.length; i++){
for (int j = 0 ; j < sarr2[i].length; j++){
System.out.printf("%d\t",sarr2[i][j]);
}
System.out.println();
}
}
//将稀疏数组存入磁盘
public static void writeArray(int[][] sparsearray){
FileWriter fos = null;
try {
fos = new FileWriter("data.txt");
for (int[] arr : sparsearray){
fos.write(arr[0]+"\t"+arr[1]+"\t"+arr[2]);
fos.write("\r\n");
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//从磁盘读取文件
public static int[][] readsparsearray(){
int[][] sparse2 = null;
boolean isnotread=false;
BufferedReader fis = null;
try {
fis = new BufferedReader(new FileReader(new File("data.txt")));
String lineStr = null;
int curCount = 0;
while (((lineStr=fis.readLine())!=null)) {
String[] tempStr = lineStr.split("\t");
if(!isnotread){
sparse2 = new int[Integer.parseInt(tempStr[2])+1][3];
sparse2[curCount][0] = Integer.parseInt(tempStr[0]);
sparse2[curCount][1] = Integer.parseInt(tempStr[1]);
sparse2[curCount][2] = Integer.parseInt(tempStr[2]);
curCount++;
isnotread=true;
}else {
sparse2[curCount][0] = Integer.parseInt(tempStr[0]);
sparse2[curCount][1] = Integer.parseInt(tempStr[1]);
sparse2[curCount][2] = Integer.parseInt(tempStr[2]);
curCount++;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sparse2;
}
}
运行结果:
二、队列
2021年8月3日09:47:58 暴雨, 今天下大雨了,差点落汤鸡QAQ
介绍:
队列是一个有序列表、可以用数组或是链表来实现
队列遵循先入先出规则:先存入队列的数据要先取出
队列本身是有序列表、若使用数组的结构来存储队列的数据,则声明如下图,其中maxSize是该队列的最大容量
因为队列的输入输出是分别从前后端来处理,因此需要两个变量front、rear分别记录队列前后端的下标,front会随着数据输出而改变,二rear则是随着数据输入而改变,如图所示
思路分析
入队:尾指针后移一位;rear+1,front==rear【空】
若尾指针小于队列的最大下标maxSize-1(数组起始为0故此),则将数据存入rear所指的数组元素中,否则无法存入数据。rear==maxSize-1【队列满】
代码思路
- 首先编写一个ArrayQueue类,
- 定义属性:
- 最大容量:maxSize
- 队列头:front
- 队列尾:rear
- 数组:arr
- 定义构造器:传入最大容量
- 判断队列是否满:rear == maxSize-1
- 判断队列是否为空:rear == front
- 添加数据到队列:
- 先判断队列是否已满、满则返回
- 没满则队尾rear后移:rear++
- 给数组赋值arr【rear】 = n (n为添加的值)
- 出队列,取队列
- 判断队列是否为空、满则返回,或抛出队列空异常提示
- 不为空则队头front后移:front++
- 返回数组 arr【front】
- 显示队列的所有数据,遍历
- 判断队列是否为空,为空则不遍历
- 不为空则遍历
- 显示队列头数据
- 判断是否为空、为空返回
- 不为空返回数据:arr【front+1】
代码demo:
package SparseArrayAndQueues;
public class ArrayQueue {
public static void main(String[] args) {
//初始化
QuereArray quereArray = new QuereArray(4);
//添加数据,多添加一个,看看能不能继续添加,然后遍历
quereArray.add(1);
quereArray.add(2);
quereArray.add(3);
quereArray.add(4);
quereArray.add(5);
quereArray.show();
System.out.println("队头");
quereArray.head();
//取出数据后遍历并看队头
quereArray.get();
System.out.println("取出一个数后的队列:");
quereArray.show();
System.out.println("队头");
quereArray.head();
//连续取出数据,再遍历
quereArray.get();quereArray.get();quereArray.get();quereArray.get();
quereArray.show();
}
static class QuereArray{
//最大容量、队头、队尾、数组
private int maxSize;
private int front;
private int rear;
private int arr[];
//构造器,给队列初始化
public QuereArray(int maxValue){
maxSize = maxValue;
front = -1; //指向头部
rear = -1; //指向尾部,初始化头尾相等
arr = new int[maxSize];
}
//判断队列是否为空
public boolean isEmpty(){
return rear == front;
}
//判断队列是否为满
public boolean isFull(){
return rear == maxSize-1;
}
//添加数据
public void add(int n){
//先判断队列是否已满,满则返回
if (isFull()){
System.out.println("队列已满,不可再添加");
return;
}
arr[++rear] = n;
return;
}
//取出数据
public int get(){
//判断队列是否为空、是则返回
if(isEmpty()){
System.out.println("队列已空,不去再取");
return 0;
}
front++;
return arr[front];
}
//遍历队列
public void show(){
if (isEmpty()){
System.out.println("队列为空,无法遍历");
return;
}
for(int i = front+1; i<=rear;i++){
System.out.println(arr[i]);
}
return;
}
public void head(){
if (isEmpty()){
System.out.println("队列为空,无法取出头部");
return;
}
System.out.println( arr[front+1]);
return;
}
}
}
运行结果:
这种队列的问题就是数组用了一个后无法再用了,队列满了之后无法取出后无法再添加,原因是没有取模,下面用环形队列解决,取模防止数组越界。
三、环形队列
思路分析
- 队头front变量做调整:指向队列第一个元素,初始化为front=0
- 队尾rear变量做调整:指向队列最后一个元素的后一个位置,故此需要空出个空间作为约定,则创建数组时多加一位:想创建3则int[4]。初始化rear=0
- 当队列满时,条件改变为(rear+1)%maxSize == front
- 当队列空时,rear==front
- 队列的有效数据个数(rear+maxSize-front)%maxSize
代码思路基本不变,demo如下:
package SparseArrayAndQueues;
public class ArrayQueue2 {
static class QuereArray{
//最大容量、队头、队尾、数组
private int maxSize;
private int front;
private int rear;
private int[] arr;
//构造器,给队列初始化
public QuereArray(int maxValue){
maxSize = maxValue;
front = 0; //指向头部
rear = 0; //指向尾部,初始化头尾相等
arr = new int[maxSize];
}
//判断队列是否为空
public boolean isEmpty(){
return front==rear;
}
//判断队列是否为满
public boolean isFull(){
return (rear+1)%maxSize==front;
}
//添加数据
public void add(int n){
//先判断队列是否已满,满则返回
if (isFull()){
System.out.println("队列已满,不可再添加");
return;
}
arr[rear] = n;
System.out.println("添加数据:arr["+rear+"]="+arr[rear] );
rear = (rear+1)%maxSize;
return;
}
//取出数据
public void get(){
//判断队列是否为空、是则返回
if(isEmpty()){
return ;
}
System.out.println("取出:arr["+front+"]="+arr[front]);
front = (front+1)%maxSize;
return;
}
//遍历队列
public void show(){
if (isEmpty()){
System.out.println("队列为空,无法遍历");
return;
}
//遍历有效个数
for(int i = front; i<front+(rear+maxSize-front)%maxSize;i++){
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
return;
}
public void head(){
if (isEmpty()){
System.out.println("队列为空,无法取出头部");
return;
}
System.out.println("队头:arr["+front+"]="+arr[front]);
return;
}
}
public static void main(String[] args) {
//初始化
QuereArray quereArray = new QuereArray(4);
//添加数据,多添加一个,看看能不能继续添加,然后遍历
quereArray.add(1);
quereArray.add(2);
quereArray.add(3);
quereArray.add(4);
quereArray.show();
quereArray.head();
//取出数据后遍历并看队头
quereArray.get();
System.out.println("取出一个数后的队列:");
quereArray.show();
quereArray.head();
//连续取出数据,再遍历
quereArray.get();quereArray.get();quereArray.get();quereArray.get();
quereArray.show();
quereArray.head();
quereArray.add(1);
quereArray.add(2);
quereArray.add(3);
quereArray.add(4);
quereArray.show();
quereArray.head();
}
}
代码截图:
加油
四、单链表
2021年8月4日15:09:15,晴,很无语崩溃,刚刚不小心关掉了,没保存
算了,不写了思路啥的了
实现demo:
package SparseArrayAndQueues;
import javax.security.auth.Subject;
/**
* 2021年8月4日10:54:22
*/
public class SingleLink {
public static void main(String[] args) {
SubjectLink subjectLink = new SubjectLink();
subject subject1 = new subject(1,"语文");
subject subject2 = new subject(2,"数学");
subject subject3 = new subject(3,"英语");
subject subject4 = new subject(4,"物理");
subject subject5 = new subject(5,"化学");
//插入不排序
// subjectLink.add(subject1);
// subjectLink.add(subject3);
// subjectLink.add(subject2);
// subjectLink.add(subject4);
//插入排序
subjectLink.addSubject(subject5);
subjectLink.addSubject(subject1);
subjectLink.addSubject(subject3);
subjectLink.addSubject(subject3);
subjectLink.addSubject(subject2);
subjectLink.addSubject(subject4);
subjectLink.show();
}
static class SubjectLink{
//初始化头结点
private subject head = new subject(0,"");
//添加结点,不排序
public void add(subject sub){
subject tem = head;
while (true){
//找到空节点则退出,否则后移
if (tem.next == null){
break;
}
tem=tem.next;
}
//指向最后
tem.next=sub;
}
//排序添加
public void addSubject(subject sub){
subject tem = head;
boolean flag = false;
while (true){
if (tem.next==null){
System.out.println("链表next域为空,break");
break;
}
if (tem.next.id > sub.id){
System.out.println("添加数据的id比中间值next域的id小,找到位置");
break;
}else if (tem.next.id == sub.id){
flag = true;
break;
}
tem = tem.next;
}
if (flag){
System.out.println("该数据已存在:"+sub.toString());
}else {
sub.next=tem.next;
tem.next=sub;
System.out.println("插入数据:"+ sub.toString());
}
}
//遍历链表
public void show(){
if (head.next==null){
System.out.println("链表为空");
return;
}
subject tem = head.next;
while (true){
if (tem==null){
System.out.println("链表已经没有数据");
break;
}
System.out.println(tem);
tem=tem.next;
}
}
}
//定义结点信息类
static class subject{
int id;
String name;
subject next;
public subject(int id, String name){
this.id = id;
this.name=name;
}
@Override
public String toString() {
return "subject{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
}
结果:
排序添加节点改版:
public void addSubject(subject sub){
subject tem = head;
while (true){
if (tem.next==null ||tem.next.id > sub.id){
sub.next=tem.next;
tem.next=sub;
System.out.println("插入数据:"+ sub.toString());
return;
}else if (tem.next.id == sub.id){
System.out.println("该数据已存在:"+sub.toString());
return;
}
tem = tem.next;
}
}
修改结点信息
- 根据id修改,id不可更改
- 传入一个subject类,先判断是否为空,为空返回
- 不为空则找到需要修改的结点,先定义中间值,定义flag判断是否找到默认false
- 遍历,判断中间值是否为空,为空表示遍历完退出
- 如果中间值.id=传入值.id 表示找到,改变flag= true,退出
- 判断flag,为true则找到,则令中间值等于传入值,false提示没找到
代码:
//修改结点信息
public void updateSubject(subject sub){
subject tem = head.next;
boolean flag = false;
while (true){
if (tem == null){
break;
}
if (tem.id == sub.id){
flag = true;
break;
}
tem=tem.next;
}
if (flag){
System.out.println("修改ID为"+tem.id+"的信息:"+tem.name+"======>"+sub.name);
tem.name=sub.name;
return;
}else {
System.out.println("没有要修改结点的id:"+sub.id);
return;
}
}
删除节点
- 找到需要删除节点的前一个节点tem(中间值),tem.next=tem.next.next
- tem没有只是待删节点,而是指向待删节点的下一节点,待删节点没有引用就会被垃圾回收机制回收
//删除节点
public void deleteSubject(int sub){
subject tem = head;
while (true){
if (tem.next == null){
System.out.println("没有要删除的结点:"+sub);
return;
}
if (tem.next.id ==sub){
tem.next=tem.next.next;
System.out.println("删除节点:"+sub);
show();
return;
}
tem=tem.next;
}
}
查找节点信息
//查找节点
public void findSubject(int sub){
subject tem = head;
while (true){
if (tem.next==null){
System.out.println("链表数据为空");
return;
}
if (tem.next.id == sub){
System.out.println("查找的节点数据为:" + tem.next.toString());
return;
}
tem=tem.next;
}
}
2021年8月5日10:29:33,晴
单链表反转
- 头结点为空或只有一个节点则返回
- 定义中间值=传入值头结点.next
- 定义第二个中间值为空
- 定义第三个中间值重新初始化:只有头节点,新链表
- 遍历传入链表cur!=null
- 中间值二则等于中间值1.next暂时保存当前结点的下一结点
- 中间值1.next=中间值3.next:将中间值二下一节点指向新列表最前端
- 中间值三.next = 中间值1,
- 中间值1后移:中间值1=中间值2
- 退出遍历后,传入值头节点.next= 中间值3.next
//链表反转
public static void reverse(subject sub){
if (sub.next==null || sub.next.next==null){
System.out.println("链表有效个数没有或只有一个");
return;
}
subject tem1 = sub.next;
subject tem2 = null;
subject tem3 = new subject(0,"");
while (tem1!=null){
tem2=tem1.next;
tem1.next=tem3.next;
tem3.next=tem1;
tem1=tem2;
}
sub.next= tem3.next;
}
单链表从尾到头打印
- 将链表反转再打印,不建议
- 利用栈数据结构
//单链表逆向打印
public static void ReversePrint(subject sub){
Stack<subject> stack = new Stack();
while (sub.next!=null){
stack.add(sub.next);
sub=sub.next;
}
while (stack.size()>0){
System.out.println(stack.pop());
}
}
链表合并
//链表合并
public void merge( subject sub){
System.out.println("+++++++++++");
if (sub.next==null){
System.out.println("链表为空");
return;
}
Stack<subject> stack = new Stack();
while (sub.next!=null){
stack.add(sub.next);
sub=sub.next;
}
while (stack.size()>0){
this.addSubject(stack.pop());
}
}
五、双向链表
思路
- 属性多了个pre指向前一个节点作业与next相反
- 遍历:与单链表一样,只是可向前编,也可向后
- 增加节点:默认添加到最后
- 首先找到最后的结点,tem.next=newSubject
- newSubject.pre=tem
- 修改节点信息:与单链表一样
- 删除:不需要辅助结点
- tem.pre.next=tem.next:(当前节点的前一节点的后节点指向当前节点的后节点)
- tem.next.pre=tem.pre:(当前节点的后节点的前节点指向当前节点的前节点)
- 查找节点根据id:遍历链表,当节点id=传入值则返回该节点数据
- 统计有效节点:定义length=0,遍历节点,头节点不为空则length++,返回length
- 查找倒数第n个结点:先求出有效节点,判断n是否符合规则,0<n<=有效节点,for循环链表,倒数第n个节点就是有效节点数-n
- 链表反转:链表有效节点数为空或只有一个时返回
- 定义中间值1为第一个有效节点,定义中间值2=null(等会在循环中记录中间值1的next值),定义中间值3为新头节点,
- 开始循环,条件为中间值1不为空
- 首先判断中间值3.next是否为空,不为空则中间值3.next.pre=中间值1
- 令中间值2=中间值.next:记录当前有效值的下一节点,
- 中间值1.next=中间值3.next:让它继承新头节点的next值
- 再中间值1.pre=中间值3、中间值3.next=中间值1:然后取代新头节点next值
- 中间值1=中间值2:轮到中间值2发挥了,中间值1就变成了他最开始的next
- 继续遍历,直到中间值1==null
- 结束遍历,令传入的头节点的next=中间值3.next
- 逆向打印链表:使用Stack<>栈辅助完成,遍历链表时把添加到栈中,接着遍历栈输出
- 链表合并:我这里也采用的是栈辅助,把副链表遍历添加到栈中,再遍历栈合并到主链表
package SparseArrayAndQueues;
import java.util.Stack;
public class DoubleLink {
public static void main(String[] args) {
SubjectDoubleLink subjectDoubleLink = new SubjectDoubleLink();
Subject subject1 = new Subject(1,"语文");
Subject subject2 = new Subject(2,"数学");
Subject subject3 = new Subject(3,"英语");
Subject subject4 = new Subject(3,"English");
Subject subject5 = new Subject(4,"物理");
Subject subject6 = new Subject(5,"化学");
Subject subject7 = new Subject(6,"生物");
Subject subject8 = new Subject(7,"历史");
Subject subject9 = new Subject(8,"地理");
Subject subject10 = new Subject(9,"政治");
subjectDoubleLink.addSort(subject1);
subjectDoubleLink.addSort(subject3);
subjectDoubleLink.addSort(subject7);
subjectDoubleLink.addSort(subject6);
subjectDoubleLink.addSort(subject2);
subjectDoubleLink.addSort(subject5);
//修改
subjectDoubleLink.updateSubject(subject4);
subjectDoubleLink.show();
//删除
subjectDoubleLink.deleteSubject(3);
subjectDoubleLink.show();
subjectDoubleLink.findSubject(6);
//统计节点有效个数
System.out.println("有效节点个数有:"+length(subjectDoubleLink.getHead()));
//查找链表倒数第n个结点
getlink(subjectDoubleLink.getHead(),2);
//双向链表反转
reverse(subjectDoubleLink.getHead());
subjectDoubleLink.show();
//逆向打印
ReversePrint(subjectDoubleLink.getHead());
//链表合并
System.out.println("_____________________________________");
SubjectDoubleLink subjectDoubleLink2 = new SubjectDoubleLink();
subjectDoubleLink2.addSort(subject8);
subjectDoubleLink2.addSort(subject10);
subjectDoubleLink2.addSort(subject9);
subjectDoubleLink2.show();
subjectDoubleLink2.merge(subjectDoubleLink.getHead());
subjectDoubleLink2.show();
}
//统计有效节点
public static int length(Subject sub){
if (sub.next==null){
return 0;
}
int length=0;
Subject tem = sub.next;
while (tem!=null){
length++;
tem=tem.next;
}
return length;
}
//查找链表倒数第n个结点
public static void getlink(Subject sub, int n){
if (sub.next==null){
return;
}
int size = length(sub);
if (n <=0 ||n >size) {
System.out.println("输入的数字小于等于0或超出有效长度");
return;
}
for (int i = 0; i<=size-n; i++){
sub=sub.next;
}
System.out.println("查找链表倒数第"+n+"个结点"+sub.toString());
}
//链表反转
public static void reverse(Subject sub){
if (sub.next==null || sub.next.next==null){
System.out.println("链表有效个数没有或只有一个");
return;
}
Subject tem1 = sub.next;
Subject tem2 = null;
Subject tem3 = new Subject(0,"");
while (tem1!=null){
if (tem3.next!=null){
tem3.next.pre=tem1;
}
tem2=tem1.next;
tem1.next=tem3.next;
tem1.pre=tem3;
tem3.next=tem1;
tem1=tem2;
}
sub.next= tem3.next;
}
//链表逆向打印
public static void ReversePrint(Subject sub){
Stack<Subject> stack = new Stack();
while (sub.next!=null){
stack.add(sub.next);
sub=sub.next;
}
while (stack.size()>0){
System.out.println(stack.pop());
}
}
}
class SubjectDoubleLink{
//初始化头结点
private Subject head = new Subject(0,"");
public Subject getHead() {
return head;
}
//遍历链表
public void show(){
if (head.next==null){
System.out.println("链表为空");
return;
}
Subject tem = head.next;
while (true){
if (tem==null){
break;
}
System.out.println(tem);
tem=tem.next;
}
}
//增加节点(不排序)
public void addSubject(Subject sub){
Subject tem = head;
while (true){
if (tem.next==null){
tem.next=sub;
sub.pre=tem;
break;
}
tem=tem.next;
}
}
//增加节点(排序):
public void addSort(Subject sub){
Subject tem = head;
while (true){
if(tem.next==null){
sub.pre=tem;
tem.next=sub;
System.out.println("插入数据:"+ sub.toString());
break;
}else if (tem.next.id > sub.id){
sub.next=tem.next;
sub.pre=tem;
tem.next.pre=sub;
tem.next=sub;
System.out.println("插入数据:"+ sub.toString());
break;
}else if (tem.next.id == sub.id){
System.out.println("该数据已存在:"+sub.toString());
break;
}
tem = tem.next;
}
}
//修改结点信息
public void updateSubject(Subject sub){
Subject tem = head.next;
while (true){
if (tem == null){
System.out.println("没有要修改结点的id:"+sub.id);
return;
}
if (tem.id == sub.id){
System.out.println("修改ID为"+tem.id+"的信息:"+tem.name+"======>"+sub.name);
tem.name=sub.name;
return;
}
tem=tem.next;
}
}
//删除节点
public void deleteSubject(int sub){
if (head.next == null){
System.out.println("链表为空,无法删除:"+sub);
return;
}
Subject tem = head.next;
while (true){
if (tem == null){
System.out.println("没有要删除的结点:"+sub);
break;
}
if (tem.id ==sub){
tem.pre.next = tem.next;
if (tem.next!=null){
tem.pre.next = tem.next;
}
System.out.println("删除节点:"+sub);
break;
}
tem=tem.next;
}
}
//查找节点
public void findSubject(int sub){
Subject tem = head;
while (true){
if (tem.next==null){
System.out.println("链表数据为空");
return;
}
if (tem.next.id == sub){
System.out.println("查找的节点数据为:" + tem.next.toString());
return;
}
tem=tem.next;
}
}
//链表合并
public void merge(Subject sub){
System.out.println("+++++++++++");
if (sub.next==null){
System.out.println("链表为空");
return;
}
Stack<Subject> stack = new Stack();
while (sub.next!=null){
stack.add(sub.next);
sub=sub.next;
}
while (stack.size()>0){
this.addSort(stack.pop());
}
}
}
class Subject{
int id;
String name;
Subject pre;
Subject next;
//构造器
public Subject(int id ,String name){
this.id = id;
this.name=name;
}
@Override
public String toString() {
return "Subject{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
运行结果:
环形链表(约瑟夫)
- 定义一个普通类peo,属性id、next
- 定义链表类,初始化first
- 添加:
- 传入int类型
- 如果传入的n小于1,返回
- 定义peo类中间值,值为空
- for循环遍历num从1开始
- 定义peo类型boy初始化i
- 当=1时,即第一个小孩,令first=boy,然后first指向自己构成环形.next=first,然后让中间值=first
- 往下遍历,boyid不为1后,中间值.next=boy、boy.next=first,再中间值=boy
- 遍历链表:
- 判断链表是否为空first==null
- first不能动,定义中间值
- while遍历,当中间值.next=first即可退出遍历,否则中间值=中间值.next继续遍历
- 实现约瑟夫:
- 方法传入参数:starNo从第几个小孩开始,countNum数几下,nums多少个小孩
- 检验传参,1<starNo>nums,first=null
- 创建中间值=first
- while循环开始,让中间值指向链表最后一个结点
- 中间值.next=first时退出
- 中间值=中间值.next
- for循环<starNo-1遍历节点,让first和中间值移动starNo-1次,移动到第一个报数节点上:first=first.next、中间值=中间值.next
- while循环节点报数,first和中间值同时移动,当只剩一节点时结束first=中间值
- for循环移动countNum-1次:first=first.next、中间值=中间值.next
- for结束,first就指向要出圈的节点,first=first.next、中间值.next=first
package SparseArrayAndQueues.link;
//单环形链表
public class CircleLink {
public static void main(String[] args) {
ChileLink boy = new ChileLink();
boy.addChilld(10);
boy.show();
boy.getChild(3,3,5);
}
}
class ChileLink{
private Child first = new Child(-1);
public void addChilld(int n){
if (n<1){
System.out.println("节点小于1,无效,返回");
return;
}
Child tem = null;
for (int i =1; i <= n ;i++){
Child boy = new Child(i);
if (i==1){
first = boy;
first.next=first;
tem=first;
}else {
tem.next=boy;
boy.next=first;
tem=boy;
}
}
}
//遍历
public void show(){
if (first==null){
System.out.println("链表没有数据");
return;
}
Child tem = first;
while (true){
System.out.println("孩子编号:"+tem.toString());
if (tem.next==first){
break;
}
tem=tem.next;
}
}
//实现约瑟夫
public void getChild(int statNo, int countNo, int nums){
if (statNo < 1 || statNo > nums || first == null){
System.out.println("传入的参数有误");
return;
}
Child tem = first;
while (tem.next!=first){
tem=tem.next;
}
for (int i = 0 ; i < statNo-1; i++){
first = first.next;
tem=tem.next;
}
while (tem!=first){
for (int j = 0; j<countNo-1;j++){
first=first.next;
tem=tem.next;
}
System.out.println("节点"+first.toString()+"出圈");
first=first.next;
tem.next=first;
}
System.out.println("最后一人"+first.next);
}
}
class Child{
int ID;
Child next;
public Child(int ID) {
this.ID = ID;
}
@Override
public String toString() {
return "Child{" +
"ID=" + ID +
'}';
}
}
六、栈
2021年8月10日09:36:29,阴
实现思路
- 定义类stackarray,定义栈大小int size,定义数组 int【】stack,栈顶int top= -1
- 构造器初始化
- 判断栈满top = size -1
- 判断栈空 top=-1
- 入栈
- 判断栈是否满,
- top++
- stack【top】 = value
- 出栈
- 判断是否为空
- 获取栈顶值,int val = stack【top】
- 栈顶下降 top--
- 遍历栈:从栈顶开始显示
- 判断是否空
- for循环 i=top
package SparseArrayAndQueues.stack;
public class stack {
public static void main(String[] args) {
stackarr sta = new stackarr(5);
sta.addStack(88);
sta.addStack(5);
sta.addStack(6);
sta.addStack(66);
sta.addStack(74);
sta.addStack(74);
sta.show();
sta.pop();
sta.pop();
sta.show();
sta.addStack(123);
sta.addStack(321);
sta.show();
}
}
class stackarr{
int maxSize;
int[] stack;
int top = -1;
//初始化
public stackarr(int size){
this.maxSize=size;
this.stack = new int[size];
}
//判断栈满
public boolean isFull(){
return top == maxSize-1;
}
//判断栈空
public boolean isEmpty(){
return top == -1;
}
//入栈
public void addStack(int num){
//判断栈满
if (isFull()){
System.out.println("栈已满,无法入栈");
return;
}
top++;
stack[top]=num;
System.out.println("入栈:stack["+top+"]="+stack[top]);
}
//出栈
public void pop(){
//判断栈空
if (isEmpty()){
System.out.println("栈已空,无法出栈");
return;
}
System.out.println("出栈:stack["+top+"]="+stack[top]);
top--;
}
//遍历栈
public void show(){
if (isEmpty()){
System.out.println("栈已空,无法遍历");
return;
}
for (int i = top; i!=-1;i--){
System.out.println("stack["+i+"]="+stack[i]);
}
}
}
栈实现计算器(加减乘除)
- 创建栈,可使用上面那个,在栈中扩展功能:
- 返回运算符的优先级
- if运算符==*或/则返回1,+-返回0其他返回-1
- 判断是不是运算符
- 计算方法,参数num1,num2,和运算符
- int res = 0 存放结果
- switch运算符
- num2 运算符 num1
- 返回res
- main
- String运算表达式
- 创建两个栈,一个放数字,一个放运算符
- 定义 int index(用于扫描),num1,num2,运算符,结果,char ch =‘’(将扫描到的char保存),String kenum=“”:存放拼接的多位数
- while扫描表达式
- ch = 表达式.substring(index,index+1).charAt(0):依次得到表达式的每个值
- 判断ch是运算符还是数字
- 如果是运算符,则判断当前符号栈是否为空
- 为空直接入栈,否则比较两个符号的优先级
- 若当前符号小于等于栈中符号优先级则从数栈中取两个数,在符号栈中取出一个运算符进行运算,结果入数栈,当前运算符入符号栈
- while循环,当符号栈为空时退出
- num1=栈.pop、num2=栈.pop、运算符=符号栈.pop
- res=计算方法(num1,num2,运算符)
- 结果入数栈,当前运算符入符号栈
- 当前符号优先级大于栈中则入符号栈
- 如果是数字则入数栈
- kenum += ch:处理多位数
- 如果ch已经是最后一位直接入栈.pop(integer.parseInt(kenum))
- 入栈.pop(integer.parseInt(kenum))
- 否则判断下个字符是不是数字,是就继续扫描,运算符则入栈。判断运算符方法(表达式.substring(index+1,index+2).charAt(0))
- 后一位是运算符:入栈.pop(integer.parseInt(kenum))
- 清空kenum=“”
- index++继续扫描表达式,
- 当index》=表达式长度时退出循环
- while循环取出两个栈中符号进行运算
- 如果符号栈为空则计算到最后的结果,即退出
- num1 = 数栈.pop、num2=数栈.pop,运算符=符号栈.pop
- res = 运算方法(num1,num2,运算符)
- 结果入栈
- 将 结果出栈
package SparseArrayAndQueues.stack;
public class mathStack {
public static void main(String[] args) {
String exp = "8-5*1-1+5*5-2*2+1+5/2-5/2";
stackarray numstack = new stackarray(10);
stackarray stack2 = new stackarray(10);
int index = 0;
int num1 = 0;
int num2 = 0;
int cor =0;
int res = 0;
char ch = 0;
String kenum = "";
while (true){
ch = exp.substring(index,index+1).charAt(0);
//判断是否是运算符
if (stack2.iscor(ch)){
//判断运算符栈是否为空
if (!stack2.isEmpty()){
if (stack2.cor(stack2.head()) >= stack2.cor(ch)){
while (true){
if (stack2.isEmpty()){
break;
}
num1 = numstack.pop();
num2 = numstack.pop();
cor = stack2.pop();
res = stack2.res(num1,num2,cor);
numstack.addStack(res);
}
stack2.addStack(ch);
}else {
stack2.addStack(ch);
}
}else {
stack2.addStack(ch);
}
}else {
kenum += ch;
if (index == exp.length()-1){
numstack.addStack(Integer.parseInt(kenum));
}else {
if (stack2.iscor(exp.substring(index+1,index+2).charAt(0))){
numstack.addStack(Integer.parseInt(kenum));
kenum="";
}
}
}
index++;
if (index >= exp.length()){
break;
}
}
while (true){
if (stack2.isEmpty()){
break;
}
num1 = numstack.pop();
num2 = numstack.pop();
cor = stack2.pop();
res = stack2.res(num1,num2,cor);
numstack.addStack(res);
}
System.out.println(exp+" = "+numstack.head());
}
}
class stackarray{
int maxSize;
int[] stack;
int top = -1;
//初始化
public stackarray(int size){
this.maxSize=size;
this.stack = new int[size];
}
//获取栈头
public int head(){
return stack[top];
}
//判断栈满
public boolean isFull(){
return top == maxSize-1;
}
//判断栈空
public boolean isEmpty(){
return top == -1;
}
//入栈
public void addStack(int num){
//判断栈满
if (isFull()){
System.out.println("栈已满,无法入栈");
return;
}
top++;
stack[top]=num;
//System.out.println("入栈:stack["+top+"]="+stack[top]);
}
//出栈
public int pop(){
//判断栈空
if (isEmpty()){
System.out.println("栈已空,无法出栈");
}
//System.out.println("出栈:stack["+top+"]="+stack[top]);
int res = stack[top];
top--;
return res;
}
//遍历栈
public void show(){
if (isEmpty()){
System.out.println("栈已空,无法遍历");
return;
}
for (int i = top; i!=-1;i--){
System.out.println("stack["+i+"]="+stack[i]);
}
}
//计算器功能----------------------------------
//判断符号优先级
public int cor(int cor){
if (cor == '*' || cor == '/'){
return 1;
}else if (cor == '+' || cor == '-'){
return 0;
}else{
return -1;
}
}
//判断是不是运算符
public boolean iscor(int cor){
return cor == '*' || cor == '/' || cor == '+' || cor == '-';
}
//计算
public int res(int num1,int num2,int cor){
int res = 0;
switch (cor){
case '+':
res = num2 + num1;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num2 * num1;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
}
代码测试结果:
2021年8月16日14:12:01,阴
前缀(波兰表达式)、中缀、后缀(逆波兰表达式)
中缀表达式:(3+4)*5-6
前缀表达式:-*+3456
后缀表达式:34+5*6-
正常表达式 逆波兰表达式
a+b ab+
a+(b-c) abc-+
a+(b-c)*d abc-d*+
a+b*(b-c) adbc-*+
a=1+3 a13+=
前缀求值:从右至左扫描表达式,碰到数字则压入栈中,碰到运算符则弹出两个数字,用运算符对它们做相应的计算,num1 运算符 num2,结果放入栈中到最后
后缀求值:从左至右扫描表达式,碰到数字则压入栈中,碰到运算符弹出两个数字,用运算符对它们做相应的计算,num2 运算符 num1,结果入栈,至最后。例如:34+5*6-:
- 从左至右,3和4入栈,
- 碰到+,弹出num1=4(栈顶),num2=3(次顶),num2+num1=7,7入栈
- 接着扫描到5,入栈
- 碰到*,弹出num1=5(栈顶),num2=7(次顶),num2*num1=35,35入栈
- 接着扫描到6,入栈
- 碰到-,弹出num1=6(栈顶),num2=35(次顶),num2-num1=29,29入栈
中缀表达式转后缀(不包含小数点)
- 初始化两个栈,运算符栈s1和中间结果栈s2
- 从左 至右扫描表达式,碰到数字入栈s2
- 碰到运算符,与其比较优先级s1:
- 如果,s1为空,或栈中顶部符号位"(",入栈s1
- 否则,若优先级比栈顶元素高,入栈s1
- 否则将s1栈顶元素弹出并压入s2,再继续(3)步骤
- 碰到括号时:
- 若是左括号"(",直接入栈s1
- 若是右括号")",则依次弹出s1栈顶,并压入s2,直到碰到有左括号,弹出左括号,这一对括号不用要了
- 重复上述步骤2至4,直至表达式结束
- 将s1中剩余运算符依次弹出并压入s2
- 将s2一次弹出压入s1再弹出即为逆波兰表达式
代码思路:
- 定义String 中缀表达式
- 定义方法,传参数为中缀表达式,将中缀表达式转为ArrayList
- 定义一个List类型为String
- 定义个指针用于遍历,int i = 0
- 定义 String str 对多位数拼接
- 定义char c 遍历到一个字符就传入c
- do..while循环,条件为 i < 参数.length
- 判断:如果c非数字,则加入到ArrayList中,条件为:c=传入值.charAt(i) < 48 || c=传入值.charAt(i) >57,list.add(""+c),然后后移i++
- 否则清空str = "",while循环,条件:i<传入值.length() && c=传入值.charAt(i) >= 48 || c=传入值.charAt(i) < =57,然后拼接str += c,后移i++
- 将str放入list
- 返回list
- 转换后缀方法,传参ArrayList
- 定义两个栈,类型String,s1符号栈,因为s2没有过弹栈操作,故可以用ArrayList代替第二个栈,这里用ArrayList表示 s2
- 增强for循环遍历传参:
- 判断:如果传参.matches("\\d+"),mathces匹配元素,+号表示可匹配多位数,如果是多位数,1位及更多。满足则直接添加到s2
- else if:碰到左括号则放入s1中,条件:传参.equals("(")
- else if:碰到右括号则依次弹出s1栈顶元素并放入s中,直到碰到左括号,
- while循环,条件:判断栈顶是否是左括号:!传参.peek().equals("(")
- 将弹出的元素加入s2,s2.add(s1.pop())
- 循环结束弹出左括号,将这一对括号丢掉:s1.pop()
- else:若都不满足
- 当遍历到的字符运算符优先级小于等于s1栈顶时,将s1栈顶弹出并加入s2,然后继续比较下一个栈顶:while循环:条件s1.size()!=0 && 当前运算符优先级小于等于s1栈顶运算符
- 循环结束将当前运算符压栈
- while遍历将s1中运算符弹出加入到s2,条件:s1.size()!=0
- 返回s2即是后缀表达式,不需要再逆转(因为没有使用两个栈)
逆波兰计算器
- 定义String逆波兰表达式,用空格分隔各个元素
- 将表达式传入ArrayList,辅助栈完成计算,创建个方法,将表达式存入ArrayList
- 将表达式分割,String[] split = 表达式.split(" "):根据双引号之间的元素分割,就是空格
- 创建一个List:List<String> list = new ArrayList<String>();
- 增强for循环把split存入list
- 返回list
- 创建方法,将ArrayList传入,求值
- 创建一个栈,增强for循环遍历 ArrayList
- 判断遍历到的值.mathces("\\d+"),mathces匹配,+号表示匹配多位数,如果是多位数,1位及更多
- 如果是数字则入栈
- 如果不是则取出两个数,数需要转型:Integer.parseInt(),然后值.equals("")判断值进行运算
- 将结果转回String入栈,可以拼接或其他方式转
- 返回最后的数,需转型
2021年8月18日09:43:37 晴
代码实现,中缀转后缀,逆波兰计算器:
package SparseArrayAndQueues.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
/**
* @author create by lbin
* @description 中缀转后缀(不包含小数),逆波兰计算器
* @since 2021/8/18 9:45
*/
public class calculateStack {
public static void main(String[] args) {
//定义中缀表达式,转成list
Scanner scanner = new Scanner(System.in);
//String exp = scanner.next();
String exp = "2+(2*3)/(2*3)-5";
System.out.println(exp);
List<String> demo = toStringList(exp);
//转后缀
List<String> suffx = toSuffixExp(demo);
System.out.println(suffx);
//求值
System.out.println(exp+=" = "+calculateSuffix(suffx));
}
public static List<String> toStringList(String exp){
/**
* @description://定义方法,将中缀表达式转为ArrayList
* @param exp
* @return {@link List< String>}
* @throws
* @author create by lbin
* @date 2021/8/18 10:15
*/
//定义List,将表达式放进去
ArrayList<String> list = new ArrayList<String>();
int i = 0;
String str;
char cc;
do {
/*
(cc = exp.charAt(i)) < 48 || (cc = exp.charAt(i)) >57
判断他们是不是数字,此为非数字,
*/
if ((cc = exp.charAt(i)) < 48 || (cc = exp.charAt(i)) >57){
list.add("" + cc);
i++;
}else {
str = "";
while (i < exp.length() && (cc = exp.charAt(i)) >= 48 && (cc = exp.charAt(i)) <=57){
str += cc;
i++;
}
list.add(str);
}
}while (i<exp.length());
return list;
}
public static List<String> toSuffixExp(List<String> infix){
/**
* @description 转为后缀表达式
* @param infix
* @return {@link List< String>}
* @throws
* @author create by lbin
* @date 2021/8/18 10:59
*/
Stack<String> stack = new Stack<>();
List<String> list = new ArrayList<>();
for (String i : infix) {
if (i.matches("\\d+")){
list.add(i);
}else if (i.equals("(")){
stack.push(i);
}else if (i.equals(")")){
while (!stack.peek().equals("(")){
list.add(stack.pop());
}
stack.pop();
}else {
if (stack.size() != 0 && stack.peek().equals("(")){
stack.push(i);
}else {
while (stack.size() != 0 && judge(i) <= judge(stack.peek())){
list.add(stack.pop());
}
stack.push(i);
}
}
}
while (stack.size()!=0){
list.add(stack.pop());
}
return list;
}
public static int judge(String i){
/**
* @description 比较+-/*优先级,
* @param i
* @return {@link int}
* @throws
* @author create by lbin
* @date 2021/8/18 10:59
*/
if (i.equals("+")){
return 0;
}else if (i.equals("-")){
return 0;
}else if (i.equals("*")){
return 1;
}else if (i.equals("/")){
return 1;
}else {
System.out.println("只可以加减乘除哦,其他符号不算");
return 10;
}
}
public static int calculateSuffix(List<String> suffix){
/**
* @description:逆波兰表达式求值(/*-+)
* @param suffix
* @return {@link int}
* @throws
* @author create by lbin
* @date 2021/8/18 11:35
*/
Stack<String> stack = new Stack<>();
for (String s : suffix) {
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 = num2 + num1;
}else if (s.equals("-")){
res = num2 - num1;
}else if (s.equals("*")){
res = num2 * num1;
}else if (s.equals("/")){
res = num2 / num1;
}
stack.push(String.valueOf(res));
}
}
return Integer.parseInt(stack.pop());
}
}
运行结果
七、递归
方法自己调用自己。
1、迷宫问题
- 创建二维数组,模拟迷宫,1表示墙,0表示空旷的地方
- 将迷宫的四周变为墙,在中间设置两块挡板
- 利用递归让老鼠从出发点【1,1】走到终点【】,2表示路径可行,3表示走过走不通,老鼠前进的方向优先级:下=》右=》上=》左
- 创建找迷宫的方法,传入二维数组,起点
- 先判断递归方法终点是否为2,2表示已经走到,返回true
- 否则:
- 判断当前起点是否走过,没有走过则将当前点标为2,假设可以走通,继续判断
- 判断:条件为递归,分别向下=》右=》上=》左分别探路,满足则返回true
- 否则标记为当前点为3,走不通,返回false
- 否则说明该点可能是1,2,3则返回false
代码实现:
package SparseArrayAndQueues.recursion;
/**
* @author create by lbin
* @description:递归-迷宫问题
* @since 2021/8/18 15:15
*/
public class mazequestion {
public static void main(String[] args) {
//定义二维数组表示迷宫,给迷宫装墙和栅栏
int arr[][] = new int[10][10];
for (int i = 0 ; i < arr.length; i++){
arr[0][i] = 1;
arr[9][i] = 1;
}
for (int i = 0 ; i < arr.length; i++){
arr[i][0] = 1;
arr[i][9] = 1;
}
//添加栅栏
arr[5][1] = 1;arr[5][2] = 1;arr[5][3] = 1;arr[5][4] = 1;
arr[4][4] = 1;arr[3][4] = 1;arr[2][4] = 1;arr[5][4] = 1;
arr[3][5] = 1;arr[3][6] = 1;arr[3][7] = 1;
System.out.println("迷宫走之前的图");
for (int i = 0; i < arr.length; i++){
for (int j = 0; j < arr[0].length; j++){
System.out.printf("%d\t",arr[i][j]);
}
System.out.println();
}
System.out.println("调用方法后老鼠路线");
findDirection(arr,1,1,8,3);
for (int i = 0; i < arr.length; i++){
for (int j = 0; j < arr[0].length; j++){
System.out.printf("%d\t",arr[i][j]);
}
System.out.println();
}
}
public static boolean findDirection(int[][] map ,int i,int j,int k,int l){
/**
* @description 老鼠迷宫问题,递归用法
* @param map
* @param i:起点横坐标
* @param j:起点纵坐标
* @param k:终点横坐标
* @param l:终点纵坐标
* @return {@link boolean}
* @throws
* @author create by lbin
* @date 2021/8/18 16:51
*/
if (map[k][l] == 2){
return true;
}else {
if (map[i][j] == 0){
map[i][j] = 2;
//老鼠前进方向为:下右上左,可根据需求改变
if (findDirection(map,i+1,j,k,l)){
return true;
}else if (findDirection(map, i, j+1, k, l)){
return true;
}else if (findDirection(map, i-1, j, k, l)){
return true;
}else if (findDirection(map, i, j-1, k, l)){
return true;
}else {
map[i][j] = 3;
return false;
}
}else {
return false;
}
}
}
}
2021年8月19日08:43:58 晴
2、八皇后问题(回溯算法)
描述:在8X8的国际象棋摆放八个皇后,任意两个皇后不能处于同一行,同一列,同一斜线,有多少中摆法
思路分析:
- 第一个皇后先放在第一行第一列
- 第二个皇后放在第二行第一列,然后判断是否OK,如果不OK,继续放在第二列第三列,依次把所有列都放完,直到找到合适位置
- 继续第三个皇后,继续第一列、第二列、第三列...直至第八个皇后也能放在一个不冲突的位置
- 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正解,全部得到
- 然后回头继续第一个皇后放第二列,后面继续循环1,2,3,4的步骤
- 定义一个max表示有多少个皇后,int max = 8,
- 定义一个一维数组,保存皇后的位置,int array[] = new int[max]
- 定义打印方法,打印皇后位置
- 定义检测方法,检测当前皇后与已摆放的皇后是否冲突,传入参数:第n个皇后
- for循环遍历:i<n
- 判断第n个皇后是否和已摆放的皇后处于同一列:array[i] == array[n] ||判断第n个皇后是否和已摆放的皇后处于同一斜线:Math.abs(n-i) == Math.abs(array[n] - array[i])即可,math.abs:取绝对值。都满足则返回false
- 循环结束后返回true
- 定义摆放皇后的方法,传入参数:第n个皇后
- 首先判断n是否等于max,是则返回,n=8表示已经摆完8个,因为int[8]
- for循环一次放入皇后,判断是否冲突,i<max
- 先把皇后n放到该行的第一列:array[n] = i;
- 判断是否冲突即上个检测方法(n)
- 如果不冲突则继续摆放n+1个皇后,开始递归
代码:
package SparseArrayAndQueues.recursion;
/**
* @author create by lbin
* @description 八皇后问题改造
* @since 2021/8/19 10:54
*/
public class Queen82 {
int max = 8;
//八皇后棋盘
int position[][] = new int[max][max];
//皇后每列的位置
int array[] = new int[max];
static int count = 0;
public static void main(String[] args) {
Queen82 queen82 = new Queen82();
queen82.printPosition();
queen82.check(0);
System.out.printf("八皇后共有%d种摆放方式",count);
}
public void printPosition(){
/**
* @description:遍历八皇后位置图,最下面一行是每列皇后的位置
* @param
* @return
* @throws
* @author create by lbin
* @date 2021/8/19 11:01
*/
for(int i = 0; i < position.length; i++){
for (int j = 0 ; j < position[i].length; j++){
System.out.printf("%d\t",position[i][j]);
}
System.out.println();
}
//打印每列皇后的位置
for (int i = 0; i < array.length; i++){
System.out.printf("%d\t",array[i]);
}
System.out.println();
System.out.println("——————————————————————————————");
}
public boolean judge(int n){
/**
* @description:检测当前皇后是否和已摆放的皇后冲突
* @param n
* @return {@link boolean}
* @throws
* @author create by lbin
* @date 2021/8/19 11:12
*/
for (int i = 0; i < n; i++){
if (array[i] == array[n] ||
Math.abs(n-i) == Math.abs(array[n] -array[i])){
return false;
}
}
return true;
}
public void check(int n){
/**
* @description:摆放皇后
* @param n
* @return
* @throws
* @author create by lbin
* @date 2021/8/19 11:12
*/
if (n==max){
count++;
for(int i = 0; i < position.length; i++){
for (int j = 0 ; j < position[i].length; j++){
position[i][j] = 0;
}
}
for (int j = 0; j < max; j++){
position[j][array[j]] = 1;
}
printPosition();
return;
}
for (int i = 0; i < max; i++){
array[n] = i;
if (judge(n)){
check(n+1);
}
}
}
}
部分运行截图(92种解法,全部截图太长):
2021年8月20日09:32:44,晴
八、排序算法
1、算法的时间复杂度
时间频度:一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。时间复杂度在n趋于无穷时,略一些数,例如:n+1 =》可省略1、n*n+n =》可省略n等等
一个算法中的语句执行次数成为语句频度或时间频度。例:
int total = 0;
int end = 100;
//方式一:使用for循环计算
for (int i= 0; i<=end; i++){
total += i;
}
/*
该算法使用for循环,需要相加相加100次,
又因为加完之后还要再判断一次,故此
时间复杂度为:101次,而且时间复杂度随end变化,
故时间复杂度:T(n)=n+1 =》O(n)
*/
//方式二:直接计算
total = (end+1)*end/2;
/*
该算法值运行一次,且无论end怎么变化时间复杂度都不变
故此:T(n)=1 =》O(1)
*/
常见的时间复杂度(下列从小到大排列):
- 常数阶:O(1)
- 对数阶:O(log₂n )
- 线性阶:O(n)
- 线性对数阶:O(nlog₂n)
- 平方阶:O(n²)
- 立方阶:O(n³)
- k次方阶:O(nⁿ)
- 指数阶:O(2ⁿ)
案例:
//常数阶:O(1)
int i = 1;
int j = 1;
++i;
j++;
int m = i + j;
//对数阶:O(log₂n )
int i = 1;
while(i < n){
i = i * 2;
}
//线性阶:O(n)
for(int i = 1; i<n; i++){
int j = i;
}
//线性对数阶:O(nlog₂n)
for(int m = 1;m<n; m++){
i = 1;
while(i<n){
i*2;
}
}
//平方阶:O(n²)
for(int i = 1; i<n; i++){
for(int j = 1; j<n; j++){
System.out.println(i+j);
}
}
//立方阶:O(n³),三层for循环
//k次方阶:O(nⁿ)k层for循环
//指数阶:O(2ⁿ)
2、空间复杂度
类似于时间复杂度的讨论,一个算法的空间复杂度定义为该算法所耗费的存储空间,它也是问题规模n的函数。
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的度量。有的算法需要占用的临时工作单元数与解决问题的规模n相关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序和归并排序
在做算法分析时,主要讨论的是时间复杂度。从用户使用体验看,更看重的是程序执行的速度,一些缓存产品(redis、memcache)和算法(基数排序)本质就是用空间换时间
3、冒泡排序
思路
- 一共进行数组.length-1次排序的循环
- 每一趟排序的次数逐渐减少
- 如果我们发现在某次排序中没有发生一次交换,就提前结束
代码思路:
- 定义数组
- 定义临时变量,方便交换:int tem=0;
- 定义标识变量,标识是否进行过交换,boolean flag =false
- for循环,共进行array.length-1次排序
- for循环,i<array.length-1-i,排序的次数逐渐减少,因为每一趟都会确定最大值
- 判断,如果当前处于i位置的数比i+1的大,交换
- flag = true:标志进行交换过
- tem = array[i]:把当前值存入临时变量
- array[i] = array[i+1]:把下一位置的值赋给当前位置
- array[i+1] = tem:将临时变量值存入下一位置,交换完成
- 判断flag是否交换过,如果没交换过,直接退出,交换过则令flag=false继续循环
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description
* @since 2021/8/20 15:19
*/
public class BubbleSort {
public static void main(String[] args) {
int array[] = {8,2,55,-5,-6,100,6,99};
//int array[] = {1,2,3,4,5};
System.out.println("排序前: "+Arrays.toString(array));
bubbleSort(array);
}
/**
* @description
* @param array
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/20 16:23
*/
public static int[] bubbleSort(int[] array){
int tem = 0;
boolean flag = false;
for (int i = 0; i < array.length-1; i++){
for (int j = 0; j < array.length-1-i; j++){
if (array[j]>array[j+1]){
flag = true;
tem = array[j];
array[j] = array[j+1];
array[j+1] = tem;
}
}
System.out.printf("第%d次排序",i+1);
System.out.println(Arrays.toString(array));
if (!flag){
break;
}else {
flag = false;
}
}
return array;
}
}
4、选择排序
分析思路:
- 第一轮排序:找到原始数组中的最小值 ,最小值下标与首位下标交换值
- 第二轮排序:找到除首位的最小值的下标,与次首位交换值
- 依次进行
- 共有array.length-1次排序
- 每一轮排序又是一次循环
- 假定当前数是最小值
- 与后面的每个数比较,如果发现更小的,重新定义最小值,得到下标
- 遍历到数组的最后时,得到本轮最小数和下标
代码思路:
- for循环,条件:i<array.length-1:共进行几次排序
- 定义最小值下标,int minIndex = i:此时假设这里的数最小
- 定义临时变量最小值,int min = array[i]
- for循环遍历:j = i +1; j<array.length:遍历数组
- 判断:如果假定最小值大于遍历到的值:则重新给假定值和下标复制
- 判断假定下标是否改变,改变则交换值,array[minIndex] = array[i],array[i]=min
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description
* @since 2021/8/20 16:23
*/
public class SelectSort {
public static void main(String[] args) {
int array[] = {8,2,55,-5,-6,100,6,99};
//int array[] = {1,2,3,4,5};
System.out.println("排序前: "+Arrays.toString(array));
selectSort(array);
}
/**
* @description
* @param array
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/20 16:32
*/
public static int[] selectSort(int[] array){
int count = 0; //用于查看经历了几次交换
for (int i = 0; i < array.length-1; i++){
int minIndex = i;
int minValue = array[minIndex];
for (int j = i + 1; j < array.length; j++){
if (minValue < array[j]){
minIndex = j;
minValue = array[j];
}
}
if (minIndex != i){
count++;
array[minIndex] = array[i];
array[i] = minValue;
System.out.println("第"+count+"次交换"+ Arrays.toString(array));
}
}
return array;
}
}
运行结果:
5、插入排序
分析:
- 首先有个初始数组,首位默认为有序排列,后面的默认为无序排列
- 第一次插入,无序排列首位A与有序排列末位B比较,大则排在后面,小则与有序排列次末位C比较,小则插进去,B后移,大则继续比较,直至比完。排序后A就变为有序列表的一员
- 第二次插入,无序排列的新一个首位A与有序排列的新一个末位B比较,大则排在后面,小则与有序排列新一为次末位C比较,小则插进去,B后移,大则继续比较,直至比完。排序后A就变为有序列表的一员
- 重复至无序列表结束。
代码思路:
- for循环:i<array.length-1,只需排列array.length-1次
- 定义临时变量保存待插入的数insertVal = array[i+1]
- 定位到待插入数前一个的下标int insertIndex = i
- while循环防止下标越界,insertIndex>=0&&insertVal < array[insertIndex],当下标不越界和待插入值小于待插入值前一位,继续循环
- 满足则把待插入值的前一位移动到待插入值的位置
- 下标继续向前走
- 退出循环时表明找到要插入的位置:array[insertIndex+1]=insertVal
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description
* @since 2021/8/20 17:22
*/
public class InsertSort {
public static void main(String[] args) {
int array[] = {8,2,55,-5,-6,100,6,99};
//int array[] = {1,2,3,4,5};
System.out.println("排序前: "+Arrays.toString(array));
insertSort(array);
}
/**
* @description
* @param array
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/20 17:28
*/
public static int[] insertSort(int[] array){
for (int i = 0; i < array.length-1; i++){
int insertValue = array[i+1];
int insertIndex = i;
while (insertIndex >= 0 && insertValue < array[insertIndex]){
array[insertIndex+1] = array[insertIndex];
insertIndex--;
}
array[insertIndex+1] = insertValue;
System.out.printf("第%d次排序",i+1);
System.out.println(Arrays.toString(array));
}
return array;
}
}
2021年8月21日20:31:22,晴
6、希尔排序
希尔排序是改良后的插入排序,也称缩小增量排序。
分析:
- 首先将数组分为group=array.length/2组,只要group》0就能继续分组,group/2
- 组内比较大小,交换位置,直至结束
交换法代码思路:
- 定义中间值tem=0便于交换
- for循环分组,分组一次排序一次,直至不能分组,int gro = array.length;gro>0;gro/=2
- for循环遍历各组int i=gro,i<array.length;i++
- for循环遍历小组元素,j=i-gro;j>=0;j-=gro
- 判断小组两个元素哪个大,前面大则换位子array[j]>arr[j+gro]
- tem=arr[j]中间值存储
- array[j]=array[j+gro]
- array[j+gro]=tem
移动法代码思路:
- for循环分组,分组一次排序一次,直至不能分组,int gro = array.length;gro>0;gro/=2
- for循环遍历小组gro,对其所在的组进行插入排序
- 定义临时变量保存待插入的值和下标int j = i,int tem = array[j]
- 判断当前下标的值是否小于小组前一头的值,array[j]<array[j-gro]
- while遍历当j-gro>=0 &&中间值tem<array[j-gro]时退出
- 前一头的值小于中间值则前一头移动到后面,array[j]=array[j-gro]
- j-=gro向前移动
- array[j]=tem,while循环已找到要插入的位置
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description:希尔排序
* @since 2021/8/22 11:49
*/
public class HillSort {
public static void main(String[] args) {
int array[] = {8,2,55,-5,-6,100,6,99};
//int array[] = {1,2,3,4,5};
System.out.println("排序前: "+ Arrays.toString(array));
//hillSort1(array);
System.out.println("排序后: "+Arrays.toString(hillSort2(array)));
}
/**
* @description:交换法
* @param array
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/22 11:58
*/
public static int[] hillSort1(int[] array){
int tem = 0;
int count = 0;
for (int group = array.length/2;group>0;group/=2){
for (int i =group;i<array.length;i++){
for (int j = i-group;j>=0;j-=group){
if (array[j]>array[j+group]){
tem=array[j];
array[j]=array[j+group];
array[j+group]=tem;
}
}
}
System.out.printf("第%d次排序",++count);
System.out.println(Arrays.toString(array));
}
return array;
}
/**
* @description:位移法
* @param array
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/22 12:02
*/
public static int[] hillSort2(int[] array){
int count = 0;
for (int group = array.length/2;group>0;group/=2){
for (int i = group;i<array.length;i++){
int j =i;
int tem = array[i];
if (array[j]<array[j-group]){
while (j-group>=0 && tem<array[j-group]){
array[j]=array[j-group];
j-=group;
}
array[j]=tem;
//System.out.printf("第%d次排序",++count);
//System.out.println(Arrays.toString(array));
}
}
}
return array;
}
}
2021年8月23日08:49:10,晴
7、快速排序
分析:
- 在数组中间选取一个值及下标,比值大的放右边,比值小的放左边,
- 同理,在右边中间选取一个值及下标,比值大的放右边,比值小的放左边(第二个选取值左边,不能插到第一个选取值前面),左边同理,直到不能再分
代码思路:
- 排序方法,传入数组,扫描左边的起始值(从左至右),扫描右边的起始值(从右至左开始扫描)
- int l = left;int r= right;辅助值
- 确定中间值,int pivot = array[(left+rigth)/2]
- 定义临时变量,交换时使用int tem = 0;
- while循环:l<r,为了把比中间值小的放左边,大的放右边
- while循环,左边找到比中间值大的值,array[l]<中间值,l+=1
- while循环,右边找到比中间值大的值,array[r]》中间值,l-=1
- 此时要么已找到,要么没有,判断l>=r:满足表示指针走到中间值,左边的已经都是小于中间值,右边的大于中间值,退出循环
- 交换:tem = arrayl];array[l]=array[r];array=tem
- ##交换后,判断array[l]和array[r]是否等于中间值,等于则r-=1前移,l+=1后移
- 判断r==l,此时会栈溢出,需要l+=1,r-=1
- 左边递归,需要在left<r的前提,if(left<r){排序方法(array,left,r)}
- 右边递归,需要在right>l的前提,if(right>l){排序方法(array,l,right)}
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description
* @since 2021/8/23 10:59
*/
public class QuickSort {
static int count = 0;
public static void main(String[] args) {
int[] array = {8,2,55,-5,-6,100,6};
// int[] array = new int[80000000];
// for (int i = 0; i < 80000000; i++){
// array[i] = (int) (Math.random() * 100000000); //生产一个【0,100000000)的数
// }
//String Random = Math.random() * 100000000 + " " + System.currentTimeMillis();
//System.out.println(Random);
System.out.println("排序前: "+ Arrays.toString(array));
System.out.println("排序后: "+Arrays.toString(quickSort(array,0,array.length-1)));
// long timeMillis1 = System.currentTimeMillis();
// System.out.println("排序前: " + timeMillis1);
// quickSort(array,0,array.length-1);
// long timeMillis2 = System.currentTimeMillis();
// System.out.println("排序后: "+ timeMillis2);
// System.out.println("共进行"+count+"次移位");
// System.out.println("共花费"+(timeMillis2-timeMillis1)+"毫秒");
}
/**
* @description
* @param array
* @param left
* @param right
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/23 13:42
*/
public static int[] quickSort(int[] array, int left, int right){
int l = left;
int r = right;
int middle = array[(left+right)/2];
int temp = 0;
//System.out.println(middle);
while (l<r){
while (array[l]<middle){
l++;
}
while (array[r]>middle){
r--;
}
if (l>=r){
break;
}
temp = array[l];
array[l] = array[r];
array[r] = temp;
//count++;
System.out.println("第"+(++count)+"次移位"+Arrays.toString(array));
if (array[l] == middle){
l++;
}
if (array[r] ==middle){
r--;
}
}
if (r == l) {
l++;
r--;
}
if (left<r){
quickSort(array,left,r);
}
if (right > r) {
quickSort(array,l,right);
}
return array;
}
}
8、归并排序
分析:
- 将一组数组从中间一分为二,左右再分,分到单个比较大小,合并到两边各自排好序
- 设两个指针,指向两边最小值,
- 左右比较,左比较小则取出放入另一个新数组,左指针右移,继续比较,右比较小则放入新数组,右指针右移,直至放完。
代码思路:
- 定义合并方法,传参数组,左边有序序列的初始索引,中间索引,右边索引,返回数组
- 初始化左右初始索引,int i = left;int j = mid + 1;
- 定义返回的数组即索引,int t = 0
- while循环将小的元素先放入返回数组,直到一侧结束,i<=mid && j<=right
- 判断左右两侧哪边大,array[i]<=array[j]
- 左侧放入返回数组,移位
- reArray[t]=array[i];t++;i++
- 否则右侧放入数组,移位
- reArray[t]=array[j];t++;j++
- while循环一侧已全部放入返回数组,另一侧还有剩余则依次填充
- 左侧条件:i<=mid,返回数组[t]=array[i];i++;t++
- 右侧条件:j<=right,返回数组[t]=array[j];j++;t++
- 将返回数组拷贝到初始数组
- 重新给t赋值t=0
- 定义临时索引int temle = left
- while(temle <= right)
- array[temle] = reArray[t]
- t++;
- temle++;
- 定义分解方法,传参数组,左边初始索引,末位索引,中间数组
- 判断左侧索引小于右侧索引
- 定义中间索引 int mid = (left+right)/2
- 向左递归分解:分解方法(array,left,mid,中间数组)
- 向右递归分解:分解方法(array,mid+1,right,中间数组)
- 合并方法(array,left,mid,right,中间数组)
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description:归并排序
* @since 2021/8/21 20:37
*/
public class MergeSort {
public static void main(String[] args) {
int array[] = {5,4,3,2,1,7};
int[] reArray = new int[array.length];
mergeSort(array, 0, array.length - 1,reArray);
System.out.println(Arrays.toString(array));
}
/**
* @description
* @param array
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/23 15:04
*/
public static void mergeSort(int[] array,int left,int right,int[] reArray){
if (left < right){
int mid = (left + right)/2;
mergeSort(array,left,mid,reArray);
mergeSort(array,mid+1,right,reArray);
merge(array,left,mid,right,reArray);
}
}
/**
* @description
* @param array
* @param left
* @param mid
* @param right
* @return {@link int[]}
* @throws
* @author create by lbin
* @date 2021/8/23 15:04
*/
public static void merge(int[] array,int left,int mid, int right,int[] reArray){
int i = left;
int j = mid+1;
int t = 0;
while (i <= mid && j <= right) {
if (array[i] <= array[j]){
reArray[t] = array[i];
t+=1;
i+=1;
}else {
reArray[t] = array[j];
t+=1;
j+=1;
}
}
while (i <= mid){
reArray[t] = array[i];
i+=1;
t+=1;
}
while (j<=right){
reArray[t] = array[j];
j+=1;
t+=1;
}
t = 0;
int temLeft = left;
while (temLeft <= right){
array[temLeft] = reArray[t];
t+=1;
temLeft+=1;
}
}
}
9、基数排序
2021年8月24日10:24:38,阴
空间换时间,可能会导致内存不足
思路:
- 定义一个二维数组表示桶,array[10][arr.length],行10存放的是个十百千的0-9,列为待排序数组最大值,防止溢出,
- 定义一个一维数组list,记录每个桶存放的数据个数
- 定义max = arr[0],设为数组中的最大位数,
- for循环遍历arr.length:如果arr[i]>max,则max = arr【i】
- 得到最大数是几位数,maxLength = (max+"").length
- for循环,设int i =0,n=1;i<maxLength;i++,n*=10,n为位数,方便取出对应值
- for循环将arr放入桶中,j <arr.length
- int arrEle = arr[j] / n %10:得到对应位数的值,
- array[arrEle][list[arrEle]] = arr[j]:将对应的数据放入对应的桶中,
- list[arrEle]++,放入桶中后,记录每个桶存放的数据个数增加,
- int index = 0,放入初始数组时初始数组对应下标变化
- for循环遍历每个桶的记录数组,若不为0则表示有数据,k<list.length
- list【k】!=0判断数组对应的桶是否有数据,
- 有数据则for循环该桶放入初始数组
- arr[index++]=array[k][l]
- 判断结束后将记录对应数据的数组list对应的桶清空,list[k] =0;
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description:基数排序
* @since 2021/8/24 9:54
*/
public class BaseSort {
public static void main(String[] args) {
int array[] = {8,2,55,985,211,100,6,99};
//int array[] = {1,2,3,4,5};
System.out.println("排序前: "+Arrays.toString(array));
System.out.println("排序后: "+Arrays.toString(baseSort(array)));
}
public static int[] baseSort(int[] arr){
//获取数组最大值位数
int max = arr[0];
for (int i = 0; i < arr.length; i++){
if (arr[i] > max) {
max = arr[i];
}
}
int maxLength = (max+"").length();
//定义二维数组表示桶:行=十个桶=》0.1.2.3.4.5.6.7.8.9————列=桶的最大容量
int[][] array = new int[10][arr.length];
//对应桶的存放的数据的数量
int[] list = new int[10];
//n为位数,方便取出对应位数值,个十百千的位数
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
//循环取出对应位数的值,依次个十百千万
for (int j = 0; j < arr.length; j++) {
//取出对应位数的值,即桶的下标
int arrEle = arr[j] / n % 10;
//放入对应的桶中arrEle,list[arrEle]记录的是对应桶的数量,也标记桶的位置
array[arrEle][list[arrEle]] = arr[j];
//list[arrEle]对应桶的数据增加,则记录数也增加
list[arrEle]++;
}
//此时已经全部放入桶,下面取出
//定义放入初始数组时初始数组对应下标变化
int index = 0;
//遍历桶的记录数组list,判断是否有数据
for (int k = 0; k < list.length; k++) {
if (list[k]!=0){
for (int l = 0; l < list[k]; l++) {
arr[index++] = array[k][l];
}
}
//对应桶清空,方便下次循环存放
list[k] = 0;
}
System.out.println("第"+(i+1)+"轮 : " + Arrays.toString(arr));
}
return arr;
}
}
九、查找算法
1、线性(顺序)查找
思路:
- 传入数组和要查找的值,
- 遍历数组,如果相同则放入集合返回。
代码实例:
package SparseArrayAndQueues.find;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author create by lbin
* @description
* @since 2021/8/24 10:41
*/
public class Sequence {
public static void main(String[] args) {
int array[] = {8,2,55,-5,-6,100,6,99,6};
System.out.println("数组: "+ Arrays.toString(array));
List<Integer> list = sequence(array, 55);
if (list.size()==0) {
System.out.println("数组没有该值");
}else {
System.out.print("该值所处的下标为:");
for (Integer integer : list) {
System.out.print(integer+" ");
}
}
}
/**
* @description
* @param array
* @param value
* @return {@link List< Integer>}
* @throws
* @author create by lbin
* @date 2021/8/24 10:48
*/
public static List<Integer> sequence(int[] array, int value){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
list.add(i);
}
}
return list;
}
}
2、二分查找(需要数组有序)
思路 :
- 找到数组中间下标,mid=(left+right)/2
- 比较value和array[mid]
- 大于则说明在右边,向右递归
- 小于说明在左边,向左递归
- 等于说明找到
- 将array[mid]存入集合,
- 定义temp = mid -1向左扫描是否还有相同的数
- while循环true
- 判断temp < 0 || arr[temp] != value:时退出
- 否则左边的数=value,放入集合
- temp--继续遍历
- 重新给temp赋值=mid+1向右扫描是否还有相同的数
- while循环true
- 判断temp > arr.length-1 || arr[temp] != value:时退出
- 否则右边的数=value,放入集合
- temp++继续遍历
- 当left>right时遍历完还找不到则退出
package SparseArrayAndQueues.find;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author create by lbin
* @description
* @since 2021/8/24 11:44
*/
public class BinarySearch {
public static void main(String[] args) {
int array[] = {1,2,3,3,4,4,5,6,7,8,9,44,55,55,66,100,100,101};
System.out.println("数组: "+ Arrays.toString(array));
binarySearch(array, 4,0,array.length-1);
// if (list.size()==0) {
// System.out.println("数组没有该值");
// }else {
// System.out.print("该值所处的下标为:"+list);
// }
}
/**
* @description
* @param array
* @param value
* @param left
* @param right
* @throws
* @author create by lbin
* @date 2021/8/24 11:59
*/
private static void binarySearch(int[] array, int value, int left, int right) {
if (left >right){
return ;
}
int mid = (left + right) / 2;
if (value > array[mid]){
binarySearch(array,value,mid+1,right);
}else if (value < array[mid]){
binarySearch(array,value,left,mid-1);
}else {
List<Integer> list = new ArrayList<>();
int temp = mid - 1;
while (true){
if (temp < 0 || array[temp] != value) {
break;
}
list.add(temp);
temp--;
}
temp = mid + 1;
while (true){
if (temp > array.length-1 || array[temp] != value) {
break;
}
list.add(temp);
temp++;
}
list.add(mid);
System.out.print("该值所处的下标为:"+list);
return;
}
}
}
2021年8月25日08:52:44,晴
3、插值查找
类似二分查找,只是mid不同,插值查找的mid:
int mid = left + (right - left) * (value - array[left]) / (array[right] - array[left])
在数据量较大,分布均匀的情况下查找速度较快,如果关键字分布不均匀,不一定比二分查找好
package SparseArrayAndQueues.find;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author create by lbin
* @description : 插值查找
* @since 2021/8/25 9:29
*/
public class InsertValueSearch {
private static int count;
public static void main(String[] args) {
//int array[] = {1,2,3,3,4,4,5,6,7,8,9,44,55,55,66,100,100,101};
int[] array = new int[100];
for (int i = 1; i < 100; i++) {
array[i]=i;
}
System.out.println("数组: "+ Arrays.toString(array));
insertSearch(array, 4,0,array.length-1);
}
/**
* @description
* @param array
* @param value
* @param left
* @param right
* @return
* @throws
* @author create by lbin
* @date 2021/8/25 9:44
*/
private static void insertSearch(int[] array, int value, int left, int right) {
System.out.println("第"+(++count)+"次查找");
if (left >right || value < array[left] || value > array[right]){
return ;
}
int mid = left + (right - left) * (value - array[left]) / (array[right] - array[left]);
if (value > array[mid]){
insertSearch(array,value,mid+1,right);
}else if (value < array[mid]){
insertSearch(array,value,left,mid-1);
}else {
List<Integer> list = new ArrayList<>();
int temp = mid - 1;
while (true){
if (temp < 0 || array[temp] != value) {
break;
}
list.add(temp);
temp--;
}
temp = mid + 1;
while (true){
if (temp > array.length-1 || array[temp] != value) {
break;
}
list.add(temp);
temp++;
}
list.add(mid);
System.out.print("该值所处的下标为:"+list);
return;
}
}
}
4、斐波那契(黄金分割法)查找
原理与前两种相似,改变了中间结点mid,mid位于黄金分割点附近:mid = left + F[k-1]-1,但顺序表的长度不一定刚好等于F[k-1],故此需要增加值F[k-1],
斐波那契数列{1,1,2,3,5,8,13,21,34,55},从第3个数开始,该数n=(n-1)+(n-2)即前两个数的和,越往后,两数之间越无限接近黄金分割值0.618
思路:
- 创建一个斐波那契数列,后面mid=left+F[k-1]-1需要使用
- 定义left=0,right=array.lenght-1
- int k =0;表示黄金分割值的下标
- int mid =0
- int F[] = fib()获取前面创建的的斐波那契数列
- while获取黄金分割值:right > F[k]-1
- k++
- F[k]的值可能大于传入数组A的长度,故此需要使用Arrays类,构建新数组指向A[],int[] temp = Arrays.copyOf(A,F[k])
- 把新数组填充for(i=high+1;i<temp.length)
- temp[i] =a[hight]
- while:left <= right找到需要查找的值
- mid = left + F[k-1] - 1
- 判断value < temp[mid]
- right = mid -1;k--
- else if value > temp[mid]
- left = mid +1k-=2
- else
- 判断mid<=high,小的是查找值的下标,返回
代码实例
package SparseArrayAndQueues.find;
import java.util.Arrays;
/**
* @author create by lbin
* @description
* @since 2021/8/25 16:33
*/
public class FibonacciSearch {
public static void main(String[] args) {
int array[] = {1,2,3,3,4,4,5,6,7,8,9,44,55,55,66,100,100,101};
System.out.println("数组: "+ Arrays.toString(array));
System.out.println("下标为:"+fibonacciSearch(array, 100));
}
public static int[] Fibonacci(int length){
int[] fibonacci = new int[length+2];
fibonacci[0] = 1;
fibonacci[1] = 1;
for (int i = 2; i < fibonacci.length; i++){
fibonacci[i] = fibonacci[i-1] + fibonacci[i-2];
}
return fibonacci;
}
/**
* @description
* @param array
* @param value
* @return {@link int}
* @throws
* @author create by lbin
* @date 2021/8/25 17:11
*/
public static int fibonacciSearch(int[] array, int value){
int left = 0;
int right = array.length-1;
int k = 0;
int mid = 0;
int f[] = Fibonacci(array.length);
while (right > f[k]){
k++;
}
int temp[] = Arrays.copyOf(array,f[k]);
for (int i = right + 1; i < temp.length; i++) {
temp[i] = array[right];
}
while (left <= right){
mid = left + f[k-1] -1;
if (value < temp[mid]){
right = mid;
k--;
}else if (value > temp[mid]){
left = mid+1;
k -= 2;
}else {
if (mid < right){
return mid;
}else {
return right;
}
}
}
return -1;
}
}
2021年8月26日08:44:19,晴
十、哈希表(散列)
数组内容指向链表等,
代码思路:
- 创建员工类:,
- 定义属性int id,String name,指向下一个 Emp next
- 构造方法,构造id,name
- 创建链表类:
- 创建头员工指针head
- 链表增加员工,传入员工Emp
- 判断头节点是否为空,为空则增加第一个员工,返回
- 不为空则定义辅助指针cur=head,辅助定位到链表最后
- while循环定位到链表最后true
- 判断cur.next==null,为空表示到了链表最后,退出循环
- cur=cur.next:后移
- cur.next = 员工。加入链表
- 遍历链表信息
- 判断链表是否为空,head==null,空则返回
- 定义辅助指针=head
- while循环遍历
- sout输出员工信息,
- 判断节点.next是否为空,空退出
- 指针后移
- 查找员工:传参员工ID
- 判断链表是否为空head=null,为空返回空
- 定义辅助指针=head
- while循环查找true
- 判断传入的id与指针id是否相等,等则退出循环
- 判断指针下个节点是否为空,为空则设指针为空,退出循环
- 指针=指针.next 后移
- 返回员工指针
- 删除员工,传参ID
- 判断链表头部是否为空,为空返回,
- 判断头部id是否等于待删除ID,
- 等于:head = head.next;返回
- 定义指针=head便于遍历
- while循环遍历链表:true
- 判断指针.next是否为空,为空返回
- 判断指针.next.id是否为待删除id,
- 是则让指针.next = 指针.next.next,把指向待删除元素的指针待删除元素的next,没有元素引用到待删除元素就成功删除。
- 创建HashTable类管理链表
- 定义属性,链表数组list,链表长度size
- 构造器,给哈希表传size长度,遍历哈希表,for(i<size)给每条链表new对象
- 添加员工
- 根据员工ID划分链表,int index = 员工.id %size,
- list[index].add(员工):添加到对应链表
- 遍历链表
- for循环size:list【i】.show()
- 查找员工,传入id
- 根据员工id定义到所属链表,int index = id % size
- list【index】.findById(id)即可拿到员工数据
- 删除,传入ID
- list[ id % size].delete(id)
代码实例:
package SparseArrayAndQueues.hashtable;
/**
* @author create by lbin
* @description :哈希表
* @since 2021/8/26 11:36
*/
public class HashTable {
public static void main(String[] args) {
HashTables hashTables = new HashTables(5);
Emp emp1 = new Emp(1,"jack");
Emp emp2 = new Emp(144,"zhangsan");
Emp emp3 = new Emp(132,"zhangsan");
Emp emp4 = new Emp(55,"zhangsan");
Emp emp5 = new Emp(22,"zhangsan");
Emp emp6 = new Emp(33,"zhangsan");
Emp emp7 = new Emp(11,"zhangsan");
Emp emp8 = new Emp(44,"zhangsan");
Emp emp9 = new Emp(666,"zhangsan");
Emp emp10 = new Emp(77,"zhangsan");
hashTables.addEmp(emp1);
hashTables.addEmp(emp2);
hashTables.addEmp(emp3);
hashTables.addEmp(emp4);
hashTables.addEmp(emp5);
hashTables.addEmp(emp6);
hashTables.addEmp(emp7);
hashTables.addEmp(emp8);
hashTables.addEmp(emp9);
hashTables.addEmp(emp10);
hashTables.show();
hashTables.find(666);
hashTables.delete(22);
hashTables.delete(44);
hashTables.delete(33);
hashTables.delete(22);
hashTables.delete(11);
hashTables.show();
}
}
class Emp{
public int id;
public String name;
public Emp next;
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
}
class EmpLink{
private Emp head;
public void addEmp(Emp emp){
if (head == null){
head = emp;
return;
}
Emp tem = head;
while (true){
if (tem.next == null ){
break;
}
tem = tem.next;
}
tem.next = emp;
}
public void show(int i){
if (head == null){
System.out.println("第"+(i+1)+"条链表为空");
return;
}
Emp temp = head;
System.out.print("第"+(i+1)+"条链表数据:");
while (true){
System.out.print(temp.toString()+" -> ");
if (temp.next == null){
System.out.println();
return;
}
temp = temp.next;
}
}
public void find(int id){
if (head==null){
System.out.println("当前链表数据为空哦,您的想要查找的信息不存在");
return;
}
Emp temp = head;
while (true){
if (id == temp.id){
System.out.println("该数据信息:" + temp.toString());
return;
}
if (temp.next == null){
System.out.println("当前链表没有您所需数据");
return;
}
temp = temp.next;
}
}
public void delete(int id){
if (head == null){
System.out.println("当前链表为空,无法删除");
return;
}
if (head.id==id){
System.out.println("删除:" + head.toString());
head = head.next;
return;
}
Emp temp = head;
while (true){
if (temp.next == null){
System.out.println("当前链表没有您要删除的数据");
return;
}
if (id == temp.next.id){
System.out.println("删除:" + temp.next.toString());
temp.next = temp.next.next;
return;
}
temp = temp.next;
}
}
}
class HashTables{
EmpLink[] empLinks;
int size;
public HashTables(int size) {
this.empLinks = new EmpLink[size];
this.size = size;
for (int i = 0; i < size; i++) {
empLinks[i] = new EmpLink();
}
}
public void addEmp(Emp emp){
empLinks[emp.id % size].addEmp(emp);
}
public void show(){
for (int i = 0; i < size; i++) {
empLinks[i].show(i);
}
}
public void find(int id){
System.out.print("您要查找的数据位于第"+((id % size)+1)+"条链表,ID 为"+id+":");
empLinks[id % size].find(id);
}
public void delete(int id){
System.out.print("您要删除的数据位于第"+((id % size)+1)+"条链表,ID 为"+id+":");
empLinks[id % size].delete(id);
}
}
运行结果:
2021年8月27日08:38:50
十一、树
1、二叉树
二叉树,每个节点最多能有两个子节点,满二叉树:所有子节点都在最后一层,节点数=2ⁿ -1,n为层数
创建二叉树:
- 定义结点类:属性:编号,姓名,左节点,右节点,构造器,get,set,toSting
前序遍历分析:根、左、右
- 先输出当前结点
- 如果左节点不为空,则递归继续前序遍历
- 如果右节点不为空,则递归继续前序遍历
- 思路:
- 先输出父节点:this
- 判断左结点是否为空:this.left !=null,递归前序遍历
- 判断右结点是否为空:this.right !=null,递归前序遍历
中序遍历分析:左、根、右
- 如果当前结点的左节点不为空,则递归继续中序遍历
- 输出当前结点
- 如果当前结点的右节点不为空,则递归继续中序遍历
- 思路:
- 判断左结点是否为空:this.left !=null,递归中序遍历
- 先输出父节点:this
- 判断右结点是否为空:this.right !=null,递归中序遍历
后序遍历分析:左、右、根
- 如果当前结点的左节点不为空,则递归继续后序遍历
- 如果当前结点的右节点不为空,则递归继续后序遍历
- 输出当前结点
- 思路
- 判断左结点是否为空:this.left !=null,递归后序遍历
- 判断右结点是否为空:this.right !=null,递归后序遍历
- 先输出父节点:this
- 查找节点:传入ID
- 判断传入ID:当传入ID比当前节点ID小
- 判断左节点是否为空,为空返回
- 到这里即不等于,递归往下查找:当前节点.left.findNode(传入ID)
- 当传入ID大于当前节点ID
- 判断右节点是否为空,为空返回
- 到这里即不等于,递归往下查找:当前节点.right.findNode(传入ID)
- 传入ID等于当前节点ID,已找到输出并返回
- 删除节点:传入ID
- 判断头节点是不是待删除id,是则删除
- 否则判断左右节点是否为空 || 节点大于小于,得出结果遍历继续
- 定义二叉树类:属性:结点类根节点,set
- 前序遍历
- 判断根节点是否为空,为空返回,不为空则调用结点前序遍历方法
- 中序遍历
- 判断根节点是否为空,为空返回,不为空则调用结点中序遍历方法
- 后序遍历
- 判断根节点是否为空,为空返回,不为空则调用结点后序遍历方法
代码实例
package SparseArrayAndQueues.tree;
/**
* @author create by lbin
* @description:二叉树
* @since 2021/8/27 9:57
*/
public class BinaryTree {
public static void main(String[] args) {
NodeTree nodeTree = new NodeTree();
Node head = new Node(11,"zs");
Node node2 = new Node(3,"ls");
Node node3 = new Node(5,"ww");
Node node4 = new Node(7,"lbin");
Node node5 = new Node(6,"lf");
Node node6 = new Node(1,"aa");
Node node7 = new Node(88,"ff");
Node node8 = new Node(55,"gg");
Node node9 = new Node(99,"oo");
Node node10 = new Node(66,"ee");
// head.setLeft(node2);
// head.setRight(node3);
// node3.setRight(node4);
head.addNode(node2);
head.addNode(node3);
head.addNode(node4);
head.addNode(node5);
head.addNode(node6);
head.addNode(node7);
head.addNode(node8);
head.addNode(node9);
head.addNode(node10);
nodeTree.setHead(head);
//nodeTree.deleNode(5);
System.out.println("前序遍历:");
nodeTree.prePrint();
// System.out.println("中序遍历:");
// nodeTree.cenPrint();
// System.out.println("后序遍历:");
// nodeTree.suffPrint();
nodeTree.deleNode(7);
System.out.println("前序遍历:");
nodeTree.prePrint();
nodeTree.findNode(55);
}
}
class Node{
private int id;
private String name;
private Node left;
private Node right;
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;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
//前序遍历:根、左、右
public void prePrint(){
System.out.println(this);
if (this.left != null){
this.left.prePrint();
}
if (this.right != null){
this.right.prePrint();
}
}
//中遍历:左、根、右
public void cenPrint(){
if (this.left != null){
this.left.cenPrint();
}
System.out.println(this);
if (this.right != null){
this.right.cenPrint();
}
}
//后遍历:左、右、根
public void suffPrint(){
if (this.left != null){
this.left.suffPrint();
}
if (this.right != null){
this.right.suffPrint();
}
System.out.println(this);
}
public void addNode(Node node){
if (this.id > node.id){
if (this.left == null){
this.left = node;
return;
}
this.left.addNode(node);
}else if (this.id < node.id){
if (this.right == null){
this.right = node;
return;
}
this.right.addNode(node);
}else if (this.id == node.id){
System.out.println("该节点已经存在,不可重复添加");
}
}
public void findNode(int node){
if (this.id == node){
System.out.println("您要查找的数据为:"+this.toString());
} else if (this.id > node){
if (this.left == null){
System.out.println("您要查找的信息不存在");
return;
}
// if (this.left.id == node){
// System.out.println("您要查找的数据为:"+this.left.toString());
// return;
// }
this.left.findNode(node);
}else if (this.id < node){
if (this.right == null){
System.out.println("您要查找的信息不存在");
return;
}
// if (this.right.id == node){
// System.out.println("您要查找的数据为:"+this.right.toString());
// return;
// }
this.right.findNode(node);
}
}
public void delNode(int node){
if (this.left != null){
if (this.left.id == node){
System.out.println("删除节点:" + this.left.toString());
this.left = null;
return;
}
this.left.delNode(node);
}
if (this.right != null) {
if (this.right.id == node){
System.out.println("删除节点:" + this.right.toString());
this.right = null;
return;
}
this.right.delNode(node);
}
}
}
class NodeTree{
private Node head;
public void setHead(Node head) {
this.head = head;
}
public void prePrint(){
if (head != null){
head.prePrint();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void cenPrint(){
if (head != null){
head.cenPrint();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void suffPrint(){
if (head != null){
head.suffPrint();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void findNode(int node){
head.findNode(node);
}
public void deleNode(int node){
if (head.getId()==node){
System.out.println("删除节点:" + head.toString());
head = null;
return;
}
head.delNode(node);
}
}
2、顺序二叉树
数组存储方式与树的存储方式可以相互转换,这种树成为顺序二叉树(通常只考虑完全 ),遍历数组时,也可前序,中序,后续完成节点遍历
完全二叉树:第n个元素的左子节点:2*n+1、第n个元素的右子节点:2*n+2、第n个元素的父节点:(n-1)/2、这里都为下标
前序遍历
- 定义顺序二叉树类:属性数组,构造方法
- 传入数组头下标n
- 判断数组是否为空 || 数组长度是否为0:是则返回
- 输出头的数据
- 向左递归:判断(2*n+1)< 数组。length
- 遍历方法:2*n+1
- 向右递归:判断(2*n+2)<数组。length
- 遍历方法:2*n+2
代码实例:
package SparseArrayAndQueues.tree;
/**
* @author create by lbin
* @description:顺序二叉树
* @since 2021/9/2 10:00
*/
public class OrderTree {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9,10};
Order orderaa = new Order(arr);
orderaa.prePrintln(0);
}
}
class Order{
int[] array;
public Order(int[] array) {
this.array = array;
}
/**
* @description:前序遍历
* @param n
* @return
* @throws
* @author create by lbin
* @date 2021/9/2 10:27
*/
public void prePrintln(int n){
if (this.array == null || this.array.length == 0){
System.out.println("数组为空");
return;
}
System.out.println(array[n]);
//向左递归
if ((2*n+1) < array.length){
prePrintln(2*n+1);
}
//向右递归
if ((2*n+2) < array.length){
prePrintln(2*n+2);
}
}
}
3、线索化二叉树
充分利用各个节点的左右指针,让各个节点可以指向自己的前后节点。n个结点的二叉链表中含有n+1个空指针域【2n-(n-1)=n+1】。利用空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为“线索”)。
这种加上了索引的二叉链表称为线索链表,相应的二叉树称为线索二叉树,
- 复制前面的结点类:添加额外属性:int leftType;int rightType,left=0则指向左子树,=1则指向前驱结点;right=0则指向右子树,=1则指向后继结点 ,添加setget
- 复制二叉树类,实现线索化,添加结点属性pre,指向前驱结点,递归进行线索化时,pre总是保留前一个节点
- 线索化方法:传入结点
- 判断传入结点node是否为空,为空返回
- 线索化左子树:线索化方法(node。getleft)
- 线索化本节点:
- 处理前驱结点:判断当前节点的左子树是否为空,为空则左指针指向前驱结点,并修改前驱结点类型==1
- 处理后继结点:判断pre是否为空&&pre的右子树是否为空,非空、空则:前驱结点右指针指向当前节点,并修改后继结点类型==1
- pre = 当前节点node:每处理一个结点后,让当前节点为下个节点的前驱结点
- 线索化右子树:线索化方法(node。getright)
- 遍历:
- 定义结点变量=head,
- while循环,node不为空则继续遍历
- while循环node找到leftType=1的节点:node = node.getleft
- 打印找到的节点
- while循环遍历node。getrightTYpe为1的节点:node=node。getRight,sout
- 替换节点遍历node= node。getright
代码实例:
package SparseArrayAndQueues.tree;
/**
* @author create by lbin
* @description:线索化二叉树,
* @since 2021/9/1 15:08
*/
public class ClueTree {
public static void main(String[] args) {
ClueNodeTree clueNodeTree = new ClueNodeTree();
ClueNode head = new ClueNode(8,"zs");
ClueNode node2 = new ClueNode(3,"zs");
ClueNode node3 = new ClueNode(6,"zs");
ClueNode node4 = new ClueNode(1,"zs");
ClueNode node5 = new ClueNode(10,"zs");
ClueNode node6 = new ClueNode(14,"zs");
ClueNode node7 = new ClueNode(9,"zs");
head.addNode(node2);
head.addNode(node3);
head.addNode(node4);
head.addNode(node5);
head.addNode(node6);
head.addNode(node7);
// head.setLeft(node2);
// head.setRight(node2);
// node2.setLeft(node4);
// node2.setRight(node5);
// node3.setLeft(node6);
clueNodeTree.setHead(head);
clueNodeTree.clueTree(head);
clueNodeTree.showClue();
}
}
class ClueNode {
private int leftType;
private int rightType;
private int id;
private String name;
private ClueNode left;
private ClueNode right;
public ClueNode(int id, String name) {
this.id = id;
this.name = name;
}
public ClueNode getLeft() {
return left;
}
public void setLeft(ClueNode left) {
this.left = left;
}
public ClueNode getRight() {
return right;
}
public void setRight(ClueNode right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
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;
}
public void addNode(ClueNode node){
if (this.id > node.id){
if (this.left == null){
this.left = node;
return;
}
this.left.addNode(node);
}else if (this.id < node.id){
if (this.right == null){
this.right = node;
return;
}
this.right.addNode(node);
}else if (this.id == node.id){
System.out.println("该节点已经存在,不可重复添加");
}
}
}
class ClueNodeTree {
private ClueNode pre = null;
private ClueNode head;
public void setHead(ClueNode head) {
this.head = head;
}
/**
* @description:线索化方法
* @param node
* @return
* @throws
* @author create by lbin
* @date 2021/9/1 15:27
*/
public void clueTree(ClueNode node){
if (node == null){
return;
}
//线索化左子节点
clueTree(node.getLeft());
//线索化当前节点
if (node.getLeft() == null){
node.setLeft(pre);
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
//线索化右子节点
clueTree(node.getRight());
}
/**
* @description:线索化中序遍历
* @param
* @return
* @throws
* @author create by lbin
* @date 2021/9/2 9:28
*/
public void showClue(){
ClueNode node = head;
while (node != null) {
while (node.getLeftType() == 0){
node = node.getLeft();
}
System.out.println("线索化:"+node.toString());
while (node.getRightType() == 1){
node = node.getRight();
System.out.println("线索化:"+node.toString());
}
node = node.getRight();
}
}
}
2021年9月2日14:54:27
4、堆排序算法
堆排序需要利用二叉树这种数据结构,k = 2i + 1(k是i的左子节点)堆是具有一下性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆,每个节点的值都小于或等于其左右孩子节点的值:小顶堆
升序采用大顶堆,降序采用小顶堆。大小顶堆用顺序排列(参考上面的顺序二叉树数组)可得到:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2];arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
分析:将待排序序列构造成一个大顶堆,此时最大值为根节点,与其末尾元素交换,末位则最大,然后将剩余n-1个元素构成一个堆,反复进行
思路
- 堆排序方法:传入数组
- for循环构建成一个堆:i = arr.length / 2 - 1,i > 0; i--:(i为非叶子节点的数量+1,0也算,数组首位,从底至上,从左至右),先排序一遍大顶堆
- 转大顶堆:arr、i、arr.length
- int temp = 0;定义中间值方便交换
- for循环将堆顶元素与末位元素交换,并重新调整结构:j = arr.length-1;j>0;j++
- temp = arr[j]
- arr[j] = arr[0]
- arr[0] = temp
- 转换大顶堆:arr,0,j:交换前已经是大顶堆了,交换后只是头变了,故此只用非叶子节点0,最后一位排序已经固定,如此传入的长度为j
- 数组转换成大顶堆方法:传入数组,length,非叶子节点在数组中的索引i
- temp = arr[i]:临时变量
- for循环从非叶子节点:k = 2*i+1;k<length;k=k*2+1
- 判断左右子节点大小,右打则k++移动到右子节点。须保证k+1<length
- 判断k索引位置的元素是否大于父节点temp,大于则把较大的值赋给当前节点i,并且让i指向k:i=k,小于则退出循环
- arr[i]=temp,将最大值移动到最前方
代码实例:
package SparseArrayAndQueues.sort;
import java.util.Arrays;
/**
* @author create by lbin
* @description:堆排序,需要利用顺序二叉树,升序降序
* @since 2021/9/2 16:20
*/
public class HeapSort {
static int count = 0;
public static void main(String[] args) {
int[] array = {0,5,2,1,8,6,13,99,25,66};
//大顶堆升序
System.out.println("大顶堆升序");
heapSort(array);
//小顶堆降序
System.out.println("小顶堆降序");
smallSort(array);
}
/**
* @description:升序
* @param arr
* @return
* @throws
* @author create by lbin
* @date 2021/9/2 16:29
*/
public static void heapSort(int[] arr){
int temp = 0;
for (int i = arr.length / 2 - 1; i >= 0 ; i--) {
toHeap(arr,i,arr.length);
}
for (int i = arr.length-1; i > 0; i--) {
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
System.out.println("第"+(++count)+"次交换:"+ Arrays.toString(arr));
toHeap(arr,0,i);
}
}
/**
* @description:转成大顶堆
* @param arr
* @param i
* @param length
* @return
* @throws
* @author create by lbin
* @date 2021/9/2 16:29
*/
public static void toHeap(int[] arr,int i,int length){
int temp = arr[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
if (k + 1 < length && arr[k] < arr[k+1]){
k++;
}
if (arr[k] > temp){
arr[i] = arr[k];
i = k;
}else {
break;
}
}
arr[i] = temp;
}
/**
* @description:降序
* @param arr
* @return
* @throws
* @author create by lbin
* @date 2021/9/2 17:04
*/
public static void smallSort(int[] arr){
int temp = 0;
for (int i = arr.length / 2 - 1; i >= 0 ; i--) {
toSmall(arr,i,arr.length);
}
for (int i = arr.length-1; i > 0; i--) {
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
System.out.println("第"+(++count)+"次交换:"+ Arrays.toString(arr));
toSmall(arr,0,i);
}
}
/**
* @description:转小顶堆
* @param arr
* @param i
* @param length
* @return
* @throws
* @author create by lbin
* @date 2021/9/2 17:03
*/
public static void toSmall(int[] arr,int i,int length){
int temp = arr[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
if (k + 1 < length && arr[k] > arr[k+1]){
k++;
}
if (arr[k] < temp){
arr[i] = arr[k];
i = k;
}else {
break;
}
}
arr[i] = temp;
}
}