C语言链表实现双端队列

 

6-1 Deque (25 point(s))

A "deque" is a data structure consisting of a list of items, on which the following operations are possible:

  • Push(X,D): Insert item X on the front end of deque D.
  • Pop(D): Remove the front item from deque D and return it.
  • Inject(X,D): Insert item X on the rear end of deque D.
  • Eject(D): Remove the rear item from deque D and return it. Write routines to support the deque that take O(1) time per operation.

Format of functions:

Deque CreateDeque();
int Push( ElementType X, Deque D );
ElementType Pop( Deque D );
int Inject( ElementType X, Deque D );
ElementType Eject( Deque D );

where Deque is defined as the following:

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};

Here the deque is implemented by a doubly linked list with a header. Front and Rear point to the two ends of the deque respectively. Front always points to the header. The deque is empty when Front and Rear both point to the same dummy header. Note: Push and Inject are supposed to return 1 if the operations can be done successfully, or 0 if fail. If the deque is empty, Pop and Eject must return ERROR which is defined by the judge program.

Sample program of judge:

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

#define ElementType int
#define ERROR 1e5
typedef enum { push, pop, inject, eject, end } Operation;

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};
Deque CreateDeque();
int Push( ElementType X, Deque D );
ElementType Pop( Deque D );
int Inject( ElementType X, Deque D );
ElementType Eject( Deque D );

Operation GetOp();          /* details omitted */
void PrintDeque( Deque D ); /* details omitted */

int main()
{
    ElementType X;
    Deque D;
    int done = 0;

    D = CreateDeque();
    while (!done) {
        switch(GetOp()) {
        case push: 
            scanf("%d", &X);
            if (!Push(X, D)) printf("Memory is Full!\n");
            break;
        case pop:
            X = Pop(D);
            if ( X==ERROR ) printf("Deque is Empty!\n");
            break;
        case inject: 
            scanf("%d", &X);
            if (!Inject(X, D)) printf("Memory is Full!\n");
            break;
        case eject:
            X = Eject(D);
            if ( X==ERROR ) printf("Deque is Empty!\n");
            break;
        case end:
            PrintDeque(D);
            done = 1;
            break;
        }
    }
    return 0;
}

/* Your function will be put here */

Sample Input:

Pop
Inject 1
Pop
Eject
Push 1
Push 2
Eject
Inject 3
End

Sample Output:

Deque is Empty!
Deque is Empty!
Inside Deque: 2 3

 

 首先是初始化函数,返回一个空双端队列

注意,这个双端队列带有一个头节点

Push操作将数据加入到双端队列头部

Pop操作弹出双端队列头部元素

Inject操作在尾部插入一个元素

Eject操作弹出尾部元素

双向链表操作要仔细细致。

/* Your function will be put here */
Deque CreateDeque()
{
	Deque deque = (Deque)malloc(sizeof(struct DequeRecord));
	PtrToNode temp = (PtrToNode)malloc(sizeof(struct Node));
	temp->Last = NULL;
	temp->Next = NULL;
	deque->Front = temp;
	deque->Rear = temp;
	return deque;

}

int Push(ElementType X, Deque D)
{
	PtrToNode temp;
	temp = (PtrToNode)malloc(sizeof(struct Node));
	if (!temp)
		return 0;
	temp->Element = X;
	// 空队列情况
	if (D->Front == D->Rear)
	{
		D->Front->Next = temp;
		temp->Last = D->Front;
		temp->Next = NULL;
		D->Rear = temp;
		return 1;
	}
	// 一般情况
	temp->Next = D->Front->Next;
	temp->Last = D->Front;
	D->Front->Next->Last = temp;
	D->Front->Next = temp;
	return 1;
	
}

ElementType Pop(Deque D)
{
	
	PtrToNode temp;
	int X;
	// 空队列
	if (D->Front == D->Rear)
		return ERROR;

	temp = D->Front->Next;
	X = temp->Element;
	// 如果只有一个节点的情况
	if (D->Front->Next == D->Rear)
	{
		D->Rear = D->Front;
		D->Rear->Next = NULL;
		free(temp);
		return X;
	}
	// 一般情况
	temp->Next->Last = D->Front;
	D->Front->Next = temp->Next;
	free(temp);
	return X;
}

