数据结构【Golang实现】(五)——栈

2023-04-16 15:40:42 浏览数 (1)

数据结构——栈

顺序栈

1. 定义结构体

代码语言:javascript复制
type SequentialStack struct {
	items []any // 栈内元素
	top   int   // 栈顶,也是栈内元素数
	size  int   // 栈容量
}

2. NewStack()

代码语言:javascript复制
// NewStack 初始化栈
func NewStack(size int) *SequentialStack {
	return &SequentialStack{
		size:  size,
		top:   0,
		items: make([]any, size),
	}
}

3. Length()

代码语言:javascript复制
// Length 获取栈内元素个数
func (stack *SequentialStack) Length() int {
	return stack.top
}

4. IsFull()

代码语言:javascript复制
// IsFull 判断栈是否已满
func (stack *SequentialStack) IsFull() bool {
	return stack.top == stack.size // 如果top=设定的栈最大长度,说明栈满
}

5. IsEmpty()

代码语言:javascript复制
// IsEmpty 判断栈是否已空
func (stack *SequentialStack) IsEmpty() bool {
	return stack.top == 0
}

6. Push()

代码语言:javascript复制
// Push 入栈
func (stack *SequentialStack) Push(item any) bool {
	if stack.IsFull() {
		fmt.Println("栈满")
		return false
	}
	stack.items[stack.top] = item
	stack.top  
	return true
}

7. Pop()

代码语言:javascript复制
// Pop 出栈
func (stack *SequentialStack) Pop() any {
	if stack.IsEmpty() {
		fmt.Println("栈已空")
		return nil
	}
	stack.top-- // 栈顶指针下移
	return stack.items[stack.top]
}

8. Peek()

代码语言:javascript复制
// Peek 获取栈顶元素但不出栈
func (stack *SequentialStack) Peek() any {
	if stack.IsEmpty() {
		fmt.Println("栈空")
		return nil
	}
	return stack.items[stack.top-1]
}

链式栈

1. 定义结构体

代码语言:javascript复制
type SequentialStack struct {
	items []any // 栈内元素
	top   int   // 栈顶,也是栈内元素数
	size  int   // 栈容量
}

2. IsEmpty()

代码语言:javascript复制
// IsEmpty 判断栈是否为空
func (stack *LinkedStack) IsEmpty() bool {
	return stack.headNode == nil
}

3. Length()

代码语言:javascript复制
// Length 获取栈内元素个数
func (stack *LinkedStack) Length() int {
	if stack.IsEmpty() {
		return 0
	}
	currentNode := stack.headNode
	count := 0
	for currentNode != nil {
		count  
		currentNode = currentNode.Next
	}
	return count
}

4. Push()

代码语言:javascript复制
// Push 入栈
func (stack *LinkedStack) Push(data any) {
	node := &Node{Data: data}
	node.Next = stack.headNode
	stack.headNode = node
}

5. Pop()

代码语言:javascript复制
// Pop 出栈
func (stack *LinkedStack) Pop() any {
	if stack.headNode == nil {
		fmt.Println("栈已空")
		return nil
	}
	data := stack.headNode.Data
	stack.headNode = stack.headNode.Next
	return data
}

6. Peek()

代码语言:javascript复制
// Peek 获取栈顶元素但不出栈
func (stack *LinkedStack) Peek() any {
	if stack.IsEmpty() {
		fmt.Println("栈已空")
		return nil
	}
	return stack.headNode.Data
}

7. Traverse()

代码语言:javascript复制
// Traverse 遍历链式栈
func (stack *LinkedStack) Traverse() {
	if stack.headNode == nil {
		fmt.Println("空栈")
		return
	}
	currentNode := stack.headNode
	for currentNode.Next != nil {
		fmt.Printf("%v -> ", currentNode.Data)
		currentNode = currentNode.Next
	}
	fmt.Println(currentNode.Data)
}

应用场景

1. 表达式求值

一个表达式包含两个部分,数字和运算符。我们用两个栈来实现表达式求值,一个栈用来存储数字,一个栈用来存储运算符。

假设有这么一个表达式1000 5*6-6,从左向右遍历表达式,当遇到数字时,将数字放入到存储数字的栈;如果遇到运算符,将存储运算符栈的栈顶元素取出,进行优先级比较。

如果比运算符栈顶元素优先级高,则将当前运算符压入到存储运算符的栈中;如果比运算符栈顶元素低或优先级一样,则从存储数字的栈中取出两个元素,然后进行计算,将计算的结果放入到存储数字的栈中。 代码实现(例如“1000 5*6-6”这样简单的正整数运算,不包括() [] ):

代码语言:javascript复制
// 只包括 -*/的简单正整数运算,不包括负数 [] ()
var numStack = NewStack(20)  //储存数字
var operStack = NewStack(20) // 储存运算符

