专栏第五篇,通过两种方式实现栈。
- 数组
- 单链表
知识图谱
先呈上关于栈的知识图谱。
栈的 API
先将栈的公共特征提取出来成为特质。
package com.shockang.study.algorithm.archive.stack
/**
* 将栈的共同特征抽象成特质
*
* @author Shockang
*/
trait Stack[E] {
//大小
var size: Int
//是否为空
def isEmpty: Boolean
//入栈
def push(e: E)
//出栈
def pop(): E
//查看栈顶
def peek(): E
//清空栈
def clear()
}
然后基于这个特质提供两种不同的实现方式。
数组实现
package com.shockang.study.algorithm.archive.stack
import java.util.StringJoiner
/**
* 数组实现的栈
*
* @author Shockang
*/
class ArrayStack[E: Manifest](initSize: Int) extends Stack[E] {
//底层用数组存储
private var array: Array[E] = if (initSize > 0) new Array(initSize) else throw new IllegalArgumentException
//当前数组最大容量
private var maxSize: Int = initSize
//当前栈大小
override var size: Int = 0
//是否为空
override def isEmpty: Boolean = size == 0
//入栈
override def push(e: E): Unit = {
//检查是否需要扩容
checkAndExpand()
array(size) = e
size += 1
}
//出栈
override def pop(): E = {
//此时栈不能为空
if (isEmpty) throw new NoSuchElementException
val e: E = array(size - 1)
array(size - 1) = null.asInstanceOf[E]
size -= 1
e
}
//查看栈顶
override def peek(): E = {
//此时栈不能为空
if (isEmpty) throw new NoSuchElementException
array(size - 1)
}
//清空栈
override def clear(): Unit = {
array = new Array(initSize)
maxSize = initSize
size = 0
}
//方便打印
override def toString: String = {
val sj: StringJoiner = new StringJoiner(",")
for (i <- 0 until size) {
sj.add(array(i).toString)
}
"[" + sj + "]"
}
//检查是否需要扩容
private def checkAndExpand(): Unit = {
if (size + 1 > maxSize) {
//两倍扩容
maxSize <<= 1
var oldArray: Array[E] = array
array = new Array(maxSize)
//通过System.arraycopy迁移数据到新数组
System.arraycopy(oldArray, 0, array, 0, size)
//有助于 GC
oldArray = null
}
}
}
单链表实现
package com.shockang.study.algorithm.archive.stack
import java.util.StringJoiner
/**
* 单链表实现的栈
*
* @author Shockang
*/
class LinkedListStack[E] extends Stack[E] {
//单链表单个结点,存储值和后继指针
private class Node(var item: E, var next: Node)
//单链表头结点
private var head: Node = _
//栈大小
override var size: Int = 0
//是否为空
override def isEmpty: Boolean = size == 0
//入栈
override def push(e: E): Unit = {
//注意倒序添加
head = new Node(e, head)
size += 1
}
//出栈
override def pop(): E = {
//判空
if (isEmpty) throw new NoSuchElementException
val temp = head
head = temp.next
size -= 1
temp.item
}
//查看栈顶
override def peek(): E = {
//判空
if (isEmpty) throw new NoSuchElementException
head.item
}
//清空栈
override def clear(): Unit = {
head = null
size = 0
}
//方便打印
override def toString: String = {
val sj: StringJoiner = new StringJoiner(",")
var node = reverse(head)
head = node
while (node != null) {
sj.add(node.item.toString)
node = node.next
}
//反转之后别忘了还原,好吧,这里这么搞主要还是为了复习两种反转链表的方法
head = reverse2(head)
"[" + sj + "]"
}
//为了和数组实现打印保持一致,这里利用递归将单链表反转
private def reverse(node: Node): Node = {
if (node == null || node.next == null) return node
val temp = node.next
val newHead = reverse(node.next)
temp.next = node
node.next = null
newHead
}
//利用迭代将单链表反转
private def reverse2(node: Node): Node = {
var temp: Node = null
var pre: Node = null
var cur: Node = node
while (cur != null) {
temp = cur.next
cur.next = pre
pre = cur
cur = temp
}
pre
}
}
测试
package com.shockang.study.algorithm.archive.stack
object Main extends App {
//var stack: Stack[Int] = new ArrayStack(5)
var stack: Stack[Int] = new LinkedListStack()
out(stack)
try {
stack.pop()
} catch {
case e: Exception => println(e)
}
stack.push(0)
stack.push(1)
stack.push(2)
stack.push(3)
stack.push(4)
out(stack)
stack.push(5)
out(stack)
println(stack.pop())
println(stack.pop())
out(stack)
stack.clear()
out(stack)
def out(stack: Stack[Int]): Unit = {
println("************************************************")
try {
println(stack.size)
println(stack.isEmpty)
println(stack)
println(stack.peek())
} catch {
case e: Exception => println(e)
}
println("************************************************")
}
}
输出
************************************************
0
true
[]
java.util.NoSuchElementException
************************************************
java.util.NoSuchElementException
************************************************
5
false
[0,1,2,3,4]
4
************************************************
************************************************
6
false
[0,1,2,3,4,5]
5
************************************************
5
4
************************************************
4
false
[0,1,2,3]
3
************************************************
************************************************
0
true
[]
java.util.NoSuchElementException
************************************************
个人网站: