基础算法-队列(二)
理论部分为转载,算法实现部分为自己写的。
线性队列
队列同样是一种特殊的线性表,其插入和删除的操作分别在表的两端进行,队列的特点就是先进先出(First In First Out)。我们把向队列中插入元素的过程称为入队(Enqueue),删除元素的过程称为出队(Dequeue)并把允许入队的一端称为队尾,允许出的的一端称为队头,没有任何元素的队列则称为空队。
队列和栈都是一种最基础的数据结构,可以看到它们都是使用数组来实现的,也可以使用链表或其他来实现,只需要保证队列的约定就好。(栈也可以使用链表来实现)
代码实现:
public class QueueDome {
private int front;
private int rear;
private int[] array;
private int size;
private final int ARRAY_LENGTH = 10;
public QueueDome() {
array = new int[ARRAY_LENGTH];
front = 0;
rear = 0;
size = 0;
}
//入队列
public void add(int value) {
checkIsFull();
array[rear++] = value;
size++;
}
//出队列
public int pull() {
if ((front + 1) == ARRAY_LENGTH) {
throw new RuntimeException("没有元素了");
}
return array[front++];
}
public int peek() {
return array[front];
}
public boolean isFull() {
return size > ARRAY_LENGTH;
}
public boolean isEmpaly() {
return size == 0;
}
public void checkIsFull() {
if (isFull()) {
throw new RuntimeException("队列已经满了");
}
}
public static void main(String args[]) {
QueueDome dome = new QueueDome();
dome.add(1);
dome.add(2);
dome.add(3);
dome.add(4);
dome.add(5);
System.out.println(dome.peek());
dome.add(6);
dome.add(7);
System.out.println(dome.peek());
dome.add(8);
System.out.println(dome.peek());
dome.add(9);
dome.add(10);
System.out.println(dome.pull());
System.out.println(dome.pull());
System.out.println(dome.pull());
System.out.println(dome.pull());
System.out.println(dome.pull());
System.out.println(dome.pull());
System.out.println(dome.pull());
System.out.println(dome.peek());
System.out.println(dome.peek());
}
解说:front指向队列头,rear指向队列尾。插入元素通过rear++来做,而读取元素通过front元素来操作。这样不就实现了一个队列嘛。
一头只能做插入动作,一头做读取操作。1元素先插入进来的,读取的时候1也是最先被读取到的。
参考地址:
http://blog.youkuaiyun.com/javazejian/article/details/53375004
参考的文章中实现了循环队列,删除的元素空出来的位置可以继续使用,其实这样会把队列的复杂度提高很多。没有多大实际意义,所以在这里就不做实现。如果要删除元素可以使用链表来实现队列,也称为链表队列这样做删除操作更好。
链式队列
链式队列就是通过链表来做的队列。
package cn.datastruts;
public class NodeQueueDome {
private Node head;
private Node tail;
private int size;
private static final int ARRAY_LENGTH = 10;
public NodeQueueDome() {
}
//入队列
public void add(int value) {
Node node = new Node(null, value);
if (head == null) {
head = node;
}
if (tail != null) {
tail.next = node;
}
tail = node;
size++;
}
//出队列
public int pull() {
int value = -1;
if (isEmpaly()) {
return value;
}
Node t = head;
if (t != null) {
value = t.data;
head = t.next;//new head
t.next = null;//delete old head
}
size--;
return value;
}
public int peek() {
return head.data;
}
public boolean isFull() {
return size > ARRAY_LENGTH;
}
public boolean isEmpaly() {
return size == 0;
}
public void checkIsFull() {
if (isFull()) {
throw new RuntimeException("队列已经满了");
}
}
private static class Node {
Node next;
int data;
public Node(Node next, int data) {
this.next = next;
this.data = data;
}
}
public static void main(String args[]) {
NodeQueueDome dome = new NodeQueueDome();
for (int i = 0; i < 10; i++) {
dome.add(i + 1);
}
while (!dome.isEmpaly()) {
System.out.println(dome.pull());
}
}
}
链表中添加节点图解:
苦笑不得两行代码,都开始怀疑自己不是搞java开发的了。
当tail!=null时
tail.next=node;
tail=node;
当时这两句把我给绕晕了,我的理解是
tail=node,然后tail.next也指向了node,我就以为是node自己指向了自己。
第一步:其实忘记了tail刚开始是指向head这个节点的,tail.next改变了了值,则head的值也跟着改变了,所以head.next也指向了node。
第二步:tail=node。相当于 tail = new Node(null,value);tail已经和前面的head节点没有关系了,现在的tail.next等于null了。
写代码写多了把自己给搞晕了。
tail=node;
head=node;
tail.next= node2;
这样修改会把head的next也跟着修改了,因为它们引用的是同一个对象,tail.next修改的是它们共同的对象node的next,所以一处修改大家一起跟着变了。
修改同一个引用的对象的属性,被引用的对象的属性变了,引用的属性也跟着变了。
tail = node ;
head = node;
tail = node2;
这样是不会把head 也跟着修改的。因为tail是修改了自己的引用,把tail自己的引用修改为node2了。
修改自己的引用地址,仅此而已。代码看多了,把自己给看晕了。