2.2队列
2.2.1 使用场景
银行排队案例:
2.2.2 队列基本介绍
1)队列是一个有序列表,可以用数组或是链表来实现。
2)遵循先入先出原则。
2.2.3 数组模拟队列的思路
- 队列本身是有序列表,若使用数组结构来存储队列的数据,则队列数组的声明如下图,,其中maxSize是该队列的最大容量值。
- 因为队列的输出、输入分别从前后端来处理的,因此需要两个变量front和rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear会随着数据的输入而改变。如图:
2.2.4 代码实现
创建一个ArrayQueueDemo类:
package com.atguigu.sparse;
import java.util.Scanner;
import javax.management.RuntimeErrorException;
public class ArrayQueueDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个数组
ArrayQueue queue = new ArrayQueue(3);
char key = ' ';//定义一个key用于接收用户输入
boolean loop = true;
Scanner scanner = new Scanner(System.in);//定义文本扫描器
while(loop) {
System.out.println("欢迎来到队列系统,请输入:");
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':
queue.showQueue();
break;
case 'a':
System.out.println("请输入一个数");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 'g':
try {
int res = queue.getQueue();;
System.out.printf("取出的数据为:%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
// TODO: handle exception
}
break;
case 'h':
try {
int reb = queue.headQueue();
System.out.printf("取出的头数据为:%d\n",reb);
} catch (Exception e) {
System.out.println(e.getMessage());
// TODO: handle exception
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
创建一个ArrayQueue类
package com.atguigu.sparse;
//使用数组模拟队列,编写一个ArrayQueue类
public class ArrayQueue {
private int maxsize;//定义数组最大值
private int front;//定义队列头
private int rear;//定义队列尾
private int [] arr;//用于存放数组
//创建队列构造器
public ArrayQueue(int maxsize) {
super();
this.maxsize = maxsize;
this.front = -1;
this.rear = -1;
this.arr = new int[maxsize];
}
//判断队列是否满
public boolean isFull(){
return rear==maxsize-1;
}
//判断队列是否空
public boolean isEmpty() {
return rear==front;
}
//为队列添加数据
public void addQueue(int n){
//首先判断队列是否满
if(isFull()) {
System.out.println("队列满,无法添加数据");
return;
}else {
rear++;
arr[rear] = n;
System.out.println("添加成功");
}
}
//从队列取出数据
public int getQueue() {
if(isEmpty()) {
//抛出异常
throw new RuntimeException("队列为空,无法获取数据");
}else {
front++;
return arr[front];
}
}
//显示队列数据
public void showQueue() {
//判断队列是否为空
if(isEmpty()) {
System.out.println("队列为空~~~");
return ;
}
for(int i = 0;i<arr.length;i++) {
System.out.printf("arr[%d] = %d\n",i,arr[i]);
}
}
public int headQueue() {
// TODO Auto-generated method stub
if(isEmpty()) {
//抛出异常
throw new RuntimeException("队列为空,无法获取头数据");
}else {
return arr[0];
}
}
}
缺点:无法实现环形队列,数据一旦被取出就不能再添加。
2.2.5优化代码
思路:
1)front变量的含义做一个调整,front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素,front初始值为0。
2)rear变量的含义做一个调整,rear指向当前元素的后一个位置,空出一个空间来做约定,rear的初始值为0.
3)当队列满时,条件为(rear+1)%maxSize=front。
4)当队列空时,条件为rear = front。
5)队列中有效个数的值为(rear+maxSize-front)%maxSize。
代码实现:
在原有代码基础上做修改,只修改ArrayQueue类,ArrayQueueDemo类可以适当做调整,比如增加队列容量。
package com.atguigu.sparse;
//使用数组模拟队列,编写一个ArrayQueue类
public class ArrayQueue {
private int maxsize;//定义数组最大值
private int front;//定义队列头
private int rear;//定义队列尾
private int [] arr;//用于存放数组
//创建队列构造器
public ArrayQueue(int maxsize) {
super();
this.maxsize = maxsize;
this.front = 0;
this.rear = 0;
this.arr = new int[maxsize];
}
//判断队列是否满
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;
}else {
//添加数据到队列
arr[rear] = n;
//令rear后移,因为是环形队列不能直接rear++,需要取模
rear=(rear+1)%maxsize;
System.out.println("添加成功");
}
}
//从队列取出数据
public int getQueue() {
if(isEmpty()) {
//抛出异常
throw new RuntimeException("队列为空,无法获取数据");
}else {
//定义一个变量来存放front的值,如果直接返回就会直接取到+1个元素,无法返回第0个元素。
int value = arr[front];
front=(front+1)%maxsize;
return value;
}
}
//计算队列有效数据
public int size() {
return (rear+maxsize-front)%maxsize;
}
//显示队列数据
public void showQueue() {
//判断队列是否为空
if(isEmpty()) {
System.out.println("队列为空~~~");
return ;
}
for(int i = front;i<front+size();i++) {
System.out.printf("arr[%d] = %d\n",i%maxsize,arr[i%maxsize]);
}
}
public int headQueue() {
// TODO Auto-generated method stub
if(isEmpty()) {
//抛出异常
throw new RuntimeException("队列为空,无法获取头数据");
}else {
return arr[front];
}
}
}
**第三章 链表**
3.1链表(Linked List)介绍
链表是有序的链表,但它在内存中的存储如下:
小结:
1)链表是以节点方式来存储的,是链式存储。
2)每个节点包含data域,next域,指向下一个节点。
3)如图,发现链表的每一个节点不一定是连续存储。
4)链表分带头节点的链表和没有节点的链表,根据实际需求来确定。
3.2单链表应用实例
使用带head头的单向链表实现–水浒英雄管理,完成对英雄人物的增删改操作。
思路:
1)添加英雄人物(不考虑编号顺序):
- 首先定义辅助节点temp来辅助遍历,因为head头指针不能移动。
- 找到链表尾部,让尾部节点next指向新节点。
- 记住temp=tempnext;语句,让指针一直向后遍历。
2)按编号顺序添加插入英雄人物
- 依然定义temp辅助遍历,并且定义boolean变量flag来判断是否找到要插入节点。
- 令temp找到要插入节点的前一个节点,跳出循环的判断条件为要插入节点的后一个节点编号大于插入节点编号:temp.next.no > heroNode.no。
- 跳出循环后将新节点heroNode.next的值赋给temp.next。再将temp.next值赋给heroNode。即可完成添加。不要忘记emp=tempnext;语句。
3)修改人物信息
- 定义temp辅助遍历,并且定义boolean变量flag来判断是否找到要修改节点。
- 如果temp.no==newheroNode.no,令flag为真,跳出循环,将修改后英雄的名字、昵称赋给temp。
4)删除英雄人物信息
- 定义temp辅助遍历,令temp=head,让temp找到要删除节点的前一个节点,并且定义boolean变量flag来判断是否找到要删除节点。
- 找到删除节点的前一个节点,令temp.next=temp.next.next;
- 没有指向的节点会被视为垃圾进入垃圾回收装置。
5)查找英雄人物信息
- 定义temp辅助遍历,并且定义boolean变量flag来判断是否找到要查找节点。
- 根据英雄id来查找,如果temp.no==id;找到查找信息,跳出循环,将temp打印出来。否则编号信息不存在。
3.3代码演示
定义SingleLinkedList类编写方法
package com.atguigu.linklist;
public class SingleLinkedList {
//定义头结点head
HeroNode head = new HeroNode(0," "," ");
//定义添加节点方法
//思路:不考虑编号顺序
//1.找到next等于null的节点
//2.让其next指向新节点
public void add(HeroNode heroNode) {
//因为head节点不能动,需要定义辅助节点来遍历
HeroNode temp = head;
while(true) {
if(temp.next==null) {
break;
}
temp=temp.next;
}
temp.next = heroNode;
}
//修改节点
public void Update(HeroNode newheroNode) {
//定义辅助遍历变量
HeroNode temp = head.next;
boolean flag = false;//判断是否找到需要修改的节点
while(true) {
//判断是否为空
if(head.next==null) {
System.out.println("链表为空");
break;
}
if(temp.no == newheroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
temp.name = newheroNode.name;
temp.nickname = newheroNode.nickname;
}else {
System.out.printf("需要修改的节点:%d未找到\n",newheroNode.no);
}
}
//删除节点
public void del(int no) {
//定义辅助遍历变量temp
HeroNode temp = head;
boolean flag = false;
while(true) {
if(temp.next == null) {
//找到最后也没有找到要删除节点
break;
}
if(temp.next.no == no) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
temp.next = temp.next.next;
}
else {
System.out.printf("要删除的节点%d不存在",no);
}
}
//查找节点
public void select(int id) {
//定义辅助遍历变量
HeroNode temp = head.next;
boolean flag = false;
while(true) {
if(temp.next==null) {
break;
}
if(temp.no==id) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
System.out.println("您要查找的英雄信息:\n"+temp);
}
else {
System.out.printf("您要查找的编号%d信息不存在\n",id);
}
}
//按编号顺序插入节点
public void addOderby(HeroNode heroNode) {
//定义辅助变量temp
//令temp找到要插入节点的前一个节点
HeroNode temp = head;
//定义一个标识
boolean flag = false;
while(true) {
if(temp.next==null) {//遍历到最后一个节点
break;
}
if(temp.next.no > heroNode.no) {//要插入节点的后一个节点编号大于插入节点编号
break;
}else if(temp.next.no==heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
System.out.printf("需要插入的节点编号:%d与已有编号冲突\n",heroNode.no);
}else {
//加入节点
heroNode.next = temp.next ;
temp.next = heroNode;
}
}
public void showlist() {
if(head.next==null) {
System.out.println("链表为空!!");
return;
}
HeroNode temp = head.next;
while(true) {
if(temp==null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
定义HeroNode类,每一个HeroNode就是一个节点。
package com.atguigu.linklist;
//创建HeroNode类,每一个HeroNode就是一个节点
public class HeroNode {
public int no;//英雄编号
public String name;//英雄姓名
public String nickname;//英雄呢称
public HeroNode next;//指向下一节点
//创建构造器
public HeroNode(int no, String name, String nickname) {
super();
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
//为了显示需要,重写此方法
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
}
定义SingleLinkedListDemo类,创建节点和链表,并调用方法。
package com.atguigu.linklist;
public class SingleLinkedListDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
//先创建节点
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
//创建链表
SingleLinkedList singlelist = new SingleLinkedList();
//添加
singlelist.addOderby(hero1);
singlelist.addOderby(hero3);
singlelist.addOderby(hero2);
singlelist.addOderby(hero4);
singlelist.addOderby(hero4);
System.out.println("修改前:\n");
singlelist.showlist();
//删除
singlelist.del(1);
System.out.println("删除后:\n");
singlelist.showlist();
//修改
HeroNode newheroNode = new HeroNode(2, "小卢", "玉~~");
singlelist.Update(newheroNode);
System.out.println("修改后:\n");
singlelist.showlist();
//查找
singlelist.select(2);
}
}