目录:

前言

1:栈

  1.1:栈的实现

  1.2:栈的应用:

    1.2.1:检验数学表达式的括号匹配

    1.2.2:将十进制数转化为任意进制

    1.2.3:后置表达式的生成及其计算

2:队列

  2.1:队列的实现

  2.2:队列的应用之囚徒问题

3:双端队列

  3.1:双端队列的实现

  3.2:双端队列的应用之回文检测

4:列表

  3.1:链表的实现

前言

  线性数据结构有四种:栈(stack),队列(queue),双端队列(deque),列表(list)

  线性数据结构就是一群数据的集合,数据的位置和其加入的先后顺序有关,并且由此将其看为有两端,头(top)与尾(rear)【其中认为头是最先加入的数据,尾是最后加入的数据】

一:栈(stack)

1:栈的构造

  栈可以看成是堆盘子,只能从上端加入以及从上端取出,也就是只能从一端对其进行操作。对应到线形结构就是只能在尾部对其进行加入与删除的数据结构。简称LIFO(last in first out),即是后进先出。

  习惯上我们把栈尾部叫做栈顶(TOP),加入数据叫做压栈(push),删除叫做出栈(pop)

  要注意一点的是,此处的栈顶其实是list的队尾,理论上将两个top对应起来也是简单的,但是前面讲过list的insert(0,i)与pop(0)都是O(n)的操作,而末尾的append(i)与pop(i)都是O(1),故依旧选择后者。

 class Stack:
def __init__(self,*args):
self.cstack = [] #c:construct
for i in args:
self.cstack.append(i)
def push(self,x): #压栈
self.cstack.append(x)
def size(self): #查看元素个数
return len(self.cstack)
def pop(self): #出栈
if self.size()>0:
return self.cstack.pop()
else:
print('Sorry, this is an empty stack')
def peek(self): #检查栈顶元素是什么
if self.size()>0:
return self.cstack[len(self.cstack)-1]
else:
return 'empty stack'

栈的实现

    由上我们可以看出,其操作都是基于list的,结合昨天的表可以知道:

Stack = Stack(1,2,3,4,5…,n)            O(n)

Push,pop,size,peek                         O(1)

  

  2:栈的应用:

    1]: 检测数学表达式的括号是否配对

      遍历输入的表达式,遇到(后压入栈,遇到)后将栈顶(出栈,若最后为空栈则是匹配成功的,其他 [ ]与 { }同理

 from stackType_01 import Stack

 while True:
stack = Stack()
str = input('input the arithmetic expression:')
if len(str) == 0:
print('input something')
else:
for i in str:
if i in '({[': #!!!
stack.push(i)
elif i in ')}]':
stack.pop()
if stack.size()==0:
print('It is a balanced arithmetic expression')
else:
print('not a balanced arithmatic expression')

检查数学表达式括号配对

    

    2]:将一个十进制的数转化为任意进制的数

原理:首先,n进制就是从0开始到n-1,若n>9则从A开始分别表示10……

          其次,假设输入的十进制是n,想要转化为x进制,此时首先对n除以x取余,将余数放入栈,而后将n整除x后的数作为下一个n重复上面两个步骤直到n整除值为零,最后栈里面的数的逆序就是结果了:

        e.g:十进制转二进制

          

 from stackType_01 import Stack
deci = int(input('input the number:'))
base = int(input('input the system you want to transfer to:')) def trans(deci,base):
flag = '0123456789ABCDEF'
stack = Stack()
while deci//base > 0:
stack.push(flag[deci%base])
deci //= base
stack.push(flag[deci])
print(stack.cstack) trans(deci,base)

