专栏第六篇,手写 3 种简单的队列
首先呈上和队列相关的知识图谱。
将队列的共同特征抽象成一个特质
package com.shockang.study.algorithm.archive.queue
/**
* 手写队列
*
* @author Shockang
*/
trait Queue[E] {
//队列大小
var size: Int
//是否为空
def isEmpty: Boolean
//清空队列
def clear()
//入队
def enqueue(e: E)
//出队
def dequeue(): E
}
package com.shockang.study.algorithm.archive.queue
import java.util.StringJoiner
/**
* 数组实现的顺序队列
*
* @author Shockang
*/
class ArrayQueue[E: Manifest](initSize: Int) extends Queue[E] {
//底层存储数组
private var array: Array[E] = new Array(initSize)
//队列开头的指针,这样可以避免每次出队的数组迁移,会导致开头的存储空间浪费,但是在动态扩容/缩容的时候可以填充满开头,以空间换时间
private var start: Int = _
//队列大小
override var size: Int = _
//队列是否为空
override def isEmpty: Boolean = size == 0
//清空队列
override def clear(): Unit = {
array = new Array(initSize)
size = 0
start = 0
}
//入队
override def enqueue(e: E): Unit = {
checkAndExpand()
array(start + size) = e
size += 1
}
//出队
override def dequeue(): E = {
checkAndShrink()
val e = array(start)
//有助于 GC
array(start) = null.asInstanceOf[E]
size -= 1
start += 1
e
}
//方便打印
override def toString: String = {
val sj = new StringJoiner(",")
for (i <- start until start + size) sj.add(array(i).toString)
"[" + sj + "]"
}
//检查并扩容
private def checkAndExpand(): Unit = {
if (start + size + 1 > array.length) change()
}
//检查并缩容
private def checkAndShrink(): Unit = {
if (isEmpty) throw new NoSuchElementException
if (size - 1 < 0.1 * array.length) change()
}
//将数组长度变为当前队列长度的两倍
private def change(): Unit = {
val newSize = size << 1
val newArray: Array[E] = new Array(newSize)
//注意从 start 开始复制数组
System.arraycopy(array, start, newArray, 0, size)
array = newArray
start = 0
}
}
package com.shockang.study.algorithm.archive.queue
import java.util.StringJoiner
/**
* 单链表实现的链式队列
*
* @author Shockang
*/
class LinkedListQueue[E] extends Queue[E] {
//单链表单个结点
private class Node(var item: E, var next: Node)
//单链表头结点
private var head: Node = _
//单链表尾结点
private var tail: Node = _
//队列大小
override var size: Int = _
//队列是否为空
override def isEmpty: Boolean = size == 0
//清空队列
override def clear(): Unit = {
head = null
size = 0
}
//入队
override def enqueue(e: E): Unit = {
val next = new Node(e, null)
if (isEmpty) head = next
else tail.next = next
tail = next
size += 1
}
//出队
override def dequeue(): E = {
if (isEmpty) throw new NoSuchElementException
val e = head.item
head = head.next
size -= 1
if (isEmpty) tail = head
e
}
//方便打印
override def toString: String = {
val sj = new StringJoiner(",")
var node = head
while (node != null) {
sj.add(node.item.toString)
node = node.next
}
"[" + sj + "]"
}
}
package com.shockang.study.algorithm.archive.queue
import java.util.StringJoiner
/**
* 数组实现的循环队列
*/
class CircularQueue[E: Manifest](initSize: Int) extends Queue[E] {
//底层存储数组
private var array: Array[E] = new Array(initSize)
//队列头指针
private var head: Int = 0
//队列尾的后继指针
private var tail: Int = 0
//队列大小
override var size: Int = _
//队列是否为空,这里有两种判断方法:1.size == 0;2.head == tail
override def isEmpty: Boolean = size == 0
//清空队列
override def clear(): Unit = {
array = new Array(initSize)
head = 0
tail = 0
size = 0
}
//入队
override def enqueue(e: E): Unit = {
checkAndExpand()
//到队尾了就从头入队,循环队列
if (tail == array.length) tail = 0
array(tail) = e
tail += 1
size += 1
}
//出队
override def dequeue(): E = {
checkAndShrink()
val e = array(head)
if (head == array.length - 1) head = 0
else head += 1
size -= 1
e
}
//方便打印
override def toString: String = {
val sj = new StringJoiner(",")
if (head < tail) {
for (i <- head until tail) sj.add(array(i).toString)
} else if (head > tail) {
for (i <- head until array.length) sj.add(array(i).toString)
for (i <- 0 until tail) sj.add(array(i).toString)
}
"[" + sj + "]"
}
//检查并扩容
private def checkAndExpand(): Unit = {
if (size + 1 > array.length) change()
}
//检查并缩容
private def checkAndShrink(): Unit = {
if (isEmpty) throw new NoSuchElementException
if (size - 1 < 0.1 * array.length) change()
}
//当前数组大小变成队列长度的两倍
private def change(): Unit = {
val newArray: Array[E] = new Array(if (size == 0) 1 else size << 1)
//数据迁移
var index: Int = 0
//这里需要分类讨论
if (head < tail) {
for (i <- head until tail) {
newArray(index) = array(i)
index += 1
}
} else if (head > tail) {
//先迁移队头到数组结尾的
for (i <- head until array.length) {
newArray(index) = array(i)
index += 1
}
//再迁移数组开头到队尾的
for (i <- 0 until tail) {
newArray(index) = array(i)
index += 1
}
}
head = 0
tail = size
array = newArray
}
}
package com.shockang.study.algorithm.archive.queue
object Main extends App {
//val queue: Queue[Int] = new ArrayQueue[Int](10)
//val queue: Queue[Int] = new LinkedListQueue[Int]()
val queue: Queue[Int] = new CircularQueue[Int](5)
out()
try {
queue.dequeue()
} catch {
case e: Exception => println(e)
}
queue.enqueue(0)
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
queue.enqueue(4)
queue.enqueue(5)
queue.enqueue(6)
queue.enqueue(7)
queue.enqueue(8)
queue.enqueue(9)
out()
queue.enqueue(10)
out()
println(queue.dequeue())
out()
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
out()
println(queue.dequeue())
out()
try {
println(queue.dequeue())
} catch {
case e: Exception => println(e)
}
queue.clear()
out()
def out(): Unit = {
println("************************************************")
try {
println(queue.size)
println(queue.isEmpty)
println(queue)
} catch {
case e: Exception => println(e)
}
println("************************************************")
}
}
************************************************
0
true
[]
************************************************
java.util.NoSuchElementException
************************************************
10
false
[0,1,2,3,4,5,6,7,8,9]
************************************************
************************************************
11
false
[0,1,2,3,4,5,6,7,8,9,10]
************************************************
0
************************************************
10
false
[1,2,3,4,5,6,7,8,9,10]
************************************************
1
2
3
4
5
6
7
8
9
************************************************
1
false
[10]
************************************************
10
************************************************
0
true
[]
************************************************
java.util.NoSuchElementException
************************************************
0
true
[]
************************************************
个人网站:
https://www.shockang.com