GO语言数据结构之队列

https://www.cnblogs.com/zly-go/p/15472059.html

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

顺序队列

在顺序队列中,为了降低运算的复杂度,元素入队时,只修改队尾指针;元素出对时,只修改队头指针。由于顺序队列的存储空间是提前设定的,因此队尾指针会有一个上限值,当队尾指针达到其上限时,就不能只通过修改队尾指针来实现新元素的入队操作了。

代码实现

//定义队列结构
type queue struct {
	MaxSize int //定义队列容量
	array   [10]int
	read    int // 定义指向队尾
	front   int // 定义指向队头
}

//添加数据
func (queue *queue) add(data int) (err error) {
	//判断队列是否已满
	if queue.read == queue.MaxSize-1 {
		return errors.New("队列满")
	}
	//添加数据,队尾下标+1
	queue.read++
	queue.array[queue.read] = data
	return
}

//取数据
func (queue *queue) get() (data int, err error) {
	if queue.read == queue.front {
		return 0, errors.New("队列空")
	}
	//取数据队头下标+1
	queue.front++
	data = queue.array[queue.front]
	return data, err
}

//展示数据
func (queue *queue) show() {
	//获取数据从对头遍历到队尾
	for i := queue.front + 1; i <= queue.read; i++ {
		fmt.Println(i, queue.array[i])
	}
}

func main() {
	//初始化队列
	q := &queue{
		MaxSize: 10,
		array:   [10]int{},
		read:    -1,
		front:   -1,
	}
	var key string
	var val int
	for true {
		fmt.Println("1 添加数据")
		fmt.Println("2 获取数据")
		fmt.Println("3 显示队列")
		fmt.Println("4 退出")
		_, err := fmt.Scanln(&key)
		if err != nil {
			return
		}
		switch key {
		case "1":
			_, err := fmt.Scanln(&val)
			if err != nil {
				return
			}
			err = q.add(val)
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println("成功")
			}
		case "2":
			get, err := q.get()
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println(get)
			}
		case "3":
			q.show()
		case "4":
			os.Exit(0)
		}
	}
}

假溢出

顺序队列入队和出队操作均是直接在其后面进行结点的链接和删除,这就造成其使用空间不断向出队的那一边偏移,产生假溢出。

假设是长度为5的数组,初始状态,空队列如所示,front与 rear指针均指向下标为-1的位置。

然后入队a1、a2、a3、a4, front指针依然指向下标为-1位置,而rear指针指向下标为3的位置。

 

 出队a1、a2,则front指针指向下标为2的位置,rear不变

再入队a5,a6,此时front指针不变,rear指针移动到数组之外。

 

假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,我们的队列在下标为0和1的地方还是空闲的。我们把这种现象叫做假溢出

如何解决这个问题,能够利用到前面空闲的区域有两种方案

1、在元素出队列时所有元素向前平移,但是效率低下不建议采用

2、将新入队元素插入空闲区域,形成一个循环的结构,这就叫环形队列

环形队列

 虽然使用循环队列,解决了假溢出问题,但是又有新问题发生——判空的问题,因为仅凭 front = rear 不能判定循环队列是空还是满。

空循环队列

满循环队列

这种情况下通常采用留一位元素空间的方式来进行判断,此时front = rear + 1

代码实现

//定义环形队列结构
type LoopQueue struct {
	data [3]int //定义环形数组
	head int    //头部指针
	tail int    //尾部指针
	cap  int    //环形数组大小
}

//入队列
func (LoopQueue *LoopQueue) add(val int) (err error) {
	//判断队列是否已满
	if (LoopQueue.tail+1)%LoopQueue.cap == LoopQueue.head {
		return errors.New("队列满")
	}
	//放入数据
	LoopQueue.data[LoopQueue.tail] = val
	LoopQueue.tail = (LoopQueue.tail + 1) % LoopQueue.cap
	return
}

//出队列
func (LoopQueue *LoopQueue) get() (val int, err error) {
	//判断队列是否为空
	if LoopQueue.head == LoopQueue.tail {
		return 0, errors.New("队列空")
	}
	//赋值
	val = LoopQueue.data[LoopQueue.head]
	LoopQueue.head = (LoopQueue.head + 1) % LoopQueue.cap
	return
}

//数据展示
func (LoopQueue *LoopQueue) show() {
	//fmt.Println(LoopQueue.data)
	length := LoopQueue.length()
	if length == 0 {
		fmt.Println("队列为空")
	}
	//设计辅助变量指向头部指针
	tmpFront := LoopQueue.head
	for i := 0; i < length; i++ {
		fmt.Printf("data[%d]=%v\t", tmpFront, LoopQueue.data[tmpFront])
		tmpFront = (tmpFront + 1) % LoopQueue.cap
	}
	fmt.Println()
}

//获取元素个数
func (LoopQueue *LoopQueue) length() int {
	return (LoopQueue.tail + LoopQueue.cap - LoopQueue.head) % LoopQueue.cap
}

func main() {
	q := LoopQueue{
		data: [3]int{},
		head: 0,
		tail: 0,
		cap:  3,
	}

	var key string
	var val int
	for true {
		fmt.Println("1 添加数据")
		fmt.Println("2 获取数据")
		fmt.Println("3 显示队列")
		fmt.Println("4 退出")
		_, err := fmt.Scanln(&key)
		if err != nil {
			return
		}
		switch key {
		case "1":
			_, err := fmt.Scanln(&val)
			if err != nil {
				return
			}
			err = q.add(val)
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println("成功")
			}
		case "2":
			get, err := q.get()
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println(get)
			}
		case "3":
			q.show()
		case "4":
			os.Exit(0)
		}
	}
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花非玉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值