数据结构02——栈和队列

“栈”是我们常常听到的一个术语,那么什么是栈呢,很简单,栈(Stack)是一个后进先出(Last in first out,简称:LIFO)的线性表,它只能从一端添加元素,也只能从一端去除元素,这一端就称为:栈顶。在计算机的世界中,栈是有着一些不可思议的作用的,例如你在编辑器中输入你想要输入的文字是,当你发现输入有误时,你会进行撤回的操作。而这就是无处不在的Undo操作。还有你安装软件时的next和back操作等等。

接下来我们就将继续我们的探索之路,来自己手写实现一个栈功能。

 

一.栈

1.首先我们定义一个栈的接口

 

public interface Stack<E> {
	
	int getSize();//获取栈的大小
	boolean isEmpty();//判断栈是够为空
	void push(E e);//进行压栈操作
	E pop();//进行出站操作
	E peek();//查看栈顶内容

}

2.实现栈的主要数据机制

栈的机制可以用数组来实现,也可以用链表来实现,我们将用上篇文章“小小数组”,不可小觑中所创建的数组来实现栈的基本操作:

package com.zfy.stackorqueues;


public class Array<E> {
	
		private E[] data;
		
		private int size;
		
		//构造函数,传入数组的容量capacity构造Array数组
		public Array(int capacity) {
			data = (E[]) new Object[capacity];
			size = 0;		
		}
		
		//无参数构造函数,默认数组的容量capacity=10
		public Array() {
			this(10);
		}
		
		//获取数组中的元素个数
		public int getSize() {
			return size;
		}
		
		//获取数组的容量
		public int getCapacity() {
			return data.length;
		}
		
		
		//返回数组是否为空
		public boolean isEmpty() {
			return size == 0;
		}
		
		//向所有元素后添加一个新元素
		public void addLast(E e) {
			add(size, e);
		}
		
		// 在所有元素前添加一个新元素
		public void addFirst(E e) {
			add(0, e);
		}
		
		// 在index索引的位置插入一个新元素e
		public void add(int index, E e) {
			
			if(index < 0 || index > size) 
				throw new IllegalArgumentException("AddLast failed. Require index >= 0 and index <= size.");
			
			if(size == data.length) 
				resize(2*data.length);
			
			for (int i = size-1; i >= index; i--) 
				data[i+1] = data[i];
			data[index] = e;
			size ++;
		}
		
		//获取index索引位置的元素
		public E get(int index) {
			if(index < 0 || index >= size) 
				throw new IllegalArgumentException("Get failed. Index is illegal");
			return data[index];
		}
		
		public E getLast() {
			return get(size - 1);
		}
		
		public E getFirst() {
			return get(0);
		}
		
		//修改index索引位置的元素为e
		public void set(int index, E e) {
			if(index < 0 || index >= size)
	            throw new IllegalArgumentException("Set failed. Index is illegal.");
			data[index] = e;
		}
		
		//查找数组中是否有元素e
		public boolean cotains(E e) {
			for (int i = 0; i < size; i++) 
				if (data[i].equals(e)) 
					return true;
			return false;
		}
		
		//查找数组中元素e所在的索引,如果不存在元素e,则返回-1
		public int find(E e) {
			for (int i = 0; i < size; i++) 
				if (data[i].equals(e)) 
					return i;
			return -1;
		}
		
		//从数组中删除index位置的元素, 返回删除的元素
		public E remove(int index) {
			if(index < 0 || index >= size)
	            throw new IllegalArgumentException("Remove failed. Index is illegal.");
			
			E ret = data[index];
			for (int i = index + 1; i < size; i++) 
				data[i -1] = data[i];
			size --;
			data[size] = null; // loitering objects != memory leak
			
			if(size == data.length / 4)
	            resize(data.length / 2);
			
			return ret;	
		}
		
		//从数组中删除第一个元素, 返回删除的元素
		public E removeFirst() {
			return remove(0);
		}
		
		// 从数组中删除最后一个元素, 返回删除的元素
	    public E removeLast(){
	        return remove(size - 1);
	    }
		
	    //从数组中删除元素e
	    public void removeElement(E e) {
	    	int index = find(e);
	    	if (index != -1)
				remove(index);
	    }
	    
		@Override
		public String toString() {
			
			StringBuilder res = new StringBuilder();
			res.append(String.format("Array: size = %d, capacity = %d \n ", size,data.length));
			res.append("[");
			for (int i = 0; i < size; i++) {
				res.append(data[i]);
				if (i != size - 1) {
					res.append(",");
				}
			}
			res.append("]");		
			return res.toString();
		}
		

		// 将数组空间的容量变成newCapacity大小
		private void resize(int newCapacity) {
			
			E[] newData = (E[]) new Object[newCapacity];
			for (int i = 0; i < size; i++) 
				newData[i] = data[i];
			data = newData;
		}

}

3.最后实现栈的功能


package com.zfy.stackorqueues;

public class ArrayStack<E> implements Stack<E> {

	Array<E> array;
	