// 末尾添加一个元素
int Inject(ElementType X, Deque D)
{
	PtrToNode temp;
	temp = (PtrToNode)malloc(sizeof(struct Node));
	if (!temp)
		return 0;
	temp->Element = X;
	// 空队列
	if (D->Front == D->Rear)
	{
		D->Front->Next = temp;
		temp->Last = D->Front;
		D->Rear = temp;
		return 1;
	}
	D->Rear->Next = temp;
	temp->Next = NULL;
	temp->Last = D->Rear;
	D->Rear = temp;
	return 1;
}

// 从末尾移除一个元素
ElementType Eject(Deque D)
{
	PtrToNode temp;
	int X;
	if (D->Front == D->Rear)
		return ERROR;

	temp = D->Rear;
	X = temp->Element;
	D->Rear = temp->Last;
	D->Rear->Next = NULL;
	free(temp);
	return X;
}

 

<think>我们正在讨论C语言中的双端队列(Deque)。双端队列是一种允许在队列两端进行插入和删除操作的数据结构。结合引用内容,特别是引用[3]和[4]中提到了双端队列实现和应用,我们可以总结出以下特点和使用方法。###双端队列的特点1.**两端操作**:双端队列允许在队列的前端(front)和后端(rear)进行插入(enqueue)和删除(dequeue)操作。这使得它比普通队列更灵活。2.**先进先出与后进先出**:双端队列结合了队列(FIFO)和栈(LIFO)的特性,可以根据需要选择从哪一端操作。3.**动态大小**:双端队列通常使用链表或动态数组实现,因此其大小可以动态调整。4.**高效操作**:在链表实现中,双端队列的插入和删除操作的时间复杂度为$O(1)$。###C语言实现双端队列在C语言中,我们可以使用链表或数组来实现双端队列。以下是一个使用双向链表实现双端队列的示例。####结构定义```ctypedefstructNode{intdata;structNode*next;structNode*prev;}Node;typedefstructDeque{Node*front;Node*rear;}Deque;```####初始化双端队列```cDeque*createDeque(){Deque*deque=(Deque*)malloc(sizeof(Deque));deque->front=NULL;deque->rear=NULL;returndeque;}```####前端插入```cvoidinsertFront(Deque*deque,intdata){Node*newNode=(Node*)malloc(sizeof(Node));newNode->data=data;newNode->prev=NULL;newNode->next=deque->front;if(deque->front==NULL){//队列为空deque->rear=newNode;}else{deque->front->prev=newNode;}deque->front=newNode;}```####后端插入```cvoidinsertRear(Deque*deque,intdata){Node*newNode=(Node*)malloc(sizeof(Node));newNode->data=data;newNode->next=NULL;newNode->prev=deque->rear;if(deque->rear==NULL){//队列为空deque->front=newNode;}else{deque->rear->next=newNode;}deque->rear=newNode;}```####前端删除```cintdeleteFront(Deque*deque){if(deque->front==NULL){printf("Dequeisempty\n");return-1;}Node*temp=deque->front;intdata=temp->data;deque->front=deque->front->next;if(deque->front==NULL){deque->rear=NULL;}else{deque->front->prev=NULL;}free(temp);returndata;}```####后端删除```cintdeleteRear(Deque*deque){if(deque->rear==NULL){printf("Dequeisempty\n");return-1;}Node*temp=deque->rear;intdata=temp->data;deque->rear=deque->rear->prev;if(deque->rear==NULL){deque->front=NULL;}else{deque->rear->next=NULL;}free(temp);returndata;}```####检查队列是否为空```cintisEmpty(Deque*deque){returndeque->front==NULL;}```###使用方法1.**初始化**:使用`createDeque`函数创建一个空的双端队列。2.**插入元素**:根据需要选择从前端插入(`insertFront`)或后端插入(`insertRear`)。3.**删除元素**:同样可以选择从前端删除(`deleteFront`)或后端删除(`deleteRear`)。4.**检查空队列**:在删除元素前,最好检查队列是否为空,以避免错误。###应用场景双端队列常用于需要同时支持队列和栈操作的场景,例如:-滑动窗口算法(如求最大值、最小值)-撤销操作(支持从两端操作)-任务调度(某些任务可能需要优先处理,即从前端插入)###性能分析-**时间复杂度**:插入和删除操作的时间复杂度为$O(1)$。-**空间复杂度**:每个元素需要额外的指针空间(双向链表),因此空间复杂度为$O(n)$。###注意事项-在使用双端队列时,要注意内存管理,避免内存泄漏。-在删除操作后,要正确处理指针,避免悬垂指针。希望这些信息能帮助你理解C语言双端队列的特点和使用方法。如果你有更多问题,请随时提问。§§相关问题§§1.双端队列和普通队列在性能上有哪些区别?2.如何用数组实现双端队列?3.双端队列在算法中有哪些典型应用?</think>### C语言双端队列的特点及使用方法 #### 一、双端队列的核心特点 1. **两端操作** 允许在队列**前端(front)和后端(rear)** 同时进行插入(enqueue)和删除(dequeue)操作,突破普通队列的FIFO限制[^3]。 2. **动态数据结构** 通常基于**双向链表**实现,节点结构包含: ```c typedef struct Node { int data; struct Node* prev; // 前驱指针 struct Node* next; // 后继指针 } Node; ``` 3. **高效时间复杂度** - 插入/删除操作:$O(1)$ - 空间复杂度:$O(n)$(需存储额外指针)[^4] 4. **灵活的应用场景** 支持栈(LIFO)和队列(FIFO)两种操作模式,适用于滑动窗口算法、撤销操作等场景[^5]。 --- #### 二、C语言实现关键代码 ##### 结构定义 ```c typedef struct Deque { Node* front; // 队首指针 Node* rear; // 队尾指针 int size; // 当前元素数量 } Deque; ``` ##### 核心操作实现 1. **前端插入** ```c void insertFront(Deque* dq, int data) { Node* newNode = createNode(data); if (dq->size == 0) { dq->front = dq->rear = newNode; } else { newNode->next = dq->front; dq->front->prev = newNode; dq->front = newNode; } dq->size++; } ``` 2. **后端删除** ```c int deleteRear(Deque* dq) { if (dq->size == 0) return -1; // 空队列 Node* temp = dq->rear; int data = temp->data; dq->rear = dq->rear->prev; if (dq->rear) dq->rear->next = NULL; else dq->front = NULL; // 队列变空 free(temp); dq->size--; return data; } ``` 3. **获取队首/队尾元素** ```c int getFront(Deque* dq) { return dq->front ? dq->front->data : -1; } int getRear(Deque* dq) { return dq->rear ? dq->rear->data : -1; } ``` --- #### 三、典型使用场景 1. **滑动窗口最大值** 用双端队列维护窗口内元素的索引,高效获取最大值: ```c void maxSlidingWindow(int arr[], int n, int k) { Deque* dq = createDeque(); for (int i = 0; i < n; i++) { // 移除超出窗口的元素 while (!isEmpty(dq) && getFront(dq) <= i - k) deleteFront(dq); // 移除尾部小于当前值的元素 while (!isEmpty(dq) && arr[getRear(dq)] <= arr[i]) deleteRear(dq); insertRear(dq, i); if (i >= k-1) printf("%d ", arr[getFront(dq)]); } } ``` 2. **撤销操作栈** 同时支持正向操作和撤销操作: ```c // 添加新操作 insertFront(history, new_action); // 撤销操作 Action last = deleteFront(history); ``` --- #### 四、与普通队列的对比 | **特性** | 普通队列 | 双端队列 | |----------------|------------------|-------------------| | 插入位置 | 仅队尾 | 队首/队尾均可 | | 删除位置 | 仅队首 | 队首/队尾均可 | | 实现复杂度 | 单向链表 | 双向链表 | | 适用算法 | BFS等 | 滑动窗口/回文检测| > 提示:实际应用中需注意**内存管理**,每次删除节点后及时释放内存[^4]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值