数据结构与算法_渡劫6_栈和队列

一、栈

1. 定义

栈(stack)是限定仅在表尾进行插入和删除操作的线性表。

  • 何时选择栈?

当某个数据集合只涉及在一段插入和删除数据,并且满足后进先出、先进后出的特性,首选栈

2. 栈实现

栈的主要操作有两个:

  • 入栈:栈顶插入一个数据

  • 出栈:栈顶删除一个数据

根据实现方式不同,分为:

  • 顺序栈:用数组实现的栈

  • 链式栈:用链表实现的栈

时间复杂度和空间复杂度均是O(1)

3. 栈的应用

最常见的是函数调用栈


int main() {

   int a = 1; 

   int ret = 0;

   int res = 0;

   ret = add(3, 5);

   res = a + ret;

   printf("%d", res);

   reuturn 0;

}



int add(int x, int y) {

   int sum = 0;

   sum = x + y;

   return sum;

}

4. 递归

  • 递归的秘诀:

将大问题分解为小问题,写出递推公式和终止条件。

  • 警惕:
  1. 不要用人脑去分解递归的每个调用关系,即每一个步骤

  2. 不要栈溢出

  3. 不要重复计算

5. 代码实现栈的操作123

  • 创建空栈

  • push(入栈)

  • pop(出栈)

  • top(取出栈最后压入的元素)


  • 顺序栈,python用表实现,表尾为栈顶:

# coding=utf-8
'''
@ Summary: python中的list可以满足栈的操作,但是多了栈所没有的操作
           缺乏安全性,因此把list作为类的内部,作为实现基础
@ Update:  

@ file:    3-1.栈的顺序表实现.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-7 下午6:17
'''

class SStack(object):
    def __init__(self):
        self._elem = []

    def is_empty(self):
        # 是否空栈
        return self._elem == []

    def top(self):
        # 取栈顶元素
        if self.is_empty():
            return None
        return self._elem[-1]

    def push(self, elem):
        # 入栈
        return self._elem.append(elem)

    def pop(self):
        # 出栈
        if self.is_empty():
            return None
        return self._elem.pop()

if __name__ == "__main__":
    st1 = SStack()
    st1.push(3)
    # print(st1.top())
    st1.push(5)
    while not st1.is_empty():
        print(st1.pop())

  • 链接表实现栈,表头为栈顶:

# coding=utf-8
'''
@ Summary: 用链表实现栈的相关操作
@ Update:  

@ file:    3-2.栈的链接表实现.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-7 下午6:29
'''

class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def __repr__(self):
        return str(self.val)

class LStack(object):
    @ staticmethod
    def c_link(vals):
        # create link
        if not vals:
            return None
        head = Node(len(vals))  # head node
        move = head  # move node
        for val in vals:
            tmp = Node(val)
            move.next = tmp
            move = tmp
        return head.next

    @ staticmethod
    def p_link(link):
        # print link
        if not link:
            return None
        while link:
            yield link.val
            link = link.next

    @ staticmethod
    def top(link):
        # 取出栈顶元素
        if not link:
            return None
        return link.val

    @ staticmethod
    def push(link, elem):
        # 入栈
        elem = Node(elem)
        elem.next = link
        return elem

    @ staticmethod
    def pop(link):
        # 出栈
        if not link:
            return None
        p = link
        link = link.next
        return p.val, link


if __name__ == "__main__":
    _list = list(range(1, 7))
    # link = None
    # for i in _list:
    #     link = LStack.push(link, i)
    # for node in LStack.p_link(link):
    #     print(node, end=" ")

    top = LStack.c_link(_list)
    for node in LStack.p_link(top):
        print(node, end=" ")
    print()
    # output: 1 2 3 4 5 6

    top = LStack.push(top, 10)
    for node in LStack.p_link(top):
        print(node, end=" ")
    print()
    # output: 10 1 2 3 4 5 6

    elem, top = LStack.pop(top)
    print(elem)
    for node in LStack.p_link(top):
        print(node, end=" ")
    print()

    elem = LStack.top(top)
    print(elem)


  • 代码练习1:二进制转十进制

