栈:一种先进后出的逻辑数据结构,具体实现方式有两种,链表和数组。
实现
热身:定容栈
API设计(java语言描述)
public class | FixedCapacityStackOfStrings | |
---|---|---|
FixedCapacityStackOfStrings(int cap) | 创建一个容量为cap的空栈 | |
void | push(String item) | 添加一个字符串 |
String | pop() | 删除最后一个添加的字符串 |
boolean | isEmpty() | 栈是否为空 |
int | size() | 栈中的字符串数量 |
实现
class FixedCapacityStackOfStrings(cap: Int = 30, var size: Int = 0) {
private val str = arrayOfNulls<String>(max(cap, 30))
fun isEmpty(): Boolean = size == 0
fun push(item: String) {
str[size++] = item
}
fun pop(): String = str[--size] ?: throw IllegalAccessException("栈内没有元素")
}
测试
fun test01() {
val stack = FixedCapacityStack<String>(15)
stack.push("4")
stack.push("5")
stack.push("6")
println("定容字符串栈现在的大小是${stack.size}") // 3
println("定容字符串栈是否为空:${stack.isEmpty()}") // false
repeat(3) { println("背包弹出元素:${stack.pop()}") } // 3 2 1
println("定容字符串栈是否为空:${stack.isEmpty()}") // true
}
备注:kotlin的StringArray不是基础数组,用它来写数据结构与算法没有意义,这里用Array
特点:使用数组实现,简单易操作,而且能做到后进先出的特性
缺点:必须在初始化时限定大小,并且无法扩容。
未采用泛型编程,所以仅限于字符串操作
融合泛型,实现自定义元素
class FixedCapacityStack<Item>(cap: Int = 30, var size: Int = 0) {
@Suppress("UNCHECKED_CAST")
private val arrays = arrayOfNulls<Any>(max(cap, 30)) as Array<Item?>
fun isEmpty(): Boolean = size == 0
fun push(item: Item) {
arrays[size++] = item
}
fun pop(): Item = arrays[--size] ?: throw IllegalAccessException("栈内没有元素")
}
fun test02() {
val stack = FixedCapacityStackOfStrings(15)
stack.push("1")
stack.push("2")
stack.push("3")
println("定容字符串栈现在的大小是${stack.size}") // 3
println("定容字符串栈是否为空:${stack.isEmpty()}") // false
repeat(3) { println("背包弹出元素:${stack.pop()}") } // 3 2 1
println("定容字符串栈是否为空:${stack.isEmpty()}") // true
}
- 这里要拓展一个点,kotlin的压制异常类型转换用的是 @Suppress(“UNCHECKED_CAST”),和java的**@SuppressWarnings(“unchecked”)**不同
调整数组大小(改造为下压栈)
class ResizingArrayStack<Item>(cap: Int = 30, var size: Int = 0) {
@Suppress("UNCHECKED_CAST")
private var arrays = arrayOfNulls<Any>(max(cap, 10)) as Array<Item?>
fun isEmpty(): Boolean = size == 0
fun push(item: Item) {
// 长度达到上限就扩容
if (size == arrays.size) resize(2 * arrays.size)
arrays[size++] = item
}
fun pop(): Item? = run {
val item = arrays[--size]
arrays[size] = null // 避免对象游离
if (size > 0 && size == arrays.size / 4) resize(arrays.size / 4)
item
}
// 扩容/缩容函数
private fun resize(max: Int) {
@Suppress("UNCHECKED_CAST")
val temp = arrayOfNulls<Any>(max) as Array<Item?>
for (index in 0 until size) {
temp[index] = arrays[index]
}
// println("内存变为${max}")
arrays = temp
}
}
测试
fun test03() {
val stack = ResizingArrayStack<Int>(10)
for (i in 0..40) {
stack.push(i)
}
for (i in 0..40) {
stack.pop()
}
}
日常使用:
// 栈(数组实现)
fun main() {
val stack = Stack<Int>()
stack.push(1)
stack.push(2)
stack.pop().run(::println)
println("==========")
stack.reversed().forEach(::println)
}
typealias Stack<E> = ArrayDeque<E>
fun <E> Stack<E>.push(e: E) = this.add(e)
fun <E> Stack<E>.pop() = this.removeLast()
// 注意:遍历的时候需要stack.reversed()
// typealias Stack<E> = LinkedList<E>
事实上,现代数据结构的ArrayDeque和LinkedList就有压栈和出栈的能力,区别在于LinkedList遍历也是倒序的,而ArrayQueue遍历是正序的,所以,个人习惯用LinkedList
// 同为日常使用
fun main() {
val stack = Stack<Int>()
stack.push(1)
stack.push(2)
stack.push(3)
stack.pop().run(::println)
println("==========")
stack.forEach(::println)
}
typealias Stack<E> = LinkedList<E>
栈(链表实现)
补充概念:链表
链表是一种递归的数据结构,它或者为空(null),或者指向一个结点(node)的引用,该结点含有一个泛型的元素和一个指向另一条链表的引用。
private class Node {
Item item;
Node next;
}
一个node里有两个实例变量,一个存储元素,一个指向下一个Node对象
实现
class Stack2<Item> /*: Iterable<Item>*/ {
private var first: Node? = Node()
var size = 0
private inner class Node(
var item: Item? = null,
var next: Node? = null
)
fun push(item: Item) {
// val oldFirst = first
// first = Node(item, oldFirst)
// first?.item = item
// first?.next = oldFirst
first = Node(item, first)
size++
}
fun pop(): Item {
val item = first?.item
first = first?.next
size--
return item ?: throw IllegalAccessException("栈内没有元素")
}
// override fun iterator(): Iterator<Item> = Iter()
}
fun main() {
val stack = Stack2<Int>()
stack.push(1)
stack.push(2)
repeat(2) { println(stack.pop()) }
}
暂时未实现迭代器遍历
日常使用
fun main() {
val stack = Stack<Int>()
repeat(4, stack::push)
stack.pop().run(::println)
println("==========")
stack.forEach(::println)
}
typealias Stack<E> = LinkedList<E>