数据结构(3)双端栈的动态数组实现ArrayStackDoubleEnd

欢迎来到第三章

在此处稍微回顾一下前两章的内容

在第一章我们从“零”开始实现了一个ArrayList线性表,我把它称之为是一种底层数据结构

在第二章我们用ArrayList实现了一个栈结构ArrayStack,通过底层数据结构的封装,继而产生出了更加高级的数据结构

所以,请大家注意,在数据结构的广袤世界中,高级的数据结构就是这样子由底层数据结构一层一层封装起来的

  • 先比如后面要学的Trie前缀树,就是 多叉树 + 映射 实现的高级数据结构
  • 仍比如后面要学的红黑树,就是 二叉树 + 2-3树 + 自平衡技术 实现的高高级数据结构
  • 再比如后面要学的哈希表,就是 数组 + 红黑树 实现的高高高级数据结构

所以,想学好数据结构,最好的途径就是从底层做起

还有,我想提醒一下大家,数据结构的设计不是一成不变的,大家可以完全按照自己的需求去定义一种新的数据结构,这都是完全可以的!数据结构不是死知识,而是活学活用的,比如这一章,给大家介绍一种比较奇葩但是非常常用的数据结构——双端栈

大家加油!

 

什么是双端栈

直白的说,双端栈是指一个数组中,存在两个栈,其中一个栈的栈底在数组表头,另一个在数组表尾

主要利用了栈“栈底位置不变,而栈顶位置动态变化” 的特性

如图所示

可以看出我们此时需要两个栈顶的标记,那么之前学过的ArrayList还能直接拿来用吗?

答案是不能的,为什么,因为在实现ArrayStack时,其实我们巧妙地使用了ArrayList中的 size-1 当成了栈顶指针

size不仅可以当做记录元素个数使用,也可以当做在表尾插入新元素时新元素的位置,然后size++

看图说明,其实会发现实际是 size-1 当做了ArrayStack的栈顶,明白了吧?

那么我们的双端栈是需要两个栈顶指针的,所以直接用ArrayList是不行的

但是双端栈本身还是栈结构,所以还是需要实现上一章的Stack接口

来,开始写代码吧

1.双端栈类的定义ArrayStackDoubleEnd

public class ArrayStackDoubleEnd<E> implements Stack<E>{
	private E[] data;        //元素容器
	private int left;        //左端栈顶 从-1开始
                                 //(left+1)表示左端栈元素的个数
	private int right;       //右端栈顶 date.length开始
                                 //(data.length-right)表示右端栈元素的个数
	public static final int L=0;	//左端栈标记
	public static final int R=1;	//右端栈标记
	private int size;        //栈中元素的总个数
	public ArrayStackDoubleEnd() {    //创建默认容量为10的双端栈
		this(10);        
	}
	public ArrayStackDoubleEnd(int capacity){    //创建指定容量为capacity的双端栈
		data=(E[]) new Object[capacity];
		left=-1;
		right=data.length;
		size=0;
	}
}

2.getSize()函数

获取栈中元素的个数

但是我们这里面有两个栈,获取哪个呢?可以再增加个方法getSize(int which),获取指定端栈的元素个数

因为在类定义中,已经定义了L和R两个标记了

那么重写的getSize()可以获取的是全部元素个数

之后的操作也是这个思路

	public int getSize() {
		return size;
	}
        public int getSize(int which){
		if(which==L){
			return left+1;
		}else{
			return data.length-right;
		}
	}

3.isEmpty()函数

判断栈是否为空;判断指定端的栈是否为空

	public boolean isEmpty() {
		return left==-1&&right==data.length&&size==0;
	}
	public boolean isEmpty(int which){
		if(which==L){
			return left==-1;
		}else{
			return right==data.length;
		}
	}

4.clear()函数

清空栈;清空某一端的栈

	public void clear() {
		left=-1;
		right=data.length;
		size=0;
	}
	public void clear(int which){
		if(which==L){
			left=-1;
		}else{
			right=data.length;
		}
	}

5.getCapacity()函数

获取双端栈的容量

	public int getCapacity(){
		return data.length;
	}

6.push()函数

入栈一个元素;向指定端入栈一个元素

注意,在没有指定入栈到某一端时,逻辑是这样处理的

左端元素个数少于右端元素个数,入栈左端

右端元素个数少于左端元素个数,入栈右端

当 left+1==right 时,表示栈满了,此处应该进行扩容操作,但是这里我就不实现了,留个大家来实现,思路和ArrayList一样

	public void push(E e) {
		if(size==data.length){
			throw new IllegalArgumentException("栈已满!");
		}
		if(getSize(L)<=getSize(R)){
			push(L,e);
		}else{
			push(R,e);
		}
	}
        public void push(int which,E e){
		if(size==data.length){
			throw new IllegalArgumentException("栈已满!");
		}
		if(which==L){
			data[++left]=e;
		}else{
			data[--right]=e;
		}
		size++;
	}

7.pop()函数

出栈一个元素;指定某一端出栈一个元素

同样,在没有指定哪个端出栈时

左端元素个数大于右端,出栈左端

右端元素个数大于左端,出栈右端

	public E pop() {
		if(getSize(L)>=getSize(R)){
			return pop(L);
		}else{
			return pop(R);
		}
	}
	public E pop(int which){
		if(isEmpty(which)){
			throw new IllegalArgumentException("栈已空!");
		}
		size--;
		if(which==L){
			return data[left--];
		}else{
			return data[right++];
		}
	}

8.peek()函数

查看栈顶元素;查看指定端的栈顶元素

	public E peek() {
		if(getSize(L)>=getSize(R)){
			return peek(L);
		}else{
			return peek(R);
		}
	}
	public E peek(int which){
		if(isEmpty(which)){
			throw new IllegalArgumentException("栈为空!");
		}
		if(which==L){
			return data[left];
		}else{
			return data[right];
		}
	}

好啦,至此双端栈讲完了~大家加油

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值