# coding=utf-8
'''
@ Summary: 二进制转十进制,用栈实现
@ Update:  

@ file:    3-3.二进制转十进制.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-7 下午7:16
'''

class SStack(object):
    def __init__(self):
        self._elem = []

    def is_empty(self):
        # 是否空栈
        return self._elem == []

    def push(self, elem):
        # 入栈
        return self._elem.append(elem)

    def pop(self):
        # 出栈
        if self.is_empty():
            return None
        return self._elem.pop()

if __name__ == "__main__":
    _input = 1010110
    _list = list(map(int, str(_input)))

    st1 = SStack()
    for i in _list:
        st1.push(i)
    print(st1._elem)

    sum = count = 0
    while not st1.is_empty():
        # print(st1.pop(), end=" ")
        _pop = st1.pop()
        sum = sum + _pop * 2**count
        count += 1
    print(sum)

  • 代码练习2:二进制转八进制

# coding=utf-8
'''
@ Summary: 用栈实现二进制转八进制
@ Update:  

@ file:    3-3.二进制转八进制.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-7 下午10:05
'''

class SStack(object):
    def __init__(self):
        self._elem = []

    def is_empty(self):
        # 是否空栈
        return self._elem == []

    def push(self, elem):
        # 入栈
        return self._elem.append(elem)

    def pop(self):
        # 出栈
        if self.is_empty():
            return None
        return self._elem.pop()

if __name__ == "__main__":
    _input = 11001001  # 初始值
    _list = list(map(int, str(_input)))  # int to list
    while len(_list) % 3 != 0:  # 三的倍数,不够补0
        _list.insert(0, 0)

    _list.insert(0, 0)  # 为了后面的while 循环到栈底的时候继续运行,增加一个0
    st1 = SStack()  # 二进制的栈
    st2 = SStack()  # 八进制的栈
    for i in _list:  # 二进制digui入栈
        st1.push(i)
    print(st1._elem)

    # 二进制 三个弹出,计算,压入八进制栈
    tmp = []  # 储存三个二进制数
    count = 1  # 计数
    while not st1.is_empty():
        if count < 4:
            # 找到三个二进制数
            tmp.append(str(st1.pop()))
            count += 1
        else:
            num = "".join(tmp)
            # 二进制转八进制
            num = int(num[2]) * 2**2 + int(num[1]) * 2**1 + \
                  int(num[0]) * 2**0
            st2.push(num)  # 入栈
            count = 1
            tmp = []

    print(st2._elem)

  • 代码练习3:逆波兰表达式

# coding=utf-8
'''
@ Summary: 逆波兰表达式,又称后缀表达式
@ Update:  

@ file:    3-4.逆波兰表达式.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-8 下午3:29
'''

class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def __repr__(self):
        return str(self.val)

class LStack(object):
    def __init__(self):
        self.top = None

    def push(self, elem):
        # 入栈
        elem = Node(elem)
        elem.next = self.top
        self.top = elem

    def pop(self):
        # 出栈
        if not self.top:
            return None
        p = self.top
        self.top = self.top.next
        return p.val

    def p_link(self):
        if not self.top:
            return None
        while self.top:
            print(self.top.val, end=" ")
            self.top = self.top.next


def RPN(_list):
    # _int = [str(i) for i in range(20)]
    operators = "+-*/"

    st1 = LStack()
    for item in _list:
        try:
            item = int(item)
            st1.push(item)
        except ValueError:
            if item in operators:
                x2 = st1.pop()
                x1 = st1.pop()
                if item == "+":
                    st1.push(x1 + x2)
                elif item == "-":
                    st1.push(x1 - x2)
                elif item == "*":
                    st1.push(x1 * x2)
                elif item == "/":
                    st1.push(x1 / x2)

    return st1.top

