数据结构和算法-Java栈

JAVA在程序运行时,在内存中划分5片空间进行数据的存储。分别是:1:寄存器。2:本地方法区。3:方法区。4:栈。5:堆。因此,栈对数据的存储是在java运行时在内存中进行的,当程序退出或者执行完毕,它们就会销毁。因此通常只是作为一种程序构思数据结构的辅助工具,后面要讲到的队列也是如此。

栈的定义和特性

一、概念:

栈是一种只允许在一端进行插入或删除的线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。栈的操作端通常被称为栈顶,另一端被称为栈底。栈的插入操作称为进栈(压栈|push);栈删除操作称为出栈(弹栈|pop)。

二、特性:

栈就像一个容器,我们只能从容器口放和取,因而按照后进先出(LIFO, Last In First Out)的原理运作。栈也称为后进先出表。

三、存储结构(物理结构)

1.顺序存储的栈称为顺序栈,结构如图1;

              图[1]          

 

2.链式存储的栈称为链式栈,结构如图2。

      图[2]  

 

Java栈实现

1.顺序栈的实现:

/**
 * 
 * 顺序栈(SqeStack)
 */
public class SqeStack<T>{
    private T data[];//用数组表示栈元素
    private int maxSize;//栈空间大小(常量)
    private int top;//栈顶指针(指向栈顶元素)
    
    public SqStack(int maxSize){
        this.maxSize = maxSize;
        this.data =  (T[])new Object[maxSize];//需要使用Object来创建
        this.top = -1;//初始化栈顶指针位置
    }

    //判断栈是否为空
    public boolean isNull(){
        return this.top == -1 ? true : false;
    }
    
    //判断是否栈满
    public boolean isFull(){
        return this.top == this.maxSize-1 ?true:false;
    }


    //获取栈顶元素
    public T peek(){
        if(isNull()){
            return null;
        }
       
        return data[top-1];
    }
    
    //压栈
    public boolean push(T vaule){
        if(isFull()){ //栈满
            return false;
        }else{
            data[++top] = vaule;//栈顶指针加1并赋值
            return true;
        }
    }

    //弹栈
    public T pop(){
        if(isNull()){
            //栈为空
            return null;
        }else{
            T value = data[top];//取出栈顶元素
            --top;//栈顶指针-1
            return value;
        }
    }
    
}

这个栈是用数组实现的,内部定义了一个数组,一个表示最大容量的值以及一个指向栈顶元素的top变量。构造方法根据参数规定的容量创建一个新栈,push()方法是向栈中压入元素,指向栈顶的变量top加一,使它指向原顶端数据项上面的一个位置,并在这个位置上存储一个数据。pop()方法返回top变量指向的元素,然后将top变量减一,便移除了数据项。要知道 top 变量指向的始终是栈顶的元素。

以上我们虽然现实了一个用数组连续存储顺序栈,但是我们发现一个局限,就是栈需要初始化容量,那么能不能不初始化容量呢?

2.链表栈的实现

针对以上问题,我们可以用链表的存储方式来解决,看下面代码:

/*
 * 链表元素
 */

class  Node<E>{
    Node<T>next=null;
    
    T data;
    
    public Node(T data){
        this.data=data;
    }
}

/*
 * 用链表来实现栈
 */
public class LinkedStack<T> {

    Node<T>top=null;    //头结点设置为空

    //判断是否为空
    public boolean isEmpty(){
        return top==null;
    }

    //入栈
    public void push(T data){
        Node<T>newNode=new Node<T>(data);
        newNode.next=top;//将现在栈顶的元素的值放在第二个
        top=newNode;    //新的栈顶元素的值放在top中
    }

   
    //出栈
    public T pop(){
        if(this.isEmpty()){
            return null;
        }

        T data= top.data;
        top=top.next;
        return data;
    }

    public T peek(){
        if(this.isEmpty()){
            return null;
        }
        T data=top.data;
        return data;
    }

}

以上的代码是用链表的形式实现的一个链表栈,比较难懂的在于设计链表的节点元素,大家可以阅读理解下。

栈的应用实例

栈结构是很基本的一种数据结构,所以栈的应用也很常见,根据栈结构“先进后出”的特点,我们可以在很多场景中使用栈,下面我们就是使用上面我们已经实现的栈进行一些常见的应用:十进制转N进制、行编辑器、校验括号是否匹配、中缀表达式转后缀表达式、表达式求值等。

1.十进制转16进制实现

class StackUtil{

    private static SqeStack<?> stack;

    //弹栈出所有元素
    public static Object[] popAll(SqeStack<?> s){
        stack = s;
        if(stack.isNull()){
            return null;
        }else{
            Object[] array = new Object[stack.peek()+1];
            int i = 0;
            while(!stack.isNull()){
                array[i]=stack.pop();
                i++;
            }
            return array;
        }
    }
    
    //转换 hex进制
    public String int2hex(Integer num,int hex){
         
       if(hex<=0||hex>36){
            return "无效的进制";
        }

        if(num == 0){
            return "0"
        } else if(num>0){//正数
           
             return "-".convert(num,hex);
        }else{//负数
            num = -num;//先去负号
            
            return "-".convert(num,hex);
        }
    }
    
    //转换进制
    public static String convert(Integer num,int hex){

         SqeStack<Integer> stack = new SqeStack<Integer>(hex);
            int tmp = num;
            while(num!=0){
                num = num / hex ;
                int mod = tmp % hex;//取余压栈
                stack.push(mod);
                tmp = num;
            }
            Object[] o = popAll(stack);//弹栈取出余数
            StringBuilder sb = new StringBuilder();
            for(Object i : o){
                int in = (int)i;
                //取出的数字如果>=10需要用字母代替
                if(in>=10){
                    char c = (char) ('A'+in-10);
                    sb.append(c);
                }else{
                    sb.append(i);
                }
            }
            return sb.toString();
       }


}

2.校验符合是否匹配

public class StackUtils{
    
    //表达式括号是否匹配()[]{}
    public static boolean isMatch(String str) {  
        SqeStack<Character> stack = new SqeStack<Character>(str.length()+1);  
        char[] arr = str.toCharArray(); 
        for (char c : arr) {  
            //遇到左括号进栈
            if(c=='('||c=='{'){
                stack.push(c);
            }
            //遇到右括号匹配栈顶符号
            else if(c==')'){
                if(stack.isNull()){
                    return false;//栈为空,匹配失败
                }else if(stack.pop()=='('){
                    continue;//匹配成功继续下一次循环
                }else{
                    return false;//匹配不成功代表该表达式不符合规则
                }
             } else if(c=='}'){
                if(stack.isNull()){
                    return false;//栈为空,匹配失败
                }else if(stack.pop()=='{'){
                    continue;//匹配成功继续下一次循环
                }else{
                    return false;//匹配不成功代表该表达式不符合规则
                }
            }
        }
        //如果最后没有右括号但是还存在左括号表示不匹配
        return stack.isNull();  
    }  

栈总结

栈通过提供限制性的访问方法push()和pop(),使得程序不容易出错。根据栈的先入后出特性,我们做了匹配字符和进制转换的例子,从这里可以看出,栈不是真正用来存储数据的,只是用来辅助完成一些特定的工作。

我们稍微分析也可以知道,栈的工作就是push和pop,每一步完成即销毁,因此他的时间复杂度是O(1),而且他并不受栈中元素个数的影响,因此栈的运行速度是非常快的,这也是和Java虚拟机的内存分配是相吻合的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值