// Calculation 表达式计算
// 关键:
//
//	如果遍历到的运算符 优先级高于 栈顶运算符,直接存入运算符栈;
//	反之 计算之前所有存入栈中的数字,然后将结果存入numStack,再将该运算符存入operStack;
func Calculation(expr string) (int, error) {
	last := -1 // 记录上一个是否是数字
	
	for i, s := range expr { // 遍历表达式
		isNum := GetPriority(s) // 获取 当前 s的优先级
		if isNum == 0 {         // 如果是数字,直接入栈
			if last == 0 { // 判断上一个是否是数字,如果是,就需要弹出、拼接、重新入栈
				lastNum := strconv.Itoa(numStack.Pop().(int))
				newNum, _ := strconv.Atoi(lastNum   string(s))
				numStack.Push(newNum)
			} else { // 如果不是,直接入栈即可
				num, _ := strconv.Atoi(string(s))
				numStack.Push(num)
				
				if i == len(expr)-1 { // 如果是最后一个字符,进行最终计算
					for !operStack.IsEmpty() && !numStack.IsEmpty() {
						first := numStack.Pop()
						second := numStack.Pop()
						oper := operStack.Pop().(int32)
						calResult := GetCalResult(second.(int), first.(int), oper)
						numStack.Push(calResult)
					}
				}
			}
			
			last = isNum // 更新last状态
		} else { // s 是运算符
			last = isNum
			if operStack.IsEmpty() { // 当前运算符栈为空,直接入栈即可
				operStack.Push(s)
				continue
			}
			
			topOper := operStack.Peek().(int32)     // 栈顶运算符
			topOperPriority := GetPriority(topOper) // 获取栈顶运算符优先级
			
			if isNum > topOperPriority { // 如果当前运算符优先级>栈底运算符优先级 ,直接入栈
				operStack.Push(s)
				continue
			} else { // 反之,将栈内所有元素弹出进行运算,并将结果入栈,再将该运算符入栈
				for !operStack.IsEmpty() && !numStack.IsEmpty() {
					first := numStack.Pop()
					second := numStack.Pop()
					oper := operStack.Pop().(int32)
					calResult := GetCalResult(second.(int), first.(int), oper) // 注意这里first和second的位置
					numStack.Push(calResult)
				}
				operStack.Push(s)
			}
		}
	}
	
	return numStack.Peek().(int), nil
}

// GetCalResult 获取两数计算结果
func GetCalResult(a, b int, oper int32) int {
	switch oper {
	case 42:
		return a * b
	case 43:
		return a   b
	case 45:
		return a - b
	case 47:
		return a / b
	default:
		return 0
	}
}

// GetPriority 获取符号优先级   - * /
func GetPriority(c int32) int {
	if c == 43 || c == 45 { //   -
		return 1
	} else if c == 42 || c == 47 { // * /
		return 2
	} else {
		return 0 // 数字
	}
}

2. 括号匹配

用栈来解决。从左到右遍历括号串,遇到未匹配的左括号则将其压入栈中,遇到右括号时,从栈顶取出一个左括号,如果能匹配,则继续遍历后边的括号,当遍历完之后,栈为空了,说明这个括号串是匹配的,否则是不匹配的。具体实现如下:

代码语言:javascript复制
// BracketMatch 括号匹配
func BracketMatch(str string) bool {
	leftBracketStack := NewStack(10) // 左括号栈
	for _, s := range str {
		if s == '[' || s == '(' || s == '{' { // 左括号直接入栈
			leftBracketStack.Push(s)
		} else {
			if leftBracketStack.IsEmpty() { // 如果左括号已经消耗完仍有有括号,直接false即可
				return false
			}
			rightBracket := leftBracketStack.Pop()
			switch rightBracket {
			case '(':
				if s == ')' {
					continue
				} else {
					return false
				}
			case '[':
				if s == ']' {
					continue
				} else {
					return false
				}
			case '{':
				if s == '}' {
					continue
				} else {
					return false
				}
			default:
				fmt.Println("非法字符")
				return false
			}
		}
	}
	return leftBracketStack.Length() == 0
}
代码语言:javascript复制
// BracketMatch2 括号匹配
func  BracketMatch2更简单的方法(str string) bool {
	brackets := map[int32]int32{')': '(', ']': '[', '}': '{'}
	var stack []int32
	for _, s := range str {
		if s == '[' || s == '(' || s == '{' { // 左括号直接入栈
			stack = append(stack, s)
		} else if len(stack) != 0 && brackets[s] == stack[len(stack)-1] { // 如果栈内有左括号,而且栈顶左括号与s匹配,出栈
			stack = stack[:len(stack)-1]
		} else {
			return false
		}
	}
	return len(stack) == 0
}

0 人点赞