82、队列的实现

队列的讨论

小B:我发现队列要比栈低效,之前实现的顺序栈和链式栈的进栈和出栈操作时间复杂度都是O(1)。而用同样的方式实现队列,进队和出队的复杂度总有一个是O(n)。

◆小A:或许这是由于队列天生的性质决定的。

◆小D:我觉得队列的实现过程中不适合使用代码复用的思想, 需要重新实现以提高进队和出队的效率。

顺序队列的瓶颈

  • 顺序队列

    • 线性表的第一个元素作为队头

    • 线性表的最后一个元素作为队尾

  • 入队的新元素是在线性表的最后,时间复杂度为O(1)

  • 出队时需要将后续的所有元素向前移动,时间复杂度O(n)

问题:

如何将出队操作的时间复杂度降低到O(1)?

顺序队列的优化方案

定义front使其始终代表队头的下标

出队时将队头元素返回,且front++

定义rear使其始终代表队尾下一个元素的下标

入队时将新元素插入,且rear++

没有必要只将下标为 。的位置定义为队头!!!

顺序队列的关键状态

  • 初始状态:Iength〓〓0,   front〓〓0,    rear〓〓0;

  • 空队列状态:Iength〓〓0,    front〓〓rear;

  • 满队列状态:Iength〓〓capacity,    front〓〓rear;

循环使用队列中的空间

入队:rear 〓 (rear + 1)%capacity;

出队:front 〓 (front + 1)%capacity;

例如:假设一个数组,他有10个元素,他能取得的下标是0-9,在下标9的位置加1就是10,按照刚才的思想应该跳到第零个位置,对10对cpacity取余后刚好是0

队列的顺序优化实现

SeqQueue.h

#ifndef _SEQQUEUE_H_
#define _SEQQUEUE_H_

typedef void SeqQueue;

SeqQueue* SeqQueue_Create(int capacity);

void SeqQueue_Destroy(SeqQueue* queue);

void SeqQueue_Clear(SeqQueue* queue);

int SeqQueue_Append(SeqQueue* queue, void* item);

void* SeqQueue_Retrieve(SeqQueue* queue);

void* SeqQueue_Header(SeqQueue* queue);

int SeqQueue_Length(SeqQueue* queue);

int SeqQueue_Capacity(SeqQueue* queue);

#endif

SeqQueue.c

#include <stdio.h>
#include <malloc.h>
#include "SeqQueue.h"

typedef unsigned int TSeqQueueNode;

typedef struct _tag_SeqQueue
{
    int capacity;
    int length;
    int front;
    int rear;
    TSeqQueueNode* node;//存储空间,用于指向队列中存储的元素的内存位置。
} TSeqQueue;

SeqQueue* SeqQueue_Create(int capacity) // O(1)
{
    TSeqQueue* ret = NULL;
    
    if( capacity >= 0 )
    {
        ret = (TSeqQueue*)malloc(sizeof(TSeqQueue) + sizeof(TSeqQueueNode) * capacity);
    }
    
    if( ret != NULL )
    {
        ret->capacity = capacity;
        ret->length = 0;
        ret->front = 0;
        ret->rear = 0;
        ret->node = (TSeqQueueNode*)(ret + 1);
        //node 指针设置为指向紧接在 TSeqQueue 结构体之后的内存位置,即队列存储空间的起始位置。
        //node 指针可以直接访问队列中存储的元素,而不需要额外的指针或偏移量来计算它们的位置。
    }
    
    return ret;
}

void SeqQueue_Destroy(SeqQueue* queue) // O(1)
{
    free(queue);
}

void SeqQueue_Clear(SeqQueue* queue) // O(1)
{
    TSeqQueue* sQueue = (TSeqQueue*)queue;
    
    if( sQueue != NULL )
    {
        sQueue->length = 0;
        sQueue->front = 0;
        sQueue->rear = 0;
    }
}

int SeqQueue_Append(SeqQueue* queue, void* item) // O(1)
{
    TSeqQueue* sQueue = (TSeqQueue*)queue;
    int ret = (sQueue != NULL) && (item != NULL);
    
    ret = ret && (sQueue->length + 1 <= sQueue->capacity);
    
    if( ret )
    {
        sQueue->node[sQueue->rear] = (TSeqQueueNode)item;
        
        sQueue->rear = (sQueue->rear + 1) % sQueue->capacity;
        
        sQueue->length++;
    }
    
    return ret;
}

void* SeqQueue_Retrieve(SeqQueue* queue) // O(1)
{
    TSeqQueue* sQueue = (TSeqQueue*)queue;
    void* ret = SeqQueue_Header(queue);//获取队头元素
    
    if( ret != NULL )
    {
        sQueue->front = (sQueue->front + 1) % sQueue->capacity;
        
        sQueue->length--;
    }
    
    return ret;
}

void* SeqQueue_Header(SeqQueue* queue) // O(1) 
{
    TSeqQueue* sQueue = (TSeqQueue*)queue;
    void* ret = NULL;
    
    if( (sQueue != NULL) && (sQueue->length > 0) )
    {
        ret = (void*)(sQueue->node[sQueue->front]);
    }
    
    return ret;//第一个元素
}