	public ArrayStack(int capacity) {
		array = new Array<>(capacity);
	}
	
	public ArrayStack() {
		array = new Array<>();
	}
	
	@Override
	public int getSize() {
		return array.getSize();
	}

	@Override
	public boolean isEmpty() {
		return array.isEmpty();
	}

	public int getCapaCity() {
		return array.getCapacity();
	}
	@Override
	public void push(E e) {
		array.addLast(e);
	}

	@Override
	public E pop() {
		return array.removeLast();
	}

	@Override
	public E peek() {
		return array.getLast();
	}

	@Override
	public String toString() {
		StringBuilder res = new StringBuilder();
		res.append("Stack: ");
		res.append("[");
		for (int i = 0; i < array.getSize(); i++) {
			res.append(array.get(i));
			if (i != array.getSize() - 1) {
				res.append(", ");
			}
		}
		res.append("] top");
		return res.toString();
	}
}

 

4.测试类以及用栈实现符号匹配功能

 

1.测试类:

        public static void main(String[] args) {
		
			ArrayStack<Integer> stack = new ArrayStack<>();

	        for(int i = 0 ; i < 5 ; i ++){
	            stack.push(i);
	            System.out.println(stack);
	        }

	        stack.pop();
	        System.out.println(stack);
	}

2.实现符号匹配功能

    public boolean isValid(String s) {

        ArrayStack<Character> stack = new ArrayStack<>();
        for(int i = 0 ; i < s.length() ; i ++){
            char c = s.charAt(i);
            if(c == '(' || c == '[' || c == '{')
                stack.push(c);
            else{
                if(stack.isEmpty())
                    return false;

                char topChar = stack.pop();
                if(c == ')' && topChar != '(')
                    return false;
                if(c == ']' && topChar != '[')
                    return false;
                if(c == '}' && topChar != '{')
                    return false;
            }
        }
        return stack.isEmpty();
    }

    public static void main(String[] args) {

        System.out.println((new Solution()).isValid("()[]{}"));
        System.out.println((new Solution()).isValid("([)]"));
    }

  数据的入栈和出栈的时间复杂度均为O(1),因此这个栈在时间性能上是很好的。

 

二.队列

队列也是线性结构,与数组相比较,队列对应的操作是数组的子集。而且队列它只能从一端(队尾)添加元素,且只能从另一端(队首)取出元素。这个和我们日常生活中的排队是类似的,因此队列是一种先进先出(First In First Out)的数据结构。接下来我们将用数组机制来实现一个数组队列。

 

1.定义一个队列的接口

public interface Queue<E> {

    int getSize();//获取队列size
    boolean isEmpty();//判断队列是否为空
    void enqueue(E e);//入队操作
    E dequeue();//出队操作
    E getFront();//获取 front
}

2.实现队列的接口

因为我们这个队列和栈一样都是用我们前面的数组进行实现的,因此这里就不再贴代码了。

package com.zfy.stackorqueues;

public class ArrayQueue<E> implements Queue<E> {

    private Array<E> array;

    public ArrayQueue(int capacity){
        array = new Array<>(capacity);
    }

    public ArrayQueue(){
        array = new Array<>();
    }

    @Override
    public int getSize(){
        return array.getSize();
    }

    @Override
    public boolean isEmpty(){
        return array.isEmpty();
    }

    public int getCapacity(){
        return array.getCapacity();
    }

    @Override
    public void enqueue(E e){
        array.addLast(e);
    }

    @Override
    public E dequeue(){
        return array.removeFirst();
    }

    @Override
    public E getFront(){
        return array.getFirst();
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append("Queue: ");
        res.append("front [");
        for(int i = 0 ; i < array.getSize() ; i ++){
            res.append(array.get(i));
            if(i != array.getSize() - 1)
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }

    public static void main(String[] args) {

        ArrayQueue<Integer> queue = new ArrayQueue<>();
        for(int i = 0 ; i < 10 ; i ++){
            queue.enqueue(i);
            System.out.println(queue);
            if(i % 3 == 2){
                queue.dequeue();
                System.out.println(queue);
            }
        }
    }
}

3.最后我们来实现一个循环队列

因为数组队列是具有局限性的,因为它的出队操作的复杂度为O(n)级别的,因此我们再来实现一个循环数组。

package com.zfy.stackorqueues;

public class LoopQueue<E> implements Queue<E> {
	
	private E[] data;//不再使用之前的数组,我们将从底层写起
	private int front;//头
	private int tail;//尾
	private int size;
	
	public LoopQueue(int capacity) {
		data = (E[]) new Object[capacity + 1];
		front = 0;
		tail = 0;
		size = 0;
	}
	
	public LoopQueue() {
		this(10);//定义无参构造函数,设置其初始长度为10
	}
	
	public int getCapacity() {
		return data.length - 1;//
	}
	
	@Override
    public boolean isEmpty(){
        return front == tail;
    }

    @Override
    public int getSize(){
        return size;
    }
	