if __name__ == "__main__":
    # _input = "1 2 - 4 5 + *"  # -9
    # _input = "5 6 7 + 8 * - 9 4 / +"  # -96.75
    _input = "3 5 - 6 17 4 * + * 3 /"
    _list = _input.split(" ")  # str to list

    print(RPN(_list))

---



- 代码练习4: 中缀表达式转后缀表达式

```python

# coding=utf-8
'''
@ Summary: 中缀表达式转换为后缀表达式
@ Update:  

@ file:    3-6.逆波兰进阶.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-8 下午4:43
'''
class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def __repr__(self):
        return str(self.val)

class LStack(object):
    def __init__(self):
        # 初始化 top node
        self._top = None

    def push(self, elem):
        # 入栈 self._top 始终指向first node
        elem = Node(elem)
        elem.next = self._top
        self._top = elem

    def pop(self):
        # 出栈 self._pop指向第二个节点, 返回第一个节点的值
        if not self._top:
            return None
        p = self._top
        self._top = self._top.next
        return p.val


def mid_2_after(l):
    # 中缀转后缀

    # 优先级
    ops_relu = {"+":1, "-":1,
                "*":2, "/":2,
                "(":1, ")":1}

    stack = LStack()  # 储存运算符和"()"的栈
    lt = []  # 输出列表

    for item in l:
        # 遇到数字添加进列表中,非数字进行栈的相关操作
        try:
            item = int(item)
            lt.append(item)
        except ValueError:
            if item != ")":
                # 优先级顺序,栈顶的优先级高则弹出栈中所有元素
                if stack._top and ops_relu[item] < ops_relu[stack._top.val]:
                    while stack._top:
                        lt.append(stack.pop())
                stack.push(item)  # 入栈
            else:
                # 遇到")", 弹出栈中的元素, 直到"("为止
                while stack._top.val != "(":
                    lt.append(stack.pop())
                stack.pop()  # delete "("

    # 弹出栈中所有元素
    while stack._top:
        lt.append(stack.pop())

    return lt


if __name__ == "__main__":
    _input = "1 + ( 2 - 3 ) * 4 + 10 / 5"  # "1 2 3 - 4 * + 10 5 / +"
    _list = _input.split(" ")  # str to list
    print("The origin list is: {}.".format(_list))

    print(mid_2_after(_list))

  • 代码练习4:斐波那契

最常见的栈:函数调用


# coding=utf-8
'''
@ Summary: 斐波那契
@ Update:  

@ file:    3-7.斐波那契.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-8 下午10:18
'''

def fib(n):
    # 递归
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

def fib2(n):
    # 生成器
    if n < 0:
        return None
    a, b = 0, 1
    for _ in range(n):
        yield b
        a, b = b, b + a


if __name__ == "__main__":
    for i in fib2(5):
        print(i)

  • 代码练习5:字符串反转

# coding=utf-8
'''
@ Summary: 递归实现字符串翻转
@ Update:  

@ file:    3-8.字符串翻转.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-9 上午9:05
'''

def reverse(s):
    if len(s) <= 1:
        return s
    elif len(s) == 2:
        return s[1] + s[0]
    return s[-1] + reverse(s[:-1])

if __name__ == "__main__":
    s = "ABC"
    print(reverse(s))

  • 代码练习6:汉诺塔

# coding=utf-8
'''
@ Summary: 递归实现汉诺塔
@ Update:  

@ file:    3-9.汉诺塔.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-9 上午9:19
'''
def hanoi(n, x, y, z):
    if n == 1:
        print(x + " --> " + z)
    else:
        # 将n-1个从X移动到Y上,借助Z
        hanoi(n-1, x, z, y)
        # 将第n个从X移动到Z上
        print(x + " --> " + z)
        # 将n-1个从Y移动到Z上,借助X
        hanoi(n-1, y, x, z)

if __name__ == "__main__":
    hanoi(4, "X", "Y", "Z")

  • 代码练习7:八皇后问题

  • 代码练习8:括号匹配



# coding=utf-8
'''
@ Summary: 
@ Update:  

@ file:    括号匹配.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    10/15/19 9:55 PM
'''

def is_valid(s):
    if not s:
        return None
    parent = "()[]{}"
    opposite = {")":"(", "]":"[", "}":"{"}
    st = []  # 栈
      for item in s:
        if item in parent:
            st.append(item)
        if item in opposite:
            del_elem = st.pop()
            if not st or opposite[del_elem] != st.pop():
                return False
    return not st

if __name__ == "__main__":
    s = "9(){}"
    if is_valid(s):
        print("{} is OK.".format(s))
    else:
        print("{} is not OK.".format(s))

二、队列

1. 定义

先进先出,比如排队买票

2. 入队、出队

  • 链式队列(链表表头为队头,表尾为队尾)

# coding=utf-8
'''
@ Summary: 链表实现队列,任意输入一串字符 # 结束
@ Update:  
@ file:    队列.py
@ version: 1.0.0
@ Author:  Lebhoryi@gmail.com
@ Date:    10/17/19 4:21 PM
'''
class Node(object):
    def __init__(self, val):
        self.val = val

        self.next = None


    def __repr__(self):
        return str(self.val)


class LQueue(object):
    def __init__(self):
        self.front = Node(0)
        self.rear = Node(0)


    def is_empty(self):
        # 判断是否为空
        return self.front.val == self.rear.val


    def enqueue(self, elem):
        # 入队
        elem = Node(elem)
        if self.is_empty():
            self.front.next = elem
        self.rear.next = elem
        self.rear = elem


    def dequeue(self):
        # 出队
        if self.is_empty():
            return None
        del_node = self.front.next.val
        if self.front.next == self.rear:
            # 当只有一个元素的时候
            self.front = self.rear
        else:
            self.front.next = self.front.next.next
        return del_node


    def get_head(self):
        # 查看队头元素
        if self.is_empty():
            return None
        return self.front.next


    def p_queue(self):
        # print queuep
        if self.is_empty():
            return None
        tmp = self.front.next
        while tmp:
            print(tmp, end=" ")
            tmp = tmp.next


if __name__ == "__main__":
    # 创建队列
    lq = LQueue()
    data = input("请输入(#结束):")
    while data != "#":
        lq.enqueue(data)
        data = input("请输入(#结束):")
    lq.p_queue()  # 最初的链式列表

  • 顺序队列的出队和入队

# coding=utf-8
'''
@ Summary: 顺序队列
@ Update:  
@ file:    队列2.py
@ version: 1.0.0
@ Author:  Lebhoryi@gmail.com
@ Date:    10/18/19 12:19 AM
'''
class SQueue(object):
    def __init__(self):
        self._queue = []


    def is_empty(self):
        return self._queue == []


    def en_queue(self, elem):
        self._queue.insert(0, elem)
        # self._queue.append(elem)


    def de_queue(self):
        if self.is_empty():
            return None
        del_node = self._queue.pop()
        # del_node = self._queue.pop(0)
        return del_node


    def p_queue(self):
        if self.is_empty():
            return None
        return self._queue


if __name__ == "__main__":
    sq = SQueue()
    data = input("请输入(输入# 结束):")
    while data != "#":
        sq.en_queue(data)
        data = input("请输入(输入# 结束):")
    print(sq.p_queue())
    sq.de_queue()
    print(sq.p_queue())


队列跟栈一样,也是一种操作受限的线性表数据结构。

3. 循环队列


# coding=utf-8
'''
@ Summary: 顺序表实现循环队列
@ Update:  
@ file:    循环队列.py
@ version: 1.0.0
@ Author:  Lebhoryi@gmail.com
@ Date:    10/18/19 10:24 AM
'''

class SQueue(object):
    def __init__(self, maxsize):
        self.maxsize = maxsize  # 自定义队列长度
        self._queue = [None] * maxsize
        self.front = 0  # 指向队头
        self.rear = 0  # 指向队尾元素的下一个位置


    def is_empty(self):
        # 判断是否为空
        return self.front == self.rear


    def q_length(self):
        # 当前队列长度计算 (rear-front+maxsize)%maxsize
        return (self.rear - self.front + self.maxsize) % self.maxsize


    def enqueue(self, elem):
        # 队尾入队 elem push
        if (self.rear + 1) % self.maxsize == self.front:
        # 判断队列是否填满 (rear+1)%maxsize == front
            print("The queue is full!")
        self._queue[self.rear] = elem
        self.rear = (self.rear + 1) % self.maxsize


    def dequeue(self):
        # 队头出队 elem pull
        if self.is_empty():
            return None
        del_elem = self._queue[self.front]
        self._queue[self.front] = None
        self.front = (self.front + 1) % self.maxsize
        return del_elem


    def p_queue(self):
        # print queue
        if self.is_empty():
            return None
        for i in range(self.maxsize):
            print(self._queue[i], end=" ")
        print()



if __name__ == "__main__":
    s = "abcdde"
    sq = SQueue(15)  # 循环队列
    for item in range(10):
        # 入队
        sq.enqueue(item)
    sq.p_queue()  # 原始队列
    for i in range(5):  # 删除队头的5个元素:0~4
        sq.dequeue()
    sq.p_queue()
    for i in range(8):  # 从队尾增加8个元素:0~7
        sq.enqueue(i)
    sq.p_queue()

4.代码练习:

  • 用两个栈实现队列 (剑指offer和力扣)

# coding=utf-8
'''
@ Summary: 剑指Offer-用两个栈来实现一个队列,完成队列的Push和Pop操作
@ Update:  (先进先出)

@ file:    3-15.2栈实现队列.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-21 下午10:00
'''
import pysnooper

class SStack(object):

    def __init__(self):
        self.stackA = []
        self.stackB = []

    def is_empty(self):
        # 判断satackA是否为空
        return self.stackA == []

    def push(self, elem):
        # stackA入栈
        self.stackA.append(elem)

    @pysnooper.snoop(watch=("self.stackA", "self.stackB"))
    def s_pop(self):
        # stackA 出栈 stackB 入栈
        if self.stackB:
            return self.stackB.pop()
        while self.stackA:
            self.stackB.append(self.stackA.pop())
        return self.stackB.pop()


if __name__ == "__main__":
    ss = SStack()
    for i in range(7):
        ss.push(i)
    print(ss.s_pop())
    ss.push(8)
    print(ss.s_pop())

  • 用两个队列实现栈(力扣)

# coding=utf-8
'''
@ Summary: 进栈:元素入队列A(后进先出)
           出栈:判断如果队列A只有一个元素,则直接出队。否则,把队A中的元素出队并入队B,
           直到队A中只有一个元素,再直接出队。为了下一次继续操作,互换队A和队B。
@ Update:

@ file:    3-16.2队列实现栈.py
@ version: 1.0.0

@ Author:  Lebhoryi@gmail.com
@ Date:    19-10-22 下午7:12
'''
class SQueue(object):
    def __init__(self):
        self.queueA = []
        self.queueB = []

    def is_empty(self):
        return self.queueA == [] and self.queueB == []

    def enqueue(self, elem):
        # 入队
        self.queueA.append(elem)

    def dequeue(self):
        #出队
        if self.is_empty():
            return None
        # if len(self.queueA) == 1:
        #     return self.queueA.pop()
        while len(self.queueA) > 1:
            self.queueB.append(self.queueA.pop(0))
        self.queueA, self.queueB = self.queueB, self.queueA
        return self.queueB.pop()


if __name__ == "__main__":
    sq = SQueue()
    for i in range(1, 7):
        sq.enqueue(i)
    print(sq.dequeue())
    sq.enqueue(3)
    print(sq.dequeue())
    print(sq.dequeue())

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值