十进制转化为任意进制

    

    3]:后置,前置,中置表达式

          

      即是把运算符放到最近的括号外(前置放括号前面,后置放后面)。

    将普通表达式转化为后置表达式的原理:创建一个list以及一个stack,遇到’(,+-*/’ 都压入栈,遇到数字放入list,遇到‘)‘就把栈里面直到最近的一个’(‘中的运算符pop入list,如下图所示:

             

      遇到后置表达式的运算原理:创建一个栈,开始遍历后置表达式,如果是数字就压入栈,如果是运算符就出栈两个数进行对应的运算后得到一个数并将其压入栈。

      #python提供的eval函数能够对string型运算表达式进行运算并返回值

          

 # -*- coding:utf-8 -*-
# from stackType_01 import Stack
import stackType_01
def ToPostfix(expression):
L = []
stack = stackType_01.Stack()
for i in expression:
if i in '(+-*/':
stack.push(i)
elif i == ')':
while stack.peek() != '(':
L.append(stack.pop())
stack.pop()
else:
L.append(i)
print('postfix:',''.join(L))
return L def calpostfix(stt):
stack = stackType_01.Stack()
for i in stt:
if i in '+-*/':
pop1 = stack.pop()
pop2 = stack.pop()
temp = pop2+i+pop1
stack.push(str(eval(temp)))
else:
stack.push(i)
print('calresult:',stack.peek()) expression = list(input('plz input the expression with balanced parentheses:'))
calpostfix(ToPostfix(expression))

转化为后置表达式以及后置表达式的运算

二:队列(queue)

1:队列的实现

  区别于栈的只能够从一边进行出,队列是一端进,一段出,就像有一截水管,从一端先进的从另一端先出,这叫FIFO(first in first out),即是先进先出。

    习惯上我们把加入数据叫做入队(enqueue),删除数据叫离队(dequeue)。

   由于还是用list来实现queue,故说明下队首与list位置的关系,为了模拟左进右出的管道,我们把list尾部实现为queue的对首,即是即将删除的数据。

 class Queue:                    # right is the front
def __init__(self,*args):
self.item = []
for i in args:
self.item.insert(0,i) def size(self):
return len(self.item) def is_empty(self):
if self.size() == 0:
return True
else:
return False
def enqueue(self,*arg):
for i in arg:
self.item.insert(0,i) def dequeue(self):
if self.size() > 0:
return self.item.pop()
else:
print('empty queue, cannot dequeue anymore') # que = Queue(1,2,3)
# que.enqueue(4,5)
# print(que.item)
# print(que.dequeue())
# print(que.dequeue())
# print(que.size())

队列的实现

    显然,dequeue方法为O(n),enqueue方法为O(1)

  

  2:队列的应用

    囚徒问题:n个犯人围城圈,从头开始数一个确定的数,到达这个数后此人出圈,他下个人继续从1开始数,直到最后只剩下一个人,用程序模拟此过程:

    实现原理:用队列实现一个圆圈的模拟,从队首开始,没经过了它就把计数器flag加一而后把此元素删除并且放到队尾,直到遇到flag为判别数的人,将此人删掉,flag置一后又开始直到只剩下一个人

 from Queue_05 import Queue
names = input('plz input the names(with blank betwwwn them):')
juge = int(input('plz input the judge number:'))
flag = 1
queue = Queue()
for i in names.split(" "):
queue.enqueue(i) while queue.size() != 1:
if flag != juge:
queue.enqueue(queue.dequeue())
flag+=1
else:
queue.dequeue()
flag=1 print(queue.item)

囚徒问题

三:双端队列(deque)

1:双端队列的实现

区别于队列的一端进一端出,双端队列是在两端都可以进出的数据结构。

我们把list右端作为其前端,且分别把队头,队尾的增删操作方法为add_front / rear,remove_front / rear

 class Deque:                    # right is the front
def __init__(self,*args):
self.item = []
for i in args:
self.item.insert(0,i) def __len__(self):
return len(self.item)
def size(self):
return len(self.item) def is_empty(self):
if self.size() == 0:
return True
else:
return False
def add_front(self,*arg):
for i in arg:
self.item.append(i) def add_rear(self,*arg):
for i in arg:
self.item.insert(0,i) def remove_front(self):
if self.size() > 0:
return self.item.pop()
else:
print('empty deque, cannot dequeue anymore') def remove_rear(self):
if self.size() > 0:
return self.item.pop(0)
else:
print('empty deque, cannot dequeue anymore')

双端队列的实现

     注意,此地增删可同时n个数,front端的增删为O(n),rear端的为O(n^2)

  2:应用

     回文检验:原理,从队列两端分别删除元素来进行比较,直到剩下不多于一个数且都全部匹配则为回文

 from Deque_07 import Deque
flag = 1
expression = input('plz input the string:')
deque = Deque()
for i in expression:
deque.add_rear(i) while deque.size() != 1 or 0:
a = deque.remove_front()
b = deque.remove_rear()
if a!=b:
flag = 0
break if flag:
print('yes, it is huiwen')
else:
print('nout a huiwen string')

回文检测

四:列表(list)

原本的list,python是有的,不过在这里我们要实现一个基于相对位置有序的列表,也就是C语言中我们所说的链表(linked list)

设计思路:拆分出来看,每个节点(node)都是一个值和一个指向下个节点的变量构成的,总体上来看,有first与last两个变量分别指向第一个节点和最后一个节点。

 class LinkedList:

     class __Node:
def __init__(self, item, next = None):
self.item = item
self.next = next def getItem(self):
return self.item def setItem(self, item):
self.item = item def setNext(self,next):
self.next = next def getNext(self):
return self.next def __init__(self,contents=[]):
self.first = LinkedList.__Node(None,None)
self.last = self.first
self.numItems = 0
for i,e in enumerate(contents):
if i == 1:
self.first = self.first.getNext()
self.append(e) def append(self,item):
node = LinkedList.__Node(item)
self.last.setNext(node)
self.last = node
self.numItems += 1 def __getitem__(self,index):
if index >= 0 and index < self.numItems:
cursor = self.first.getNext()
for i in range(index):
cursor = cursor.getNext()
return cursor.getItem() raise IndexError("LinkedList index out of range") def __setitem__(self,index,val):
if index >= 0 and index < self.numItems:
cursor = self.first.getNext()
for i in range(index):
cursor = cursor.getNext()
cursor.setItem(val)
return raise IndexError("LinkedList assignment index out of range") def __add__(self,other):
if type(self) != type(other):
raise TypeError("Concatenate undefined for " + str(type(self)) + " + " + str(type(other)))
result = LinkedList()
cursor = self.first.getNext()
while cursor != None:
result.append(cursor.getItem())
cursor = cursor.getNext() cursor = other.first.getNext() while cursor != None:
result.append(cursor.getItem())
cursor = cursor.getNext()
return result def insert(self,index,item):
cursor = self.first
if index < self.numItems:
for i in range(index):
cursor = cursor.getNext() node = LinkedList.__Node(item, cursor.getNext())
cursor.setNext(node)
self.numItems += 1
else:
self.append(item) linkedlist = LinkedList([1,2,3,4,5])
linkedlist2 = LinkedList([9,8,7,6])
print(linkedlist.first.item) print(linkedlist[2])
linkedlist.append('append')
linkedlist.insert(2,'insert')
linkedlist += linkedlist2
for i in linkedlist:
print(i)

链表的实现

  其实这个程序有点小问题不知你发现没有,我也是刚发现的,其实就是first指标其实一直指向了一个空节点。通过实例.getnext()才能够获得真正的第一个节点。

将(*)部分改成如下即可:

 for i,e in enumerate(contents):
if i == 1:
self.first = self.first.getNext()
self.append(e)

  需要注意的是linked list的性质就和list完全不一样了,我们回顾一下,list的访问元素时间都是O(1),中间插入元素为O(k)[k为插入位置后面有的元素数],而linked元素的位置由于是相对的,访问元素需要从first开始依次查找,故时间为O(k)[k为寻找元素前面的元素个数],插入元素则比较简单,改变下两个next指向就行了,所以为O(1),好吧,不开玩笑了,其实不是这样的,插入元素前需要先从头开始找到插入元素前的节点,而此为O(k),是更高阶,所有还是为O(k),好吧,其实上面两个都对,o(1)情况是若linked list无序时,直接加到最后,O(k)是若是有序的,则需要从头开始比较比它小的k个数后再放入。

python 下的数据结构与算法---4:线形数据结构,栈,队列,双端队列,列表的更多相关文章

  1. 《算法实战策略》-chaper19-队列、栈和双端队列

    对于计算机专业的学生来说,他们一定会很熟悉一句话:程序设计 = 算法 + 数据结构.而根据笔者的理解,所谓程序设计其实就是为了编程解决实际问题,所谓算法是一种解决问题某种思维的方法,但是思维需要得到编 ...

  2. 用python实现栈/队列/双端队列/链表

    栈是元素的有序集合,添加操作与移除操作都发生在其顶端,先进后出栈操作:创建空栈,增删(顶端),查(顶端元素,元素个数,是否为空)应用:将十进制数转换成任意进制数 class Stack: # 用列表创 ...

  3. 《Java数据结构与算法》笔记-CH5-链表-3双端链表

    /** * 双端链表的实现 */ class LinkA { public long dData; public LinkA next; public LinkA(long d) { dData = ...

  4. 用Python实现的数据结构与算法:双端队列

    一.概述 双端队列(deque,全名double-ended queue)是一种具有队列和栈性质的线性数据结构.双端队列也拥有两端:队首(front).队尾(rear),但与队列不同的是,插入操作在两 ...

  5. Python实现的数据结构与算法之双端队列详解

    一.概述 双端队列(deque,全名double-ended queue)是一种具有队列和栈性质的线性数据结构.双端队列也拥有两端:队首(front).队尾(rear),但与队列不同的是,插入操作在两 ...

  6. 重读《学习JavaScript数据结构与算法-第三版》- 第5章 队列

    定场诗 马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁! 前言 本章为重读<学习JavaScript数据结构与算法-第三版>的系列文章,主要讲述队列数据 ...

  7. python基础教程_学习笔记19:标准库:一些最爱——集合、堆和双端队列

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/signjing/article/details/36201499 标准库:一些最爱 集合.堆和双端队 ...

  8. JavaScript 数据结构与算法2(队列和双端队列)

    学习数据结构的 git 代码地址: https://gitee.com/zhangning187/js-data-structure-study 1.队列和双端队列 队列和栈非常类似,但是使用了与 后 ...

  9. 自己动手实现java数据结构(四)双端队列

    1.双端队列介绍 在介绍双端队列之前,我们需要先介绍队列的概念.和栈相对应,在许多算法设计中,需要一种"先进先出(First Input First Output)"的数据结构,因 ...

随机推荐

  1. sqlserver常用全局变量

    @@SERVERNAME    : 返回运行SQL Server 2000本地服务器的名称. @@REMSERVER       : 返回登录记录中记载的远程SQL Server服务器的名称. @@C ...

  2. 流Stream个人学习理解

    1.Stream类 命名空间:System.IO 程序集:mscorlib 流是对字节序列的抽象,提供字节序列的一般视图. 流的操作包括三个方面: 1.读取(Read):将流数据传入到数据结构 2.写 ...

  3. vs code(egret wing) php配置与调试

    所需插件 下面是便于编写以及调试php的插件,可以从IDE Store中搜索. PHP Debug,PHP IntelliSense,PHP IntelliSence Cranne. 环境配置 找到项 ...

  4. phpstorm 2016.1注册码

    phper 享受生产PHP Web开发phpStorm.利用深代码理解,一流的编码的援助,并支持所有主要的工具和框架. 先看看 phpstorm 2016.1 带来那些新变化呢? 1,更好的PHP语言 ...

  5. JS函数与call()apply()详解

    JavaScript中的每个函数都是一个对象. 因为函数都是对象,它们有自己的属性和方法.我们可以把它们看作数据(data). 函数和方法的区别? 函数立足于它们自己(例如:alert()), 而方法 ...

  6. 全排列算法之Perm算法实现

    题目描述:   给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列.   我们假设对于小写字母有'a' < 'b' < … < 'y' < 'z',而且给定的字符 ...

  7. Entity Framework with MySQL 学习笔记一(查询)

    参考 : http://msdn.microsoft.com/en-us/data/jj574232.aspx EF 查询基本上有3中 默认是 Lazy Loading 特色是只有在需要数据的时候EF ...

  8. Linux服务器间信任关系建立方法

    http://blog.csdn.net/jiangzeyun/article/details/42489359

  9. 模糊语意变数、规则和模糊运算--AForge.NET框架的使用(二)

    原文:模糊语意变数.规则和模糊运算--AForge.NET框架的使用(二) 语意变数(Linguistic Variable) 语意变数存储了数个语意量(标签),每个语意量包含一个识别名和模糊集合.在 ...

  10. Linux系统编程(35)—— socket编程之TCP服务器的并发处理

    我们知道,服务器通常是要同时服务多个客户端的,如果我们运行上一篇实现的server和client之后,再开一个终端运行client试试,新的client就不能能得到服务了.因为服务器之支持一个连接. ...