大话数据结构 - 栈

代码GitHub地址


栈的分类

  • 栈是特殊的线性表
  • 栈的典型应用递归,四则运算表达式求值。

栈的分类有两种:

  • 静态栈(数组实现)
  • 动态栈(链表实现)

我们接下来会分别实现这两种栈。

栈的操作

数组方式

代码GitHub地址 - 欢迎star

栈节点

public class Node {

    private int value;

    public Node() {

    }

    public Node(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

由于是数组方式存储的,每个节点都能方便的存取它的前驱后继节点。所以我们声明的栈节点只需要存在一个存值的value属性即可。

public class Stacks {

    private static final int MAX_SIZE = 100;
    private static final int MIN_SIZE = -1;

    /**
     * 空栈默认 top = 1
     */
    private int top = -1;
    /**
     * 顺序栈默认最大 100
     */
    private Node[] nodes = new Node[MAX_SIZE];

    public Stacks() {

    }

    public Stacks(int top) {
        this.top = top;
    }

    public Stacks(Node[] nodes) {
        this.nodes = nodes;
    }

    public Stacks(int top, Node[] nodes) {
        this.top = top;
        this.nodes = nodes;
    }

}

由于是数组形式声明的栈,所以我们需要规定栈最大容量,并且设立top变量来定位栈顶元素。

入栈

/**
 * 入栈
 *
 * @param stacks
 * @param value
 * @return
 */
public static int push(Stacks stacks, int value) {
    Node node = new Node(value);
    if (stacks.top == MAX_SIZE) {
        return 0;
    }
    stacks.top++;
    stacks.nodes[stacks.top] = node;
    return 1;
}

很简单,只需要注意代码执行顺序即可,需要明白栈顶指针指向栈顶元素。每次入栈操作执行完top必然会++那么它又需要在入栈后指向最新插入的新元素,就必然在赋值前++,同时赋值的时候找数组的top索引处给就是最方便快捷的。

出栈

/**
 * 出栈
 *
 * @param stacks
 * @return
 */
public static int pop(Stacks stacks) {
    if (stacks.top == MIN_SIZE) {
        return 0;
    }
    stacks.nodes[stacks.top] = null; //
    stacks.top--;
    return 1;
}

和入栈操作相反,需要把栈的top索引处的元素置空,在top还在指向该节点的时候置空,然后top--即可。

遍历

/**
 * 遍历
 *
 * @param stacks
 */
public static void traverse(Stacks stacks) {
    int top = stacks.top;
    while (top > MIN_SIZE) {
        System.out.println(stacks.nodes[top--].getValue());
    }
}

根据top从顶到底,从最大值到0遍历即可。
需要注意的是:不要直接操作Stack.top变量,而是把他取出来换其他局部变量操作。否则在你遍历完之后,栈中就真的没有元素了。因为Stack.top变量已经被你指向了栈底。

判断是否为空

/**
 * 判断是否为空
 *
 * @param stacks
 * @return
 */
public static boolean isEmpty(Stacks stacks) {
    return stacks.top == MIN_SIZE;
}

清空栈

/**
 * 清空栈
 *
 * @param stacks
 */
public static void clean(Stacks stacks) {
    while (stacks.top > MIN_SIZE) {
        stacks.nodes[stacks.top] = null;
        stacks.top--;
    }
}

也可以循环调用出栈(pop)方法。通过while循环来判断top变量是否大于0来判断是否空栈。

共享栈

共享栈一般的使用场景是两个栈的空间有相反的需求关系的时候。也就是一个栈增长的时候另一个栈在缩短的情况。比如购买股票,有人买入就一定有人卖出,总量放在那里不变。不可能两面都有人一直买入,那么栈很快就会溢出了。所以在使用共享栈的时候考虑好使用场景是否符合。

public class SharedStack {

    private static final int MAX_SIZE = 100;
    private static final int MIN_SIZE = -1;

    public Node[] stackElement = new Node[MAX_SIZE];
    /**
     * 栈1的栈顶指针初始为-1
     */
    public int top_1 = MIN_SIZE;
    /**
     * 栈2的栈顶指针 初始为n
     */
    public int top_2 = MAX_SIZE;

    public SharedStack() {

    }
    
}

共享栈 - 入栈

/**
 * 入栈
 *
 * @param stack       栈
 * @param value       插入的元素值
 * @param stackNumber 栈序号,栈1,还是栈2
 * @return 成功与否,失败0,成功返回插入的值
 */
public static int push(SharedStack stack, int value, int stackNumber) {
    Node newNode = new Node(value);
    if (stack.top_1 + 1 == stack.top_2) {
        return 0;
    }
    if (stackNumber == 1) {
        // 栈1插入元素
        stack.stackElement[++stack.top_1] = newNode;
    } else if (stackNumber == 2) {
        // 栈2插入元素
        stack.top_2--;
        stack.stackElement[--stack.top_2] = newNode;
    }
    return value;
}

共享栈 - 出栈

/**
 * 出栈
 *
 * @param stack
 * @param stackNumber
 * @return 删除的栈顶元素的值
 */
public static int pop(SharedStack stack, int stackNumber) {
    int value = 0;
    if (stackNumber == 1) {
        if (stack.top_1 == MIN_SIZE) {
            return 0;
        }
        value = stack.stackElement[stack.top_1--].getValue();
    } else if (stackNumber == 2) {
        if (stack.top_2 == MAX_SIZE) {
            return 0;
        }
        value = stack.stackElement[stack.top_2++].getValue();
    }
    return value;
}

链表方式

代码GitHub地址 - 欢迎star

栈节点

public class Node {

    private int value;
    private Node nextNode;

    public Node() {
    }

    public Node(int value) {
        this.value = value;
    }

    public Node(Node nextNode) {
        this.nextNode = nextNode;
    }

    public Node(int value, Node nextNode) {
        this.value = value;
        this.nextNode = nextNode;
    }
    
    getter/setter

可以看出相比于数据方式,栈节点多了一个nextNode指针域。指向它的后继节点

public class Stacks {

    /**
     * 栈顶结点
     */
    private Node topElement;
    /**
     * 栈底节点
     */
    private Node bottmElement;

    public Stacks() {

    }

    public Stacks(Node topElement, Node bottmElement) {
        this.topElement = topElement;
        this.bottmElement = bottmElement;
    }

}

此时栈如果是空栈的话topElement = bottmElement

入栈

/**
 * 入栈
 *
 * @param stacks
 * @param value
 * @return
 */
public static int push(Stacks stacks, int value) {
    Node node = new Node(value);
    // 下一节点为根节点
    node.setNextNode(stacks.topElement);
    stacks.topElement = node;
    return 1;
}

需要记住的一个思路就是,新插入节点的后继节点是当前栈的栈顶元素。插入之后才能移动该栈顶元素的指针topElement

出栈

/**
 * 出栈
 *
 * @param stacks
 * @return
 */
public static int pop(Stacks stacks) {
    if (stacks.topElement == stacks.bottmElement) {
        return 0;
    }
    stacks.topElement = stacks.topElement.getNextNode();
    return 1;
}

栈顶指针指向其后继节点即可。

遍历

/**
 * 遍历
 *
 * @param stacks
 */
public static void traverse(Stacks stacks) {
    Node node = stacks.topElement;
    while (node != stacks.bottmElement) {
        System.out.println(node.getValue());
        node = node.getNextNode();
    }
}

换一个节点指向当前栈顶节点的节点即可。然后一直遍历输出到栈底节点为止。

判断是否为空

/**
 * 判断是否为空
 *
 * @param stacks
 * @return
 */
public static boolean isEmpty(Stacks stacks) {
    return stacks.topElement == stacks.bottmElement;
}

清空

/**
 * 清空
 *
 * @param stacks
 */
public static void clean(Stacks stacks) {
    stacks.bottmElement = null;
    stacks.topElement = stacks.bottmElement;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容