本文为学习笔记,感兴趣的读者可在MOOC中搜索《数据结构与算法Python版》或阅读《数据结构(C语言版)》(严蔚敏)
目录链接:https://blog.csdn.net/floating_heart/article/details/123991211
1.1.1什么是线性结构Linear Structure
线性结构是一种有序数据项的集合,其中每个数据项都有唯一的前驱和后继
除了第一个没有前驱,最后一个没有后继
新的数据项加入到数据集中时,只会加入到原有某个数据项之前或之后
具有这种性质的数据集,就称为线性结构
4种最简单但功能强大的结构:
栈Stack;队列Queue;双端队列Deque;列表List
1.1.2栈抽象数据类型及实现
抽象数据类型“栈”是一个有次序的数据集,
每个数据项仅从“栈顶”一端加入到数据集中、从数据集中移除,
栈具有后进先出LIFO(Last in First out)的特性
“栈”定义的操作:
**Stack():**创建一个空栈,不包含任何数据项
**push(item):**将item加入栈顶,无返回值
**pop():**将栈顶数据项移除,并返回,栈被修改
peek():“窥视”栈顶数据项,返回栈顶的数据项但不移除,栈不被修改
**isEmpty():**返回栈是否为空栈
**size():**返回栈中有多少个数据项
实现
代码:Python
class Stack://栈顶为list尾端的实现方法,其push()和pop()为O(1),存在其他实现方法——Python
def __init__(self):
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 isEmpty(self):
return self.items == []
def size(self):
return len(self.items)
代码:JavaScript-prototype
function Stack() {//prototype方法——Javascript
this.items = []
}
Stack.prototype.push = function (item) {
this.items.push(item)
}
Stack.prototype.pop = function () {
return this.items.pop()
}
Stack.prototype.peek = function () {
return this.items[this.items.length - 1]
}
Stack.prototype.isEmpty = function () {
return this.items.length == 0
}
Stack.prototype.size = function () {
return this.items.length
}
代码:JavaScript-class
class Stack_c {//class方法——Javascript
constructor() {
this.items = []
}
push(item) {
this.items.push(item)
}
pop() {
return this.items.pop()
}
peek() {
return this.items[this.items.length - 1]
}
isEmpty() {
return this.items.length == 0
}
size() {
return this.items.length
}
}
1.1.3简单括号匹配
问题描述:
括号的使用必须遵循“平衡”规则
首先,每个开括号要恰好对应一个闭括号;
其次,每对开闭括号要正确的嵌套
正确的括号:
(()()()()),(((()))),(()((())()))
错误的括号:
((((((()),())),(()()(()
算法说明:
实现
代码:Python
from Stack import Stack
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 += 1
if balanced and s.isEmpty():
return True
else:
return False
代码:JavaScript
function parChecker(symbolstring) {//JavaScript
var s = new Stack()
var balanced = true
var index = 0
while (index < symbolstring.length && balanced) {
var symbol = symbolstring[index]
if (symbol == '(') {
s.push(symbol)
} else {
if (s.isEmpty()) {
balanced = false
} else {
s.pop()
}
}
index += 1
}
if (balanced && s.isEmpty()) {
return true
} else {
return false
}
}
扩展:通用括号匹配
问题描述:
下面这些是匹配的:
{ { ( [ ] [ ] ) } ( ) }
[ [ { { ( ( ) ) } } ] ]
[ ] [ ] [ ] ( ) { }
下面这些是不匹配的:
( [ ) ] ( ( ( ) ] ) )
[ { ( ) ]
实现
代码:Python
from Stack import Stack
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 += 1
if balanced and s.isEmpty():
return True
else:
return False
def matches(open,close):
opens = '([{'
closes = ')]}'
retrun opens.index(open) == closes.index(close)
代码:JavaScript
function matches(open, close) {
var opens = '([{'
var closes = ')]}'
return opens.indexOf(open) == closes.indexOf(close)
}
function parChecker(symbolstring) {
var s = new Stack()
var balanced = true
var index = 0
while (index < symbolstring.length && balanced) {
var symbol = symbolstring[index]
if ('([{'.indexOf(symbol) != -1) {
s.push(symbol)
} else {
if (s.isEmpty()) {
balanced = false
} else {
var top = s.pop()
if (!matches(top, symbol)) {
balanced = false
}
}
}
index += 1
}
if (balanced && s.isEmpty()) {
return true
} else {
return false
}
}
1.1.4十进制转换为二进制
问题描述:
算法说明:
连续除以2求余数
实现
代码:Python
from Stack import Stack
def divideBy2(decNumber):
remstack = Stack()
while decNumber > 0:
rem = decNumber % 2
remstack.push(rem)
decNumber //= 2
binString = ''
while not remstack.isEmpty():
binString += str(remstack.pop())
return binString
代码:JavaScript
function divideBy2(decNumber) {
var remstack = new Stack()
while (decNumber > 0) {
var rem = decNumber % 2
remstack.push(rem)
decNumber = parseInt(decNumber / 2)
}
var binString = ''
while (!remstack.isEmpty()) {
binString += String(remstack.pop())
}
return binString
}
1.1.5 表达式转换-中缀转后缀
中缀表达式“(A+B)C”:
按照转换的规则,前缀表达式是“+ABC”,而后缀表达式是“AB+C”
无论表达式多复杂,需要转换成前缀或者后缀,只需要两个步骤:
1.将中缀表达式转换为全括号形式;
2.将所有的操作符移动到子表达式所在的左括号(前缀)或者右括号(后缀)处,替代之,再删除所有的括号。
问题描述:
在中缀表达式转换为后缀形式的处理过程中,操作符比操作数要晚输出;
而这些暂存的操作符,由于优先级的规则,还有可能要反转次序输出。
后缀表达式中操作符应该出现在左括号对应的右括号位置
即:
在从左到右扫描逐个字符扫描中缀表达式的过程中,采用一个栈来暂存未处理的操作符
栈顶的操作符就是最近暂存进去的,当遇到一个新的操作符,就需要跟栈顶的操作符比较下优先级,再行处理。
算法说明:
前提说明
约定中缀表达式是由空格隔开的一系列单词(token)构成,
操作符单词包括*/±(),而操作数单词则是单字母标识符A、B、C等。
1.从左到右扫描中缀表达式单词列表:
如果单词是操作数,则直接添加到后缀表达式列表的末尾
如果单词是左括号“(”,则压入opstack栈顶
如果单词是右括号“)”,则反复弹出opstack栈顶操作符,加入到输出列表末尾,直到碰到左括号
如果单词是操作符“*/±”,则压入opstack栈顶
- 但在压入之前,要比较其与栈顶操作符的优先级
- 如果栈顶的高于或等于它,就要反复弹出栈顶操作符,加入到输出列表末尾
- 直到栈顶的操作符优先级低于它
2.中缀表达式单词列表扫描结束后,把opstack栈中的所有剩余操作符依次弹出,添加到输出列表末尾
3.把输出列表再用join方法合并成后缀表达式字符串,算法结束。
实现
代码:Python
from Stack import Stack
def infixToPostfix(infixexpr):
prec = {
'*':3,
'/':3,
'+':2,
'-':2,
'(':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)
代码:JavaScript
const {Stack} = require('./Stack')
function infixToPostfix(infixexpr) {
let prec = {
'*': 3,
'/': 3,
'+': 2,
'-': 2,
'(': 1,
}
let opStack = new Stack()
let postfixList = []
let tokenList = infixexpr.split(' ')
for (let token of tokenList) {
if (
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.indexOf(token) != -1 ||
'0123456789'.indexOf(token) != -1
) {
postfixList.push(token)
} else if (token == '(') {
opStack.push(token)
} else if (token == ')') {
let topToken = opStack.pop()
while (topToken != '(') {
postfixList.push(topToken)
topToken = opStack.pop()
}
} else {
while (!opStack.isEmpty() && prec[opStack.peek()] >= prec[token]) {
postfixList.push(opStack.pop())
}
opStack.push(token)
}
}
while (!opStack.isEmpty()) {
postfixList.push(opStack.pop())
}
return postfixList.join('')
}
1.1.6后缀表达式求值
问题描述:
在对后缀表达式从左到右扫描的过程中,由于操作符在操作数的后面,所以要暂存操作数,在碰到操作符的时候,再将暂存的两个操作数进行实际的计算(操作符只作用于离它最近的两个操作数)。
算法说明:
- 创建空栈operandStack用于暂存操作数
- 将后缀表达式用split方法解析为单词(token)的列表
- 从左到右扫描单词列表:
- 如果单词是一个操作数,将单词转换为整数int,压入operandStack栈顶
- 如果单词是一个操作符(*/±),就开始求值,从栈顶弹出2个操作数,先弹出的是右操作数,后弹出的是左操作数,计算后将值重新压入栈顶
- 单词列表扫描结束后,表达式的值就在栈顶
- 弹出栈顶的值,返回。
实现
代码:Python-1
from Stack import Stack
def postfixEval(postfixExpr)://输入的表达式中不能存在小数
operandStack = Stack()
tokenList = postfixExpr.split()
for token in tokenList:
if token in '+-*/':
op2 = operandStack.pop()
op1 = operandStack.pop()
result = doMath(token,op1,op2)
operandStack.push(result)
else:
operandStack.push(int(token))
return operandStack.pop()
def doMath(op,op1,op2):
if op == '*':
return op1 * op2
elif op == '/':
return op1 / op2
elif op == '+':
return op1 + op2
else:
return op1 - op2
代码:Python-2
from Stack import Stack
def postfixEval(postfixExpr):
operandStack = Stack()
tokenList = postfixExpr.split()
for token in tokenList:
if token in '+-*/':
op2 = operandStack.pop()
op1 = operandStack.pop()
operandStack.push(eval(str(op1)+token+str(op2)))
else:
operandStack.push(eval(token))
return operandStack.pop()
代码:JavaScript
const { Stack } = require('./Stack')
function doMath(op, op1, op2) {
if (op == '*') {
return op1 * op2
} else if (op == '/') {
return op1 / op2
} else if (op == '+') {
return op1 + op2
} else {
return op1 - op2
}
}
function postfixEval(postfixExpr) {
let operandStack = new Stack()
let tokenList = postfixExpr.split(' ')
for (let token of tokenList) {
if ('+-*/'.indexOf(token) != -1) {
let op2 = operandStack.pop()
let op1 = operandStack.pop()
let result = doMath(token, op1, op2)
operandStack.push(result)
} else {
operandStack.push(parseInt(token))
}
}
return operandStack.pop()
}