committed by
GitHub
4 changed files with 243 additions and 18 deletions
@ -0,0 +1,107 @@ |
|||
import { beforeEach, describe, expect, it } from 'vitest'; |
|||
|
|||
import { createStack, Stack } from '../stack'; |
|||
|
|||
describe('stack', () => { |
|||
let stack: Stack<number>; |
|||
|
|||
beforeEach(() => { |
|||
stack = new Stack<number>(); |
|||
}); |
|||
|
|||
it('push & size should work', () => { |
|||
stack.push(1, 2); |
|||
|
|||
expect(stack.size).toBe(2); |
|||
}); |
|||
|
|||
it('peek should return top element without removing it', () => { |
|||
stack.push(1, 2); |
|||
|
|||
expect(stack.peek()).toBe(2); |
|||
expect(stack.size).toBe(2); |
|||
}); |
|||
|
|||
it('pop should remove and return top element', () => { |
|||
stack.push(1, 2); |
|||
|
|||
expect(stack.pop()).toBe(2); |
|||
expect(stack.size).toBe(1); |
|||
expect(stack.peek()).toBe(1); |
|||
}); |
|||
|
|||
it('pop on empty stack should return undefined', () => { |
|||
expect(stack.pop()).toBeUndefined(); |
|||
expect(stack.peek()).toBeUndefined(); |
|||
}); |
|||
|
|||
it('clear should remove all elements', () => { |
|||
stack.push(1, 2); |
|||
|
|||
stack.clear(); |
|||
|
|||
expect(stack.size).toBe(0); |
|||
expect(stack.peek()).toBeUndefined(); |
|||
}); |
|||
|
|||
it('toArray should return a shallow copy', () => { |
|||
stack.push(1, 2); |
|||
|
|||
const arr = stack.toArray(); |
|||
arr.push(3); |
|||
|
|||
expect(stack.size).toBe(2); |
|||
expect(stack.toArray()).toEqual([1, 2]); |
|||
}); |
|||
|
|||
it('dedup should remove existing item before push', () => { |
|||
stack.push(1, 2, 1); |
|||
|
|||
expect(stack.toArray()).toEqual([2, 1]); |
|||
expect(stack.size).toBe(2); |
|||
}); |
|||
|
|||
it('dedup = false should allow duplicate items', () => { |
|||
const s = new Stack<number>(false); |
|||
|
|||
s.push(1, 1, 1); |
|||
|
|||
expect(s.toArray()).toEqual([1, 1, 1]); |
|||
expect(s.size).toBe(3); |
|||
}); |
|||
|
|||
it('remove should delete all matching items', () => { |
|||
stack.push(1, 2, 1); |
|||
|
|||
stack.remove(1); |
|||
|
|||
expect(stack.toArray()).toEqual([2]); |
|||
expect(stack.size).toBe(1); |
|||
}); |
|||
|
|||
it('maxSize should limit stack capacity', () => { |
|||
const s = new Stack<number>(true, 3); |
|||
|
|||
s.push(1, 2, 3, 4); |
|||
|
|||
expect(s.toArray()).toEqual([2, 3, 4]); |
|||
expect(s.size).toBe(3); |
|||
}); |
|||
|
|||
it('dedup + maxSize should work together', () => { |
|||
const s = new Stack<number>(true, 3); |
|||
|
|||
s.push(1, 2, 3, 2); // 去重并重新入栈
|
|||
|
|||
expect(s.toArray()).toEqual([1, 3, 2]); |
|||
expect(s.size).toBe(3); |
|||
}); |
|||
|
|||
it('createStack should create a stack instance', () => { |
|||
const s = createStack<number>(true, 2); |
|||
|
|||
s.push(1, 2, 3); |
|||
|
|||
expect(s.toArray()).toEqual([2, 3]); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,103 @@ |
|||
/** |
|||
* @zh_CN 栈数据结构 |
|||
*/ |
|||
export class Stack<T> { |
|||
/** |
|||
* @zh_CN 栈内元素数量 |
|||
*/ |
|||
get size() { |
|||
return this.items.length; |
|||
} |
|||
/** |
|||
* @zh_CN 是否去重 |
|||
*/ |
|||
private readonly dedup: boolean; |
|||
/** |
|||
* @zh_CN 栈内元素 |
|||
*/ |
|||
private items: T[] = []; |
|||
|
|||
/** |
|||
* @zh_CN 栈的最大容量 |
|||
*/ |
|||
private readonly maxSize?: number; |
|||
|
|||
constructor(dedup = true, maxSize?: number) { |
|||
this.maxSize = maxSize; |
|||
this.dedup = dedup; |
|||
} |
|||
|
|||
/** |
|||
* @zh_CN 清空栈内元素 |
|||
*/ |
|||
clear() { |
|||
this.items.length = 0; |
|||
} |
|||
|
|||
/** |
|||
* @zh_CN 查看栈顶元素 |
|||
* @returns 栈顶元素 |
|||
*/ |
|||
peek(): T | undefined { |
|||
return this.items[this.items.length - 1]; |
|||
} |
|||
|
|||
/** |
|||
* @zh_CN 出栈 |
|||
* @returns 栈顶元素 |
|||
*/ |
|||
pop(): T | undefined { |
|||
return this.items.pop(); |
|||
} |
|||
|
|||
/** |
|||
* @zh_CN 入栈 |
|||
* @param items 要入栈的元素 |
|||
*/ |
|||
push(...items: T[]) { |
|||
items.forEach((item) => { |
|||
// 去重
|
|||
if (this.dedup) { |
|||
const index = this.items.indexOf(item); |
|||
if (index !== -1) { |
|||
this.items.splice(index, 1); |
|||
} |
|||
} |
|||
this.items.push(item); |
|||
if (this.maxSize && this.items.length > this.maxSize) { |
|||
this.items.splice(0, this.items.length - this.maxSize); |
|||
} |
|||
}); |
|||
} |
|||
/** |
|||
* @zh_CN 移除栈内元素 |
|||
* @param itemList 要移除的元素列表 |
|||
*/ |
|||
remove(...itemList: T[]) { |
|||
this.items = this.items.filter((i) => !itemList.includes(i)); |
|||
} |
|||
/** |
|||
* @zh_CN 保留栈内元素 |
|||
* @param itemList 要保留的元素列表 |
|||
*/ |
|||
retain(itemList: T[]) { |
|||
this.items = this.items.filter((i) => itemList.includes(i)); |
|||
} |
|||
|
|||
/** |
|||
* @zh_CN 转换为数组 |
|||
* @returns 栈内元素数组 |
|||
*/ |
|||
toArray(): T[] { |
|||
return [...this.items]; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @zh_CN 创建一个栈实例 |
|||
* @param dedup 是否去重 |
|||
* @param maxSize 栈的最大容量 |
|||
* @returns 栈实例 |
|||
*/ |
|||
export const createStack = <T>(dedup = true, maxSize?: number) => |
|||
new Stack<T>(dedup, maxSize); |
|||
Loading…
Reference in new issue