    @Override
    public void enqueue(E e) {
    	
    	if (tail + 1 % data.length == front) {//判断tail+1余当前数组的length是否等于front,如果等于则队列是满的
			resize(getCapacity() * 2);//这里使用getCapaCity(),是因为我们前面设置的数组length可以减了一个1
		}
    	data[tail] = e;
    	tail = (tail + 1) % data.length;//本来是tail++;但是由于是循环数组,所以需要这样写
    	size ++;
    }
    
    @Override
    public E dequeue() {
    	if(isEmpty())
    		throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
    	E ret = data[front];
    	data[front] = null;
    	front = (front + 1) % data.length;//本来是tail++;但是由于是循环数组,所以需要这样写
    	size --;
    	if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {//为了减少空间浪费,当size为getCapaCity() / 4时,进行缩容操作,且getCapaCity() / 2不能为零
			resize(getCapacity() / 2);
		}
    	return ret;
    }
    
    @Override
    public E getFront() {
    	if(isEmpty())
            throw new IllegalArgumentException("Queue is empty.");
        return data[front];
    }
    
    private void resize(int newCapacity){
    	
    	E[] newData = (E[]) new Object[newCapacity+1];//因为前面数组的length-1,所以这里要想容纳就必须要+1,这个和构造函数里的+1是一样的
    	for (int i = 0; i < size; i++) {
			newData[i] = data[(i + front) % data.length];//因为是循环数组,所以,新的data的队首对应的就是(i+front)的这样一个偏移位置,但是由于这是循环数组,(i+front)可能会越界,所以我们需要%data.length一下
		}
    	data = newData;
    	front = 0;
    	tail = size;
    }
    
    @Override
    public String toString(){

        StringBuilder res = new StringBuilder();
        res.append(String.format("Queue: size = %d , capacity = %d\n", size, getCapacity()));
        res.append("front [");
        for(int i = front ; i != tail ; i = (i + 1) % data.length){
            res.append(data[i]);
            if((i + 1) % data.length != tail)
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }
    
    public static void main(String[] args){

        LoopQueue<Integer> queue = new LoopQueue<>();
        for(int i = 0 ; i < 10 ; i ++){
            queue.enqueue(i);
            System.out.println(queue);

            if(i % 3 == 2){
                queue.dequeue();
                System.out.println(queue);
            }
        }
    }
}

 

着了设计的循环队列的出列和入列的操作的时间复杂度均为O(1)。

 

最后语:不积跬步,无以至千里;不积小流,无以成江海。数据结构其实是一个非常美妙的东西,只要你能领会其中的含义,你就可能会爱不释手,个人觉得代码还是要自己动手去写,这样才能领会其中真意!

参考:bobobo老师的玩转数据结构

版权声明:尊重博主原创文章,转载请注明出处 https://blog.csdn.net/zfy163520

城市运行管理的重要性与挑战 城市运行体系是以人为本的服务和经济发展体系,涉及决策、管理和执行三个层次。当前城市运行管理面临城市化快速发展、资源环境制约和社会矛盾突出等挑战。信息技术的发展为城市运行管理提供了重要手段,城市信息化经历了数字化、智能化到智慧化的发展过程。我国城市信息化虽取得进展,但仍处于初级阶段,存在缺乏整体规划、资源浪费和协作效率不高等问题。 智慧城市综合运行管理解决方案 智慧城市运行管理中心(SCOC)是支撑城市运行综合管理的神经中枢,旨在掌控城市运行综合体征,促进服务型政府转型。该中心通过全面整合运行资源,服务城市未来发展,提升城市运行水平和突发事件处置效率。中心纵向提升综合职能,横向贯通专业分工,包括综合管理平台、专业管理平台和业务操作平台,覆盖城市交通、公共安全、生态环境等多个领域。 智慧城市综合运行管理平台的结构与功能 智慧城市综合运行管理平台包括决策支持系统、处置系统、基础设施和监测系统。平台通过综合展现系统、综合应急指挥系统、综合运行业务联动系统等,实现城市运行的综合监测和管理。物联网数据采集系统利用网络通讯技术,实现城市物联网设备的高效运行。平台还包含云计算业务支撑系统、城市基础数据库、视频图像云平台等,以支持城市运行管理的各个方面。 智慧城市综合运行管理解决方案的优势 该解决方案具有三个核心优势:首先,它提供了完整的智慧城市视角,不仅仅是指挥中心或数据中心,而是智慧城市的实际载体。其次,它建立了完整的城市运行联动体系,打通业务部门壁垒,形成有机融合的业务联动平台,提升业务处理效率和服务水平。最后,方案凝聚了多年智慧城市建设咨询经验,为城市运行管理提供了成熟的解决方案。 项目实施建议 智慧城市运行管理中心的建设思路和项目实施建议是方案的重要组成部分,旨在指导城市如何有效实施智慧城市运行管理解决方案,以应对城市运行管理的挑战,提升城市管理的智能化和效率。通过这些建议,城市能够更好地规划和实施智慧城市项目,实现可持续发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值