leetcode 641 设计循环双端队列 思路与C Python实现

题目地址:641. 设计循环双端队列 - 力扣(LeetCode)

队列——>循环队列——>双端循环队列,兼具循环和双端的特性。循环表现在可以充分利用队列空间,队列是一个环;双端表现在插入、删除和获取操作既可以在队首执行,也可以在队尾执行。

双端队列结构体的组成元素:队首指针,队尾指针,队列尺寸,一个指向一块存储空间大小为队列尺寸的一级指针;

typedef struct {
	int *arr;
	int head;
	int tail;
	int size;
} MyCircularDeque; //1个结构体就代表1个双端队列,队列空间用结构体中的数组成员来表示;

在队尾删除:判断队列是否为空,队尾指针前移,因为循环队列存在队尾指针指向索引为0的情况,这时队尾指针-1为负数索引,所以前移后的队尾指针obj->tail = (obj->tail-1+obj->size) % obj->size;

bool myCircularDequeDeleteLast(MyCircularDeque* obj) {
	if ( myCircularDequeIsEmpty(obj)) return false;

	obj->tail = (obj->tail - 1 + obj->size) % obj->size; 
	//因为循环队列存在队尾指针指向索引为0的情况,此时队尾指针-1为负数索引;
	return true;
}

在队首删除:判断队列是否为空,因为循环队列存在队首指针后移越界的情况,所以后移后的队首指针obj->head = (obj->head+1) % obj->size;

bool myCircularDequeDeleteFront(MyCircularDeque* obj) {
	if ( myCircularDequeIsEmpty(obj)) return false;

	obj->head = (obj->head + 1) % obj->size;
	//因为循环队列存在队首指针后移越界的情况;
	return true;

}

在队尾添加:判断队列是否为满,在此时队尾指针指向的位置存储添加的元素,队尾指针后移,后移后的队尾指针obj->tail = (obj->tail+1) % obj->size;

bool myCircularDequeInsertLast(MyCircularDeque* obj, int value) {

	if ( myCircularDequeIsFull(obj)) return false;

	obj->arr[obj->tail] = value;
	obj->tail = (obj->tail + 1) % obj->size;

	return true;
}

 在队首添加:判断队列是否为满,队首指针前移,前移后的队首指针obj->head = (obj->head+obj->size-1) % obj->size,在前移后的队首指针指向的位置存储添加的元素;

bool myCircularDequeInsertFront(MyCircularDeque* obj, int value) {

	if (myCircularDequeIsFull(obj)) return false;

	int pos = (obj->head - 1 + obj->size) % obj->size;
	//因为循环队列存在队首指针指向索引为0的情况,此时队首指针-1为负数索引;
	obj->arr[pos] = value;
	obj->head = pos;
	return true;
}

获得队首元素:因为队首指针指向的就是队首元素,所以在判断队列不为空的情况下,直接返回队首指针指向的元素;

int myCircularDequeGetFront(MyCircularDeque* obj) {
	if ( myCircularDequeIsEmpty(obj) ) return -1;

	return obj->arr[obj->head];
}

获得队尾元素;在队列不为空的情况下,返回队尾指针指向位置的前一个位置的元素(考虑循环特性,返回元素的索引表达式和队尾指针前移的表达式相同);

int myCircularDequeGetRear(MyCircularDeque* obj) {
	if ( myCircularDequeIsEmpty(obj) ) return -1;

	int pos = (obj->tail - 1 + obj->size) % obj->size;
	//考虑队尾指针指向位置0的情况;

	return obj->arr[pos];
}

总结:无论是队首指针还是队尾指针,考虑到循环队列的循环特性,后移(队首删除&队尾添加)表达式相同且比较简单,前移(队首添加&队尾删除)表达式相同不要忘记进行+obj->size操作;
判断队列为空:首尾指针重合;

bool myCircularDequeIsEmpty(MyCircularDeque* obj) {

	return (obj->head == obj->tail) ? true : false;//头尾指针重合

}

判断队列为满:队尾指针+1与队首指针重合,考虑到循环特性判断表达式为obj->head == (obj->tail+1) % obj->size,

bool myCircularDequeIsFull(MyCircularDeque* obj) {

	return (obj->head == (obj->tail + 1) % obj->size) ? true : false;
	//队尾指针+1与队首指针重合,考虑队尾指针+1后越界的情况;
}

注意这实际上牺牲了一个存储空间,这也是为什么队列尺寸初始化为k+1,以及为队列malloc一块大小为k+1的存储空间;

MyCircularDeque* myCircularDequeCreate(int k) {

	MyCircularDeque *obj = malloc(sizeof(MyCircularDeque));
	obj->arr = malloc(sizeof(int) * (k+1)); //循环队列有意浪费一个位置;
	obj->head = 0; //用整形表示队首和队尾指针,索引从0开始;
	obj->tail = 0; 
	obj->size = k + 1;
	return obj;
}
void myCircularDequeFree(MyCircularDeque* obj) {

	free(obj->arr); //先释放队列空间即结构体内的数组成员;
	free(obj); //再释放队列结构体指针;
	return ;

}

疑问:栈顶指针top初始值为-1,使用++top进行入栈操作,top始终指向栈顶元素;队列的尾指针都指向队尾元素的下一个位置?

//测试代码;
void main() {
	MyCircularDeque *obj;
	obj = myCircularDequeCreate(3);
	printf("%d\n", myCircularDequeInsertLast(obj, 1));
	printf("%d\n", myCircularDequeInsertLast(obj, 2));
	printf("%d\n", myCircularDequeInsertFront(obj, 3));
	printf("%d\n", myCircularDequeInsertFront(obj, 4));
	printf("%d\n", myCircularDequeGetRear(obj));
	printf("%d\n", myCircularDequeIsFull(obj));
	printf("%d\n", myCircularDequeDeleteLast(obj));
	printf("%d\n", myCircularDequeInsertFront(obj, 4));
	printf("%d\n", myCircularDequeGetFront(obj));
}

python版本:

class MyCircularDeque:

    def __init__(self, k: int):
        self.front = self.rear = 0
        self.elements = [0] * (k + 1)

    def insertFront(self, value: int) -> bool:
        if self.isFull():
            return False
        self.front = (self.front - 1 + len(self.elements)) % len(self.elements)
        self.elements[self.front] = value
        return True

    def insertLast(self, value: int) -> bool:
        if self.isFull():
            return False
        self.elements[self.rear] = value
        self.rear = (self.rear + 1) % len(self.elements)
        return True

    def deleteFront(self) -> bool:
        if self.isEmpty():
            return False
        self.front = (self.front + 1) % len(self.elements)
        return True

    def deleteLast(self) -> bool:
        if self.isEmpty():
            return False
        self.rear = (self.rear - 1 + len(self.elements)) % len(self.elements)
        return True

    def getFront(self) -> int:
        return -1 if self.isEmpty() else self.elements[self.front]


    def getRear(self) -> int:
        return -1 if self.isEmpty() else self.elements[(self.rear - 1 + len(self.elements)) % len(self.elements)]

    def isEmpty(self) -> bool:
        return self.front == self.rear

    def isFull(self) -> bool:
        return self.front == ((self.rear + 1) % len(self.elements))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

艺术家常

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

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

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

打赏作者

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

抵扣说明:

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

余额充值