ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

尚硅谷 java数据结构与算法 学习笔记(一)

2020-12-09 17:00:39  阅读:196  来源: 互联网

标签:arr java temp int System println 硅谷 数据结构 out


这里写目录标题

线性结构和 非线性结构

在这里插入图片描述

稀疏数组SparseArray

需求

记录棋盘的位置 可用用二位数组将它保存 ,但是会发现记录很多没有意义的数据很多空间浪费 可以用稀疏数组进行优化
在这里插入图片描述

介绍

稀疏数组就是压缩缩多余的冗余数据
在这里插入图片描述
第一行的记录原表的行列数和 非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): 查看对列头的数据

问题

现在队列经历了加满 也全部取出 当前列表理论上为空
但是任然无法加入数据 这个数组无法复用 只能使用一次 没有到达复用的效果
需要将这个数组由算法改造成一个环形数组(取模)
在这里插入图片描述

数组模拟环形队列

注意现在调整了front 和 rear 队列的
在这里插入图片描述

环形队列代码实现

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

链表

在这里插入图片描述

小结上图:

  1. 链表是以节点的方式来存储, 是链式存储
  2. 每个节点包含 data 域, next 域:指向下一个节点.
  3. 如图:发现 链表的各个节点不一定是连续存储.
  4. 链表分 带头节点的链表和 没有头节点的链表,根据实际的需求来确定

单链表

单链表看似连续 但是并不是 这只是逻辑结构
在这里插入图片描述

不考虑排名

在这里插入图片描述

考虑排名

在这里插入图片描述

修改

思路(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个节点

	// 求单链表的倒数第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<String> s2 代替
		// Stack<String> s2 = new Stack<String>();
		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();
	}
}

排序算法

在这里插入图片描述

时间复杂度

在这里插入图片描述

时间频度

在这里插入图片描述
在这里插入图片描述

结论:

  1. 2n+20 和 2n 随着 n 变大,执行曲线无限接近, 20 可以忽略
  2. 3n+10 和 3n 随着 n 变大,执行曲线无限接近, 10 可以忽略

在这里插入图片描述

结论:

  1. 2n^2+3n+10 和 2n^2 随着 n 变大, 执行曲线无限接近, 可以忽略 3n+10
  2. n^2+5n+20 和 n^2 随着 n 变大,执行曲线无限接近, 可以忽略 5n+20

忽略系数(3次方及以上不能忽略)
在这里插入图片描述

时间复杂度

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

空间复杂度

在这里插入图片描述

冒泡排序

冒泡排序(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));

时间复杂度 O(n^2)

冒泡排序的优化

		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]
		 */

韩老师的电脑大概20秒处理8万个随机数

选择排序算法

在这里插入图片描述
在这里插入图片描述

代码实现

在这里插入图片描述

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));
	}

}

时间复杂度O(n^2)

韩老师的电脑处理8万个随机数大概3秒

插入排序法

在这里插入图片描述

在这里插入图片描述

代码实现

有一群小牛, 考试成绩分别是 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));
	}
}

时间复杂度O(n^2)

韩老师的电脑 8万个随机数排序 大致5秒

哈希排序算法图解

简单插入排序的问题

在这里插入图片描述

希尔排序是插入排序的一种高效版本

在这里插入图片描述

在这里插入图片描述

代码实现(交换法)效率并不是很高 韩老师的电脑对8万个随机数进行排序 花了17秒

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));
	}


}

代码实现(移位法) 效率较高 韩老师的电脑对8万个随机数进行排序 花了1秒

	// 对交换式的希尔排序法进行优化 -> 移位法
	public static void hellSortBetter(int[] arr){
		int temp = 0;
		int group = arr.length/2;
		while (group != 0) {
			// 从group个元素, 逐个对其所在的组进行直接插入排序
			for (int i = group; i < arr.length; i++) {
				int insertIndex = i; //要插入的位置
				temp = arr[insertIndex];
				if(arr[insertIndex] < arr[insertIndex - group]){
					while (insertIndex - group >= 0 && temp < arr[insertIndex - group]){
						// 移动
						arr[insertIndex] = arr[insertIndex - group];
						insertIndex -= group;
					}
					// 退出while循环后 找到了插入的位置
					arr[insertIndex] = temp;
				}
			}
			group /= 2;
		}
		System.out.println("希尔排序结果");
		System.out.println(Arrays.toString(arr));
	}

时间复杂度O(n^(1.3—2))

快速排序算法

在这里插入图片描述
在这里插入图片描述

代码实现

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);
		}
	}
}

韩老师的电脑对8万个随机数进行排序 花了不到一秒 80w数据也不到1秒 800w 2-3秒

快排 时间复杂度O (nlogn) 但是空间复杂度较高

归并排序

在这里插入图片描述
在这里插入图片描述

代码实现

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++];
		}
	}

}

时间复杂度O(nlogn) 韩老师的电脑对8万个随机数进行排序 花了不到0-1秒 80w 一秒 800w3-4秒

基数排序 (桶排序)

在这里插入图片描述
稳定排序 : 如果要排序的数组有两个相同的数字 如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));
	}


}

时间复杂度O(nlogn) 韩老师的电脑对8万个随机数进行排序 花了不到0-1秒 80w 1秒不到 800w1秒 8000w 80000000 *11 *4 /1024/1024/1024 = 3.3G的内存

在这里插入图片描述

如果有负数就不使用基数排序

八大排序算法的对比

在这里插入图片描述

堆排序要学了二叉树才能理解

查找算法

在这里插入图片描述

二分查找算法

在这里插入图片描述

在这里插入图片描述

d

标签:arr,java,temp,int,System,println,硅谷,数据结构,out
来源: https://blog.csdn.net/weixin_43691773/article/details/110420321

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有