赞
踩
队列是一个有序列表,可以使用数组或链表实现
一次性队列,不能复用:
思路:队列(数组)最大长度: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();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。