数据结构:循环队列

Leetcode622:题目

设计一个可以容纳 k 个元素的循环队列。需要实现以下接口:

class MyCircularQueue {
    // 参数k表示这个循环队列最多只能容纳k个元素
    public MyCircularQueue(int k);
    // 将value放到队列中, 成功返回true
    public boolean enQueue(int value);
    // 删除队首元素,成功返回true
    public boolean deQueue();
    // 得到队首元素,如果为空,返回-1
    public int Front();
    // 得到队尾元素,如果队列为空,返回-1
    public int Rear();
    // 看一下循环队列是否为空
    public boolean isEmpty();
    // 看一下循环队列是否已放满k个元素
    public boolean isFull();
}

解法一

我们使用frontrearused来表示循环队列的头指针、尾指针和队列中已存在的元素数量
图中的k=6
图中的k为6

注意:
1、我们可以看到,当队列为空或者队列满时,front和rear指向的区域相同;只有当队列不为空并且未满时,front和rear才指向不同的区域,其中rear指向尾部元素的后一块空间。
2、由于front和rear存在指向一块空间的情况,因此需要使用used变量来表示当前循环队列是否为空或是否为满。
3、在面试时只能通过普通的数组结构进行,因此需要规定好下标的活动范围: [0, k - 1]
4、index=i时,前一个元素是i-1, 后一个元素是i+1
5、index=0时,前一个元素是capacity-1,后一个元素是1
6、index=capacity-1时,前一个元素是capacity-2,后一个元素是0
以上前后元素下标的计算方式都可以用如下公式计算:
前一个元素下标:(index - 1 + capacity) % capacity
后一个元素下标:(index + 1) % capacity

根据上述分析可以得到如下代码:

class MyCircularQueue {

    // 队首指针
    private int front = 0;

    // 队尾指针: 指向队尾元素的下一块区域
    private int rear = 0;

    // 队列中已经存在的元素个数
    private int used = 0;

    // 队列容量
    private int capacity = 0;

    // 队列中已经存在的元素(数组)
    private int[] nums = null;

    // 初始化
    public MyCircularQueue(int k) {
        this.capacity = k;
        nums = new int[k];
    }
    
    public boolean enQueue(int value) {
        // 如果队列已满,则无法插入元素
        if (isFull()) {
            return false;
        }

        // 添加元素并将队尾指针指向下一块区域
        nums[rear] = value;
        rear = (rear + 1) % capacity;
        used += 1;
        return true;
    }
    
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }

        // 提取队首元素,将指针指向下一个元素所在区域
        int ret = nums[front];
        front = (front + 1) % capacity;
        used--;
        return true;
    }
    
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return nums[front];
    }
    
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        
        // 由于rear指向的是队尾元素的下一块区域,所以获取队尾元素需要进行 -1后取模
        int tail = (rear - 1 + capacity) % capacity;
        return nums[tail];
    }
    
    public boolean isEmpty() {
        if (used == 0) {
            return true;
        }
        return false;
    }
    
    public boolean isFull() {
        if (used == capacity) {
            return true;
        }
        return false;
    }
}

解法二

解法一中使用的是容量为k的情况,还可以使用k + 1容量的队列存储k个元素:
1、申请空间时,需要申请k+1大小的数组(实际存储k个元素)
2、当元素满时,front和rear需要保证有间隙
在这里插入图片描述
可以发现,
1、循环队列实际上浪费了一个元素的空间,front和rear的下标区间为[0, k]。这个浪费的元素必须卡在 front 与 rear 之间。判断队列空或者满可以:
front == rear, 此时队列为空;
(rear + 1) % capacity == front,此时队列为满。
2、由于空队列和满队列front和rear下标的指向不同,所以可以不需要用used来判断队列是否为满

class MyCircularQueue {

    // 队首指针
    private int front = 0;

    // 队尾指针: 指向队尾元素的下一块区域
    private int rear = 0;

    // 队列容量
    private int capacity = 0;

    // 队列中已经存在的元素(数组)
    private int[] nums = null;

    // 初始化
    public MyCircularQueue(int k) {
        this.capacity = k + 1;
        nums = new int[k + 1];
    }
    
    public boolean enQueue(int value) {
        // 如果队列已满,则无法插入元素
        if (isFull()) {
            return false;
        }

        // 添加元素并将队尾指针指向下一块区域
        nums[rear] = value;
        rear = (rear + 1) % capacity;
        return true;
    }
    
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }

        // 提取队首元素,将指针指向下一个元素所在区域
        int ret = nums[front];
        front = (front + 1) % capacity;
        return true;
    }
    
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return nums[front];
    }
    
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        
        // 由于rear指向的是队尾元素的下一块区域,所以获取队尾元素需要进行 -1后取模
        int tail = (rear - 1 + capacity) % capacity;
        return nums[tail];
    }
    
    public boolean isEmpty() {
        if (front == rear) {
            return true;
        }
        return false;
    }
    
    public boolean isFull() {
        // 队列满时,头指针和尾指针相邻
        if ((rear + 1) % capacity == front) {
            return true;
        }
        return false;
    }
}

总结

相似点

两种解法都使用了取模的方法;当求前一个元素下标时,一定要使用(front - 1 + capacity) % capacity, 否则当front为0时会出错

不同点

解法一使用了三种控制变量;解法二使用了两种控制变量,但是损失了一个元素的存储空间。在多线程编程的情况下,控制变量越少,越容易实现无锁编程;因此使用解法二更容易实现无锁队列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一章 绪论作业答案(共50分) 一、分析如下程序中 (1)~ (10)各语句的频度。(每个1分,共10分) Ex( ) { int i , j , t ; (1) for( i=1 ; i<10 ; i++) //n = (2) printf(“\n %d” , i ); //n = (3) for(i=1; i<=2; i++) //n = (4) printf(“\n”); //n = (5) for(i=1; i<=9; i++) //n = { (6) for(j=1; j <= i ; j++) //n = { (7) t = i * j ; //n = (8) printf(“]”,t); //n = } (9) for(j=1; j 0) { if(x > 100) {x -= 10 ; y -- ;} else x ++ ; } 问if 语句执行了多少次?(2分) y--执行了多少次?(2分) x ++执行了多少次?(2分) 三、回答问题(共25分) 书中16页的起泡排序如下: void bubble_sort(int a[],int n){ //将a中整数序列重新排列成自小至大有序的整数序列。 for(i=n-1,change=TRUE;i>=1&&change;--i){ change=FALSE; for(j=0;ja[j+1]{a[j]<-->a[j+1];change=TRUE; } } }//bubble_sort 1.(共15分)分析该算法的最佳情况 ,最坏情况和平均情况下各自的时间复杂度(给出分析思路与过程)。 (1) 最佳情况的时间复杂度分析(5分): (2) 最坏情况的时间复杂度分析(5分): (3) 平均情况的时间复杂度分析(5分): 2.(共10分)比较与C语言书中的起泡排序异同,并从时空效率角度说明谁更优。 四、完成如下选择题(每3分,共9分)。 1. 设f为原操作,则如下算法的时间复杂度是( )。 for (i = 1; i*i=1;i--) for(j=1;jA[j+1]) A[j]与A[j+1]对换; 其中n为正整数,则算法在最坏情况下的时间复杂度为( )。 A.O(n) B.O(nlog2n) C. O(n3) D. O(n2)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值