队列是一个有序列表,可以使用数组或链表实现
一次性队列,不能复用:
思路:队列(数组)最大长度:maxSize 队列首元素的前一个:front 队列最后元素(尾部):rear
1、数据入队(添加元素):
2、数据出队(取出元素):
public class ArrayQueueDemo {
public static void main(String[] args) {
//创建一个队列
ArrayQueue arrayQueue=new ArrayQueue(5);
Scanner sc=new Scanner(System.in);
while (true){
System.out.println("1:显示队列");
System.out.println("2:添加数据到队列");
System.out.println("3:从队列取出数据");
System.out.println("4:显示队列第一个元素");
System.out.println("5:退出");
int i = sc.nextInt();
switch (i){
case 1:arrayQueue.showQueue();break;
case 2:
System.out.println("请输入一个数:");
int value = sc.nextInt();
arrayQueue.addQueue(value);
break;
case 3:try {
arrayQueue.getQueue();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 4:try{
arrayQueue.headQueue();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 5:System.exit(0);
}
}
}
}
/*
使用数组模拟队列:先进先出 一次性队列
思路:队列(数组)最大长度:maxSize
队列前一个元素:front
队列最后元素(尾部):rear
数据入队:1、判断队列是否满-条件:rear==maxSize-1
2、若:rear < maxSize-1,则添加数据
数据出队:
1、判断队列是否为空-条件:front==rear
*/
class ArrayQueue{
private int maxSize;//数组的长度
private int front;//队列头(第一个数据的前一个)
private int rear;//队列尾部(最后一个数据)
private int[] arr;//存放数据,模拟队列
//初始化队列
public ArrayQueue(int arrMaxSize){
maxSize=arrMaxSize;
arr=new int[maxSize];
front=-1;
rear=-1;
}
//判断队列是否满
public boolean isFull(){
return rear==maxSize-1;
}
//判断队列是否空
public boolean isEmpty(){
return front==rear;
}
//添加数据到队列
public void addQueue(int n){
//判断队列是否满了
if(isFull()){
System.out.println("队列满了呀,不能添加数据哦!");
return;
}
//未满,则添加
rear++;//rear指针往后移
arr[rear]=n;
}
//出队列
public int getQueue(){
//判断是否为空
if (isEmpty()){
//为空,则抛出异常
throw new RuntimeException("队列是空的呀,不能取出数据!");
}
//若队列不为空
front++;//front指针后移
return arr[front];
}
//显示队列全部数据
public void showQueue(){
if (isEmpty()){
System.out.println("队列是空的呀,没有数据!");
}
//若队列不为空,则遍历队列(数组)
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
//显示队列头数据
public int headQueue(){
if (isEmpty()){
throw new RuntimeException("队列是空的呀,没有数据!");
}
return arr[front+1];
}
}
front:第一个元素,初始值=0
rear:尾指针的下一个元素,也就是预留出来的数组空间 初始值=0
预留一个数组空间出来,比如队列的长度maxSize4,因为要预留一个空间,预留的空间为空,其实有效数据个数其实是3(对应数组下标为:0,1,2),当添加了三个数据时,rear3(其实就是预留空间的数组下标),由此可以判断数组是否满:(rear(3)+1)% maxSize(4) == front(0),如果队列已经满了,就不能添加数据到队列,此时rear=3;若此时取出一个数据,front0 变为 front 1,若再添加一个数据,此时条(rear(3)+1)% maxSize 0 不等于 front 1,判断满的条件不成立,可以添加,之前的rear3,此时rear0,添加之后已经三个元素了,满了。若再添加,根据条件:(rear(0)+1)% maxSize == 1front1,满的条件成立。数组的下标是动态的变化的
1、数据入队(添加元素)
2、数据出队(取出元素):
队列有效数据个数: (rear+maxSize-front)%maxSize
import java.util.Scanner;
public class CircleQueueDemo {
public static void main(String[] args) {
//创建一个队列
CircleQueue queue=new CircleQueue(4);
Scanner sc=new Scanner(System.in);
while (true){
System.out.println("1:显示队列");
System.out.println("2:添加数据到队列");
System.out.println("3:从队列取出数据");
System.out.println("4:显示队列第一个元素");
System.out.println("5:退出");
int i = sc.nextInt();
switch (i){
case 1:queue.showQueue();break;
case 2:
System.out.println("请输入一个数:");
int value = sc.nextInt();
queue.addQueue(value);
break;
case 3:try {
queue.getQueue();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 4:try{
queue.headQueue();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 5:System.exit(0);
}
}
}
/*
判断是否满:(rear+1)% maxSize == front
判断是否空: rear=front
队列有效数据个数: (rear+maxSize-front)%maxSize
*/
static class CircleQueue{
private int maxSize;//数组的长度
private int front;//队列头(第一个数据)
private int rear;//队列尾部后一个
private int[] arr;//存放数据,模拟队列
//初始化队列
public CircleQueue(int arrMaxSize){
maxSize=arrMaxSize;
arr=new int[maxSize];
front=0;
rear=0;
}
//判断队列是否满
public boolean isFull(){
return (rear+1)%maxSize==front;
}
//判断队列是否空
public boolean isEmpty(){
return front==rear;
}
//添加数据到队列
public void addQueue(int n){
//判断队列是否满了
if(isFull()){
System.out.println("队列满了呀,不能添加数据哦!");
return;
}
//未满,则添加
arr[rear]=n;
//rear指针往后移
rear=(rear+1)%maxSize;
}
//出队列
public int getQueue(){
//判断是否为空
if (isEmpty()){
//为空,则抛出异常
throw new RuntimeException("队列是空的呀,不能取出数据!");
}
//若队列不为空
int value=arr[front];
//front指针后移
front=(front+1)%maxSize;
return value;
}
//显示队列全部数据
public void showQueue(){
if (isEmpty()){
System.out.println("队列是空的呀,没有数据!");
}
//若队列不为空,则遍历队列(数组)
for (int i = front; i < (front+(rear+maxSize-front)%maxSize); i++) {
System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
//显示队列头数据
public int headQueue(){
if (isEmpty()){
throw new RuntimeException("队列是空的呀,没有数据!");
}
return arr[front];
}
}
}
可以是不连续的空间地址
head:头结点(不放数据) 如何判断结束,当节点不指向下一个节点时为结束标志
主要思路:
代码实现:不考虑编号
package LinkList;
public class SingleLinkListDeom {
public static void main(String[] args) {
//创建结点
HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
//创建链表
SingleLinkList singleLinkList = new SingleLinkList();
singleLinkList.add(hero1);
singleLinkList.add(hero2);
singleLinkList.add(hero3);
singleLinkList.add(hero4);
//显示
singleLinkList.list();
}
//使用链表管理节点(英雄)
static class SingleLinkList{
//先初始化头结点,不存放具体的数据
private HeroNode head=new HeroNode(0,"","");
//添加结点到单链表
//当不考虑链表的顺序时,找到当前结点的最后结点,然后将当前结点的next指向新结点
public void add(HeroNode heroNode){
HeroNode temp=head;//因为头结点不能动,所以需要辅助遍历 temp
//遍历链表,
while (true){
//找到最后的结点
if(temp.next==null){
break;
}
//如果没有找到,将temp后移
temp=temp.next;
}
//当退出循环时,temp就指向链表的最后,
temp.next=heroNode;
}
//显示链表 遍历(需要辅助变量)
public void list(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
HeroNode temp=head.next;//因为头结点不能动,所以需要辅助变量
while (true){
//判断链表是否到最后
if (temp==null){
break;
}
//输出结点信息
System.out.println(temp);
//将next后移
temp=temp.next;
}
}
}
//节点对象
static class HeroNode{
public int no;
public String name;
public String niceName;
public HeroNode next;//指向下一个节点
public HeroNode(int no, String name, String niceName) {
this.no = no;
this.name = name;
this.niceName = niceName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", niceName='" + niceName + '\'' +
'}';
}
}
}
考虑编号:
思路:首先定义一个临时的指针—temp,帮助我们找到要插入节点的位置,然后遍历链表(其实就是通过循环,不断地让temp指针往后指,直到找到符合的节点,就break(结束循环)),
那么结束循环的条件是啥呢?如下:
当我们找到了要插入的位置时,只需要让插入节点的next指向temp节点的下一个节点(heroNode.next=temp.next;),然后将temp节点的next指向插入节点(temp.next=heroNode)。
//考虑编号
public void add2(HeroNode heroNode){
//辅助指针 temp ,帮助找到添加的位置
HeroNode temp=head;
boolean flag=false;//添加的编号是否存在
while (true){
if (temp.next==null){//temp在链表的最后
break;
}
if (temp.next.no>heroNode.no){//说明符合编号顺序
break;//退出循环
}
else if (temp.next.no==heroNode.no){
//说明要添加编号存在
flag=true;
break;
}
temp=temp.next;//往后移--相当于遍历当前链表,
}
//判断falg值
if (flag){
System.out.println("要插入的英雄编号已经存在"+heroNode.no);
}else {
//插入到链表
heroNode.next=temp.next;
temp.next=heroNode;
}
}
思路:首先判断链表是否为空,为空还咋找?然后搞一个临时指针指向头节点的下一个节点,找到要修改的节点,那么咋找呢?就是遍历链表(其实就是通过循环,不断地让temp指针往后指,直到找到符合的节点,就break(结束循环)),循环结束条件:
//根据编号修改
public void update(HeroNode newHeroNode){
//判断是否为空
if (head.next==null){
System.out.println("链表为空");
return;
}
//找到需要修改的节点
HeroNode temp=head.next;
boolean flag=false;//表示是否找到该节点
while (true){
if (temp==null){
break;//已经遍历完链表
}
if (temp.no==newHeroNode.no){
flag=true;
break;
}
//指针往后移
temp=temp.next;
}
if (flag){
temp.name=newHeroNode.name;
temp.niceName=newHeroNode.niceName;
}else {
System.out.println("没有找到该节点!"+newHeroNode.no);
}
}
}
思路:
首先先找到要删除节点的上一个节点,我们用临时节点temp表示 条件:temp.next.no==编号;
然后直接让temp节点的下一个节点直接指向删除节点的下一个节点 条件:temp.next=temp.next.next
//根据指定编号删除节 点
public void delete(int no){
if (head.next==null){
System.out.println("链表为空");
return;
}
//指定一个临时指针节点
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.println("没有该节点");
}
}
获取单链表的有效节点个数(不计头结点) head-表示链表的头节点
思路:先判断链表不为空,定义一个变量来记录遍历链表的次数
//head-表示头节点
public static int getLength(HeroNode head){
if (head.next==null){
System.out.println("链表为空");
return 0;
}
int count=0;
HeroNode temp=head.next;
// while (true){
// if (temp==null){
// break;
// }
// temp=temp.next;
// count++;
// }
while (temp!=null){
count++;
temp=temp.next;
}
return count;
}
输出:System.out.println(getLength(singleLinkList.getHead()));
思路:判断链表不为空,根据传过来的链表得到有效节点的个数(length),再判断传的k是否合法,根据length-k找到要查找的节点
//查找单链表的倒数第k个节点
public static HeroNode findLastNode(HeroNode head,int k){
if (head.next==null){
System.out.println("链表为空");
return null;
}
//第一次遍历,得到有效节点个数
int length = getLength(head);
//第二次遍历到length-k位置,就是倒数第k的节点
if (length<=0||k>length){
return null;
}
HeroNode temp=head.next;//temp为第一个有效节点
for (int i = 0; i < length-k; i++) {
temp=temp.next;
}
return temp;
}
输出:System.out.println(findLastNode(singleLinkList.getHead(),2));
思路:定义一个新的链表,头节点为reverseHead,准备一个空的next节点,遍历让其指向下一个节点,保存下来(因为当你取出一个节点时,原来的连接就断了,temp.next为空)。然后遍历链表,每遍历一个节点,就把这个节点放到新链表的最前面,最后遍历结束时,让原来链表头节点的下一个节点指向新链表头节点的下一个节点,也就是让head.next指向原来链表的最后一个节点(因为已经实现了反转)
//链表翻转
public static void reverseLinkList(HeroNode head){
//链表为空 链表只有一个节点
if (head.next==null||head.next.next==null){
return;
}
//定义一个指针,遍历原来的链表
HeroNode temp=head.next;
//指向当前节点(temp)的下一个节点
HeroNode next=null;
HeroNode reverseHead=new HeroNode(0,"","");
//遍历原来的链表,没遍历一个节点,就取出,放在新链表reverseHead的最前端
while (temp!=null){
next=temp.next;//先暂时保存当前节点的下一个节点,因为后面需要使用
temp.next=reverseHead.next;
reverseHead.next=temp;
temp=next;//让temp后移
}
//将原来链表的head.next指向reverseHead.next
head.next=reverseHead.next;
}
递归实现反转链表:
思路:假设链表箭头的方向从左往右,共有四个有效节点 1—>2—>3—>4
先将链表递归到最后一个节点-HeroNode reverseHead = reverseLinkList2(head.next),
此时head.next表示最后一个节点-4,head表示倒数第二个节点-3,reverseHead 表示最后一个节点-4,这时最重要的反转来了:
head.next.next = head 由于递归:head 是变化的
第一次:让节点4的下一个节点指向节点3 == 相当于 4—>3 此时head是3
第二次:让节点3的下一个节点指向节点2 ==相当于4—>3—>2 此时head是2
第三次:让节点2的下一个节点指向节点1 ==相当于4—>3—>2—>1 此时head是1
//链表翻转--法二 递归
public static HeroNode reverseLinkList2(HeroNode head) {
if (head == null || head.next == null) {
return head;
}
// 遍历到链表尾部
HeroNode reverseHead = reverseLinkList2(head.next);
// 反转
head.next.next = head;
head.next = null;
return reverseHead;
}
//从尾到头打印链表,不改变原链表
public static void reversePrint(HeroNode head){
if (head.next==null){
return;//空链表
}
//创建一个栈,将各个节点压入栈
Stack<HeroNode> stack = new Stack<>();
HeroNode temp=head.next;
while (temp!=null){
stack.push(temp);
temp=temp.next;
}
//打印栈里节点
while (stack.size()>0){
System.out.println(stack.pop());
}
}
public static HeroNode twoLinkList(HeroNode heroNode1,HeroNode heroNode2){
if (heroNode1==null) return heroNode2;
if (heroNode2==null) return heroNode1;
HeroNode temp;
if (heroNode1.no<heroNode2.no){
temp=heroNode1;
temp.next=twoLinkList(heroNode1.next,heroNode2);
}else {
temp=heroNode2;
temp.next=twoLinkList(heroNode1,heroNode2.next);
}
return temp;
}
打印:
HeroNode heroNode = toLinkList(singleLinkList.getHead(), singleLinkList2.getHead());
while (heroNode!=null){
System.out.println(heroNode);
heroNode=heroNode.next;
}
static class LinkNode{
public int id;
public String name;
public LinkNode next;
public LinkNode pre;
public LinkNode(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "LinkNode{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
//遍历链表
public void list(){
if (head.next==null){
System.out.println("链表为空");
return;
}
LinkNode temp=head.next;//代表是当前节点
while (true){
if (temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
思路:
//添加节点
public void addNode(LinkNode newNode){
LinkNode temp=head;
while (true){
if (temp.next==null){
break;
}
temp=temp.next;
}
temp.next=newNode;
newNode.pre=temp;
}
public static void main(String[] args) {
LinkNode linkNode1 = new LinkNode(1,"懒羊羊");
LinkNode linkNode2 = new LinkNode(2,"美羊羊");
LinkNode linkNode3 = new LinkNode(3,"喜洋洋");
LinkNode linkNode4 = new LinkNode(4,"小灰灰");
DoubleLink link = new DoubleLink();
//添加节点
System.out.println("++++++++++没有根据编号添加++++++++++++");
link.addNode(linkNode1);
link.addNode(linkNode2);
link.addNode(linkNode4);
link.addNode(linkNode3);
link.list();
}
思路:temp 表示要插入节点的前一个节点
//添加节点 按照顺序
public void addNodeById(LinkNode newNode){
LinkNode temp=head;
boolean flag=false;//判断要添加的编号是否已经存在
while (true){
if (temp.next==null){
break;
}
if (temp.next.id>newNode.id){
break;
}
if (temp.next.id==newNode.id){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
System.out.println("该编号已经存在");
}else {
if (temp.next!=null){
newNode.next=temp.next;//让新节点的下一个节点 指向 temp的下一个节点 这样就让新节点插入到了temp的后面
temp.next.pre=newNode;//让temp的下一个节点的前一个节点指向新节点 这样就让新节点链接到了temp的下一个节点的前面
}
temp.next=newNode;
newNode.pre=temp;
}
}
System.out.println("++++++++++++根据编号添加++++++++++++");
link.addNodeById(linkNode1);
link.addNodeById(linkNode4);
link.addNodeById(linkNode2);
link.addNodeById(linkNode3);
link.list();
思路:同单链表相同
//修改节点
public void updateNode(LinkNode newNode){
if (head.next==null){
System.out.println("链表为空");
return;
}
LinkNode temp=head.next;
boolean flag=false;
while (true){
if (temp==null){
break;
}
if (temp.id==newNode.id){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
temp.name=newNode.name;
}else {
System.out.println("没有该节点");
}
}
思路:
//删除节点
public void deleteNode(int id){
if (head.next==null){
System.out.println("链表为空");
return;
}
LinkNode temp = head.next;
boolean flag=false;
while (true){
if (temp==null){
break;
}
if (temp.id==id){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
temp.pre.next=temp.next;
//temp.next.pre=temp.pre;//注意这里 temp.next可能为空,因为可能要删除的是最后一个节点 就会出现空指针异常
if (temp.next!=null){
temp.next.pre=temp.pre;
}
}else {
System.out.println("没有该节点");
}
}
LinkNode newNode = new LinkNode(4,"灰太狼");
//修改节点
link.updateNode(newNode);
System.out.println();
System.out.println("++++++++ 修改节点后 +++++++++++++");
link.list();
//删除节点
link.deleteNode(4);
System.out.println();
System.out.println("+++++++ 删除节点后 +++++++++++++");
link.list();