1.什么是线性结构
线性结构是一种有序数据项的集合,其中每个数据项都有唯一的前驱和后继(除了第一个没有前驱,最后一个没有后继)。新的数据项加入到数据集中时,只会加入到原有某个数据项之前或之后。具有这种性质的数据集,就称为线性结构。 不同线性结构的关键区别在于数据项增减的方式。有的结构只允许数据项从一端添加,而有的机构则允许数据项从两端移除。
2.从4个最简单但功能强大的结构入手,开始研究数据结构(栈Stack、队列Queue、双端队列DQeque、列表List)
2.1 栈
2.1.1 什么是栈?
一种有次序的数据项集合。在栈中,数据项的加入和移除仅发生在同一端。这一端叫栈顶(top),另一端叫栈底(base) 距离栈底越近的数据项留在栈中的时间越长,而新加入栈的数据项会被最先移除。这种次序通常称为“后进先出”(Last in First out,LIFO)。这是一种基于数据项保存时间的次序,时间越短的离栈顶越近,而时间越长的离栈底越近。
2.1.2 抽象数据类型“栈”定义的操作
操作样例
2.1.3 用python实现ADT Stack
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
s
=Stack
()
print(s
.isEmpty
())
s
.push
(4)
s
.push
('dog')
print(s
.peek
())
s
.push
(True)
print(s
.size
())
print(s
.isEmpty
())
s
.push
(8.4)
print(s
.pop
())
print(s
.pop
())
print(s
.size
())
2.1.4 栈的应用:简单括号匹配
如何构造括号匹配识别算法?
从左到右扫描括号串,最新打开的左括号应该匹配最先遇到的有括号。这样,第一个左括号(最先打开)就应该匹配最后一个右括号(最后遇到)。这种反转的识别,正好符合栈的特性。
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
def parChecker(symbolString
):
s
=Stack
()
balanced
=True
index
=0
while index
<len(symbolString
) and balanced
:
symbol
=symbolString
[index
]
if symbol
=="(":
s
.push
(symbol
)
else:
if s
.isEmpty
():
balanced
=False
else:
s
.pop
()
index
=index
+1
if balanced
and s
.isEmpty
():
return True
else:
return False
print(parChecker
('((()))'))
print(parChecker
('(()'))
通用括号匹配算法
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
def parChecker(symbolString
):
s
=Stack
()
balanced
=True
index
=0
while index
<len(symbolString
) and balanced
:
symbol
=symbolString
[index
]
if symbol
in"([{":
s
.push
(symbol
)
else:
if s
.isEmpty
():
balanced
=False
else:
top
=s
.pop
()
if not matches
(top
,symbol
):
balanced
=False
index
=index
+1
if balanced
and s
.isEmpty
():
return True
else:
return False
def matches(open,close
):
opens
="([{"
closers
=")]}"
return opens
.index
(open)==closers
.index
(close
)
print(parChecker
('{{([][])}()}'))
print(parChecker
('([)'))
2.1.5 栈的应用:十进制转二进制
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
def divideBy2(number
):
remstack
=Stack
()
while number
>0:
rem
=number
%2
remstack
.push
(rem
)
number
=number
//2
binString
=""
while not remstack
.isEmpty
():
binString
=binString
+str(remstack
.pop
())
return binString
print(divideBy2
(42))
十进制转十六进制以下的任意进制
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
def baseConverter(number
,base
):
digits
="0123456789ABCDEF"
remstack
=Stack
()
while number
>0:
rem
=number
%base
remstack
.push
(rem
)
number
=number
//base
newString
=""
while not remstack
.isEmpty
():
newString
=newString
+digits
[remstack
.pop
()]
return newString
print(baseConverter
(25,2))
print(baseConverter
(25,8))
print(baseConverter
(25,16))
2.1.6 栈的应用:通用的中缀转后缀算法
我们通常看到的表达式像这样:BC,很容易知道这是B乘以C。这种操作符(operator)介于操作数(operand)中间的表示法称为中缀表示法。但是有时候中缀表示法会引起混淆,如"A+BC"表示A+B然后再乘以C还是B*C然后再加A? 例如中缀表达式A+B 将操作符移到前面,变成“+AB”; 或者将操作符一道最后,变为“AB+” 我们就得到了表达式的另外两种表示法:“前缀”和“后缀”表示法(以操作符相对于操作数的位置来定义)。 举例
中缀表达式转换为前缀和后缀形式的步骤
(1)将中缀表达式转换为全括号形式 (2)将所有的操作符移动到子表达式所在左括号(前缀)或者右括号(后缀)处,替代之,再删除所有括号。
通用的中缀转后缀算法
后面的算法描述中,约定中缀表达式是由空格隔开的一系列单词(token)构成。操作符单词包括*/±(),而操作数单次则是单字母标识符A、B、C等 (1)创建空栈opstack用于暂存操作符,空表postfixList用于保存后缀表达式。 (2)将中缀表达式转换为单词(token)列表 A+BC=split=>[‘A’,’+’,‘B’,’’,‘C’] (3)从左到右扫描中缀表达式单词列表
如果单词是操作数,则直接添加到后缀表达式列表的末尾如果单词是左括号“(”,则压入opstack栈顶如果单词是右括号“)”,则反复弹出opstack栈顶操作符,加入到输出列表末尾,直到碰到左括号如果单词是操作符“*/±”,则压入opstack栈顶(但是咋压入之前,要比较其与栈顶操作符的优先级,如果栈顶的高于或低于它,就要反复弹出栈顶操作符,加入到输出列表末尾,直到栈顶的操作符优先级低于它) (4)中缀表达式单词列表扫描结束后,把opstack栈中的所有剩余操作符依次弹出,添加到输出列表末尾。 (5)把输出列表再用join方法合并成后缀表达式字符串,算法结束。
实例
python代码实现
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
def infixToPostfix(infixexpr
):
prec
={}
prec
["*"]=3
prec
["/"]=3
prec
["+"]=2
prec
["-"]=2
prec
["("]=1
opStack
=Stack
()
postfixList
=[]
tokenList
=infixexpr
.split
()
for token
in tokenList
:
if token
in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token
in "0123456789":
postfixList
.append
(token
)
elif token
=='(':
opStack
.push
(token
)
elif token
==')':
topToken
=opStack
.pop
()
while topToken
!='(':
postfixList
.append
(topToken
)
topToken
=opStack
.pop
()
else:
while (not opStack
.isEmpty
()) and \
(prec
[opStack
.peek
()]>=prec
[token
]):
postfixList
.append
(opStack
.pop
())
opStack
.push
(token
)
while not opStack
.isEmpty
():
postfixList
.append
(opStack
.pop
())
return " ".join
(postfixList
)
print(infixToPostfix
('A * B + C * D'))
2.1.7 后缀表达式求值问题
在对后缀表达式从左到右扫描的过程中,由于操作符在操作数后面,所以要暂存操作数,在碰到操作符的时候再将暂存的两个操作数进行实际的计算。
实例
后缀表达式求值算法流程
(1)创建空栈operandStack用于暂存操作数 (2)将后缀表达式用split方法解析为单词(token)的列表 (3)从左到右扫描单词列表
如果但此时一个操作数,将单词转换为整数int,压入operandStack栈顶如果单词是一个操作符(*/±),就开始求值,从栈顶弹出2个操作数,先弹出的是右操作数,后弹出的是左操作数,计算后将值重新呀入栈顶。 (4)单词列表扫描结束后,表达式的值就在栈顶 (5)弹出栈顶的值,返回
python代码实现
class Stack:
def __init__(self
):
self
.items
=[]
def isEmpty(self
):
return self
.items
==[]
def push(self
,item
):
self
.items
.append
(item
)
def pop(self
):
return self
.items
.pop
()
def peek(self
):
return self
.items
[len(self
.items
)-1]
def size(self
):
return len(self
.items
)
def postfixEval(postfixExpr
):
operandStack
=Stack
()
tokenList
=postfixExpr
.split
()
for token
in tokenList
:
if token
in "0123456789":
operandStack
.push
(int(token
))
else:
operand2
=operandStack
.pop
()
operand1
=operandStack
.pop
()
result
=doMath
(token
,operand1
,operand2
)
operandStack
.push
(result
)
return operandStack
.pop
()
def doMath(op
,op1
,op2
):
if op
=="*":
return op1
*op2
if op
=="/":
return op1
/op2
if op
=="+":
return op1
+op2
else:
return op1
-op2
print(postfixEval
("4 5 6 * +"))