int SeqQueue_Length(SeqQueue* queue) // O(1)
{
    TSeqQueue* sQueue = (TSeqQueue*)queue;
    int ret = -1;
    
    if( sQueue != NULL )
    {
        ret = sQueue->length;
    }
    
    return ret;
}

int SeqQueue_Capacity(SeqQueue* queue) // O(1)
{
    TSeqQueue* sQueue = (TSeqQueue*)queue;
    int ret = -1;
    
    if( sQueue != NULL )
    {
        ret = sQueue->capacity;
    }
    
    return ret;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "SeqQueue.h"

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char *argv[]) 
{
    SeqQueue* queue = SeqQueue_Create(6);
    int a[10] = {0};
    int i = 0;
    
    for(i=0; i<10; i++)
    {
        a[i] = i + 1;
        
        SeqQueue_Append(queue, a + i);
    }
    
    printf("Header: %d\n", *(int*)SeqQueue_Header(queue));
    printf("Length: %d\n", SeqQueue_Length(queue));
    printf("Capacity: %d\n", SeqQueue_Capacity(queue));
    
    while( SeqQueue_Length(queue) > 0 )
    {
        printf("Retrieve: %d\n", *(int*)SeqQueue_Retrieve(queue));
    }
    
    printf("\n");
    
    for(i=0; i<10; i++)
    {
        a[i] = i + 1;
        
        SeqQueue_Append(queue, a + i);

        printf("Retrieve: %d\n", *(int*)SeqQueue_Retrieve(queue));
    }
    
    SeqQueue_Destroy(queue);
    
	return 0;
}

result

Header: 1
Length: 6
Capacity: 6
Retrieve: 1
Retrieve: 2
Retrieve: 3
Retrieve: 4
Retrieve: 5
Retrieve: 6

Retrieve: 1
Retrieve: 2
Retrieve: 3
Retrieve: 4
Retrieve: 5
Retrieve: 6
Retrieve: 7
Retrieve: 8
Retrieve: 9
Retrieve: 10

链式队列的瓶颈

  • 链式队列

    • 线性表的第一个元素作为队头

    • 线性表的最后一个元素作为队尾

  • 入队的新元素是在线性表的最后,时间复杂度为O(n)

  • 出队的元素即链表的第一个元素,时间复杂度O(1)

问题:

如何将入队操作的时间复杂度降低到O(1)?

链式队列的优化方案

定义reQr指针始终指向链表中的最有一个元素

入队时将新元素通过reQr插入队尾,且将reQr指向新元素

链式队列的关键状态

空队列状态:front==NULL,rear==NULL;

关键操作

  • 入队:队尾指向新节点,队尾移到新节点,新节点指向NULL

    • rear->next=node;

    • rear =node;

    • node->next  =NULL;

  • 出队:队头移动到下一个结点处

    • front=front->next;

队列的链式优化实现

LinkQueue.h

#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_

typedef void LinkQueue;

LinkQueue* LinkQueue_Create();

void LinkQueue_Destroy(LinkQueue* queue);

void LinkQueue_Clear(LinkQueue* queue);

int LinkQueue_Append(LinkQueue* queue, void* item);

void* LinkQueue_Retrieve(LinkQueue* queue);

void* LinkQueue_Header(LinkQueue* queue);

int LinkQueue_Length(LinkQueue* queue);

#endif

LinkQueue.c

#include <malloc.h>
#include <stdio.h>
#include "LinkQueue.h"

typedef struct _tag_LinkQueueNode TLinkQueueNode;
struct _tag_LinkQueueNode
{
    TLinkQueueNode* next;
    void* item;
};//节点

typedef struct _tag_LinkQueue
{
    TLinkQueueNode* front;
    TLinkQueueNode* rear;
    int length;
} TLinkQueue;//队头、队尾、队长

LinkQueue* LinkQueue_Create() // O(1)
{
    TLinkQueue* ret = (TLinkQueue*)malloc(sizeof(TLinkQueue));
    
    if( ret != NULL )
    {
        ret->front = NULL;
        ret->rear = NULL;
        ret->length = 0;
    }
    
    return ret;
}

void LinkQueue_Destroy(LinkQueue* queue) // O(n)
{
    LinkQueue_Clear(queue);
    free(queue);
}

void LinkQueue_Clear(LinkQueue* queue) // O(n)
{
    while( LinkQueue_Length(queue) > 0 )
    {
        LinkQueue_Retrieve(queue);
    }
}

int LinkQueue_Append(LinkQueue* queue, void* item) // O(1)
{
    TLinkQueue* sQueue = (TLinkQueue*)queue;
    TLinkQueueNode* node = (TLinkQueueNode*)malloc(sizeof(TLinkQueueNode));
    int ret = (sQueue != NULL ) && (item != NULL) && (node != NULL);
    
    if( ret )
    {
        node->item = item;
        
        if( sQueue->length > 0 )
        {
            sQueue->rear->next = node;
            sQueue->rear = node;
            node->next = NULL;
        }
        else
        {
            sQueue->front = node;
            sQueue->rear = node;
            node->next = NULL;
        }
        
        sQueue->length++;
    }
    
    if( !ret )
    {
        free(node);
    }
    
    return ret;
}

void* LinkQueue_Retrieve(LinkQueue* queue) // O(1)
{
    TLinkQueue* sQueue = (TLinkQueue*)queue;
    TLinkQueueNode* node = NULL;
    void* ret = NULL;
    
    if( (sQueue != NULL) && (sQueue->length > 0) )
    {
        node = sQueue->front;//node指向队列里第一个元素
        
        sQueue->front = node->next;//front移动到下一个元素上面去
        
        ret = node->item;//取得第一个元素的数据
        
        free(node);
        
        sQueue->length--;
        
        if( sQueue->length == 0 )
        {
            sQueue->front = NULL;
            sQueue->rear = NULL;
        }
    }
    
    return ret;
}

void* LinkQueue_Header(LinkQueue* queue) // O(1)
{
    TLinkQueue* sQueue = (TLinkQueue*)queue;
    void* ret = NULL;
    
    if( (sQueue != NULL) && (sQueue->length > 0) )
    {
        ret = sQueue->front->item;
    }
    
    return ret;
}

int LinkQueue_Length(LinkQueue* queue) // O(1)
{
    TLinkQueue* sQueue = (TLinkQueue*)queue;
    int ret = -1;
    
    if( sQueue != NULL )
    {
        ret = sQueue->length;
    }
    
    return ret;
}

main

#include <stdio.h>
#include <stdlib.h>
#include "LinkQueue.h"

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char *argv[]) 
{
    LinkQueue* queue = LinkQueue_Create();
    int a[10] = {0};
    int i = 0;
    
    for(i=0; i<10; i++)
    {
        a[i] = i + 1;
        
        LinkQueue_Append(queue, a + i);
    }
    
    printf("Header: %d\n", *(int*)LinkQueue_Header(queue));
    printf("Length: %d\n", LinkQueue_Length(queue));
    
    LinkQueue_Clear(queue);
    
    while( LinkQueue_Length(queue) > 0 )
    {
        printf("Retrieve: %d\n", *(int*)LinkQueue_Retrieve(queue));
    }
    
    LinkQueue_Destroy(queue);
    
	return 0;
}

result

Header: 1
Length: 10

小结

  • 优化的顺序队列循环利用顺序空间提高出队操作的效率

  • 优化的链式队列定义rear指针指向队尾元素提高出队操作的效率

效率提高了,可是实现  的复杂性也提高了。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
磁盘调度算法是指计算机操作系统中用于控制磁盘读写的算法。常见的磁盘调度算法有 FCFS、SSTF、SCAN、C-SCAN、LOOK 和 C-LOOK 等。 下面是 Java 实现 SCAN 磁盘调度算法的示例代码: ```java import java.util.ArrayList; import java.util.Collections; public class DiskScheduling { private int currentHeadPosition; private ArrayList<Integer> requests; public DiskScheduling(int currentHeadPosition, ArrayList<Integer> requests) { this.currentHeadPosition = currentHeadPosition; this.requests = requests; } public int calculateTotalHeadMovement() { int totalHeadMovement = 0; ArrayList<Integer> sortedRequests = new ArrayList<>(requests); Collections.sort(sortedRequests); int index = sortedRequests.indexOf(currentHeadPosition); // head movement in the forward direction for (int i = index; i < sortedRequests.size(); i++) { totalHeadMovement += Math.abs(sortedRequests.get(i) - currentHeadPosition); currentHeadPosition = sortedRequests.get(i); } // head movement in the backward direction for (int i = index - 1; i >= 0; i--) { totalHeadMovement += Math.abs(sortedRequests.get(i) - currentHeadPosition); currentHeadPosition = sortedRequests.get(i); } return totalHeadMovement; } public static void main(String[] args) { int currentHeadPosition = 50; ArrayList<Integer> requests = new ArrayList<>(); requests.add(82); requests.add(170); requests.add(43); requests.add(140); requests.add(24); requests.add(16); requests.add(190); DiskScheduling diskScheduling = new DiskScheduling(currentHeadPosition, requests); int totalHeadMovement = diskScheduling.calculateTotalHeadMovement(); System.out.println("Total head movement: " + totalHeadMovement); } } ``` 在上述代码中,我们首先定义了一个 `DiskScheduling` 类,其中包含当前磁头位置和请求队列两个属性,并且实现了 `calculateTotalHeadMovement` 方法来计算总的磁头移动距离。 在 `calculateTotalHeadMovement` 方法中,我们首先对请求队列进行排序,然后找到当前磁头位置在排序后的队列中的索引。接着,我们分别计算磁头向前和向后移动的距离,并将当前磁头位置更新为当前请求的位置。最终,返回总的磁头移动距离。 在 `main` 方法中,我们定义了当前磁头位置和请求队列,并使用 `DiskScheduling` 类来计算总的磁头移动距离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值