循環隊列詳解!!c 語言版本(兩種方法)雙向鏈表和數組法!!

目錄

1.什么是循環隊列

2.循環隊列的實現(兩種方法)

第一種方法 數組法

1.源代碼

2.源代碼詳解!!

1.創造隊列空間和struct變量

2.隊列判空

3.隊列判滿(重點)

4.隊列的元素插入

5.隊列的元素刪除

6.隊列的頭元素

7.隊列的尾元素(重點)

8.隊列空間的釋放

第二種方法 雙向循環鏈表法

1.源代碼

2.源代碼詳解!!

?1.創造哨兵位 和 struct 變量

2. 隊列判空和隊列判滿

3.元素插入

4.元素刪除

5.頭元素

6.尾元素

7.空間的釋放

三關于這兩種方法的差距?

3.循環隊列有什么用


1.什么是循環隊列

首先關于循環隊列,你得先知道什么是隊列,詳情可以去看看我的另一篇博客

數據結構 棧與隊列詳解!!-CSDN博客

這里簡單的說一下,隊列就是字面意思,你在銀行排隊,你不是什么VIP只是普通人,那你先排隊那你就先得服務,隊列就是 你先插入的數字(入隊),就得先放出(出隊)。大致就是這個意思。

而循環隊列呢?他的原理也是隊列,但和隊列不同的地方在于,他的空間是固定的。在一個空間里進行重復的入隊出隊。

這是一張循環隊列的大致圖。

其中和隊列一樣有兩個指針指向頭和尾。添加實在尾針處加,而刪除就是將隊首的元素刪除。為什么循環呢?就是在你的尾指針走到最后一格的時候(就是總共有5個空間,你的尾指針已經走到5并且已經插入數據)你的隊列就滿了,這時候循環隊列已經不能插入數據,只能刪除數據。

刪除數據就是把你的頭指針的元素刪除,如果刪除走到尾指針,頭和尾在一個位置了。

所以綜上可以抽象出,當你的頭和尾指針走到一起的時候,這時候隊列是空。

但你的尾走到底 頭元素的上一個,那就說明你的隊列已充滿。

當隊尾追到了隊頭那就是滿,當隊頭追到了隊尾那就是空

2.循環隊列的實現(兩種方法)

第一種方法 數組法

數組法采用的是 多開辟一個空間,用來做假溢出,判斷隊列是否充滿,當然還有一種定size法,大家可以討論一下!!

1.源代碼

typedef struct {int* a;int front;int rear;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->a = (int*)malloc(sizeof(int) * (k + 1));obj->k = k;obj->front = 0;obj->rear = 0;return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->rear;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->rear + 1) % (obj->k + 1) == obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}obj->rear %= obj->k + 1;obj->a[obj->rear] = value;obj->rear++;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}obj->front %= obj->k + 1;obj->front++;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->a[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->a[obj->rear - 1];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);obj->a = NULL;free(obj);obj = NULL;
}

2.源代碼詳解!!

1.創造隊列空間和struct變量
typedef struct {int* a;int front;int rear;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->a = (int*)malloc(sizeof(int) * (k + 1));obj->k = k;obj->front = 0;obj->rear = 0;return obj;
}

?關于 匿名typedef struc 就是定義了一個類型名就叫做MyCircularQueue。

里面的元素第一個用來開辟空間,第二個定義頭指針,第三個定義尾指針,第4個就是空間的大小。(這里的指針并不是真的指針,而是對于一個數組來說的下標)

關于 創造隊列的空間,首先既然他是一個結構體變量,那得先給他開辟一個空間用來給里面的元素地址存儲

第二因為你是要在一個數組里進行循環隊列,那你就得開辟一塊數組的空間。(k+1是因為這里采用的是多開辟一格進行判滿)。

第三給結構體里的變量賦初始值。?

2.隊列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->rear;
}

?關于隊列的判空,就是兩個隊頭和隊尾指針如果重疊時,兩者應該是滿了或者空了的關系,所以兩者的判斷表達式不能相同,所以下面的判滿得想到另一個表達式才能保證隊列不報錯(當隊尾追到了隊頭那就是滿,當隊頭追到了隊尾那就是空)

3.隊列判滿(重點)
bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->rear + 1) % (obj->k + 1) == obj->front;
}

?這里關于隊列的判滿,采用的是這一串表達式的原因是,假設這時你開辟的有元素的空間是3,那現在的K就是3,但由于采用的是多開辟一格來判滿的方法,那就是代表了開辟了4個空間。

4個空間對應的下標為0 1 2 3.那為什么要改兩個指針rear和k加一呢?假設這時你的rear和front 都還在下標為 0 的位置,如果你不給 rear 加一 ,那你得出的結果就是 0 % 4 == 0.

這樣導致的后果就是和 上面判空的條件 是一樣的 0 == 0.會導致插入操作錯誤,會一直判滿從而不能進行插入。

而如果你給 rear 和 k 都加一 就變成了 1 % 4 == 1.而這時 front 是 = 0的

就不會出現誤判的情況。只有當你的 rear 走到你多開辟的那一塊空間后才會出現判滿的情況。(這只是當 front 沒變的情況下的討論,關于 front改變后的討論,你可以構思一下大致相同。)

4.隊列的元素插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}obj->rear %= obj->k + 1;obj->a[obj->rear] = value;obj->rear++;return true;
}

?隊列的插入就是在rear的 下標初進行插入。首先你要進行判滿,是因為循環隊列如果滿了以后就不會在追加新的元素,就很像游戲里的背包,滿了以后就不能獲得物品。為什么要讓rear % k+1呢?因為當你的元素走到最后一格的下一格,這時候是越界訪問的。因為是循環隊列,所以可以把他重新變到下標為 0 的位置。

我們的rear 的位置,每次都會加1 所以每次是在rear 的位置插入數據。

5.隊列的元素刪除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}obj->front++;obj->front %= obj->k + 1;return true;
}

?元素刪除,首先如果你的隊列為空那就刪除不了了,返回false。

如果不為空,那front++ 即可,并且和 rear 一樣要 % k+1,是為了使隊列變成循環。如果不這樣就會越界訪問并且與題意不符(循環)。

要先++ 的原因是,如果你后++ 就會導致,如果此時你的front已經處于最后一個位置,

后++就會導致取頭元素時 越界。如果先++ ,然后在%k+1 這樣在出了最后一格以后可以及時,變回0 下標,不會越界。

6.隊列的頭元素
int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->a[obj->front];
}

?返回頭元素相較簡單,只要返回此時front 位置的元素即可。

7.隊列的尾元素(重點)
int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}//if (obj->rear == 0)//{//	return obj->a[obj->k];//}//else//{//	return obj->a[obj->rear - 1];//}return obj->a[((obj->rear - 1) + (obj->k + 1)) % (obj->k + 1)];
}

?

?和頭元素的不同點在于,隊尾元素不能直接取隊尾的下標。

因為由前面的插入操作我們可以知道,在rear位置插入數據后,rear是要進行++的,此時在rear下標里是沒有元素的,隊尾實際上是在rear 的前一個下標的位置。

但如果你只用 obj->a[obj->rear - 1] 來訪問的話(如果你的rear是先%k +1,再++是可以的),但如果是先++,再%就不可以了,你可以思考一下當rear == 0,的時候,那你訪問的就是obj->[-1] 那就是未知訪問,錯誤。有兩種方法。

第一種,判斷當rear == 0的時候,你可以直接選擇返回 最后一格 obj->k 的元素,

rear != 0時就返回 obj->rear - 1的下標的元素。

第二種,直接判斷不用 if else,obj->a[((obj->rear - 1) + (obj->k + 1)) % (obj->k + 1)]。

這串表達式的意思就是,因為我們是要返回 rear - 1下標位置的元素,所以我們 rear - 1;

而 加上 k +1的原因是 在 rear -1 的位置下,你加+ 上這個循環隊列的大小,那你還是在原地的,這里是為了防止特殊情況 rear = -1 時,rear = -1 原本是想訪問 隊列最后一個空間的元素,但等于 - 1訪問不到,所以 rear = -1 加上 一個 k+1,其實就是等于 k ,k % k +1 = k。所以訪問最后一個位置的元素。

8.隊列空間的釋放
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);obj->a = NULL;free(obj);obj = NULL;
}

?將開辟的空間釋放即可,但要先釋放obj->a 處的空間。

第二種方法 雙向循環鏈表法

1.源代碼

typedef struct MyCircularQueue {struct MyCircularQueue* prev;struct MyCircularQueue* next;int val;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* phead = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));phead->next = phead;phead->prev = phead;phead->val = -1;phead->k = k;return phead;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {if (obj->next == obj){return true;}return false;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {MyCircularQueue* cur = obj->next;int size = 1;while (cur != obj){cur = cur->next;size++;}return size == obj->k;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}MyCircularQueue* newnode = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->val = value;MyCircularQueue* prev = obj->prev;newnode->prev = prev;newnode->next = obj;prev->next = newnode;obj->prev = newnode;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}MyCircularQueue* Next = obj->next;obj->next = Next->next;Next->next->prev = obj;free(Next);Next = NULL;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->next->val;
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->prev->val;
}void myCircularQueueFree(MyCircularQueue* obj) {MyCircularQueue* cur = obj->next;while (cur != obj){MyCircularQueue* del = cur;cur = cur->next;free(del);del = NULL;}free(obj);obj = NULL;
}

2.源代碼詳解!!

?1.創造哨兵位 和 struct 變量
typedef struct MyCircularQueue {struct MyCircularQueue* prev;struct MyCircularQueue* next;int val;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* phead = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));phead->next = phead;phead->prev = phead;phead->val = -1;phead->k = k;return phead;
}

?我們定義一個雙鏈表的struct 結構。

然后再定義一個頭結點,并且將 變量賦值。

2. 隊列判空和隊列判滿
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {if (obj->next == obj){return true;}return false;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {MyCircularQueue* cur = obj->next;int size = 0;while (cur != obj){cur = cur->next;size++;}return size == obj->k;
}

?相較于 數組的循環隊列,雙鏈表的判空判滿就比較簡單。空就是 只有一個哨兵位就是空。

滿就是用一個size ++ 遍歷鏈表,后如果size == obj->k 就是滿。size 必須從 0?開始。

因為不從0?開始 ,總體隊列會少一個空間。

3.元素插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}MyCircularQueue* newnode = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->val = value;MyCircularQueue* prev = obj->prev;newnode->prev = prev;newnode->next = obj;prev->next = newnode;obj->prev = newnode;return true;
}

元素的插入就是雙鏈表的尾插。?

4.元素刪除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}MyCircularQueue* Next = obj->next;obj->next = Next->next;Next->next->prev = obj;free(Next);Next = NULL;return true;
}

元素刪除就是雙鏈表的頭刪。?

5.頭元素
int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->next->val;
}

返回 頭元素?

6.尾元素
int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->prev->val;
}

返回尾元素?

7.空間的釋放
void myCircularQueueFree(MyCircularQueue* obj) {MyCircularQueue* cur = obj->next;while (cur != obj){MyCircularQueue* del = cur;cur = cur->next;free(del);del = NULL;}free(obj);obj = NULL;
}

雙鏈表的銷毀。?

三關于這兩種方法的差距?

首先在數組 方法中,我們并沒有采用循環來執行,這樣的時間復雜度是0(1),而雙向鏈表中有多處我們采用了while 循環所以時間復雜度是 o(n)。

數組寫法中 坑很多需要很多思考,但是總體代碼量是偏少的。

而鏈表雖然簡單明了,但代碼量是稍微多一點。需要細心檢查。

3.循環隊列有什么用

應用場景廣泛:由于其高效的插入和刪除操作、空間利用率高以及能夠動態調整隊列大小的特性,循環隊列在許多領域都有廣泛的應用,如操作系統中的任務調度、通信協議中的數據包處理、線程池中的線程管理等。所以循環隊列是比較重要的

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160778.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160778.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160778.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

GIT實踐與常用命令---回退

實踐場景 場景1 回退提交 在日常工作中,我們可能會和多個同事在同一個分支進行開發,有時候我們可能會出現一些錯誤提交,這些錯誤提交如果想撤銷,可以有兩種解決辦法:回退( reset )、反做(revert) keywords:reset、rev…

2023軟件測試的4個技術等級,你在哪個級別?

最近,我們討論了軟件測試工程的的分級,大家都貢獻了自己的想法,對于大家來說,軟件測試人的分級其實也代表了我們的進階方向,職業發展。總體來說,測試工程師未來發展有三個方向: 技術精英 行業專…

層次分析法--可以幫助你做決策的簡單算法

作用 層次分析法是一個多指標的評價算法,主要用來在做決策時,給目標的多個影響因子做權重評分。特別是那些需要主觀決策的、或者需要用經驗判斷的決策方案,例如: 買房子(主觀決策)選擇旅游地(…

android11 申請所有文件訪問權限

Android 11 引入了強制執行分區存儲的限制,導致應用默認不能訪問外部文件。 針對以前涉及較多文件的操作,可采用申請所有文件訪問權限的方式來解決這一問題,實現方式如下。 (雖然這樣做安全性低,官方并不推薦這樣&…

preplexity test

Preplexity test can use model claude and gpt-4, feel speed is ok and only for $10 with coupon (below give a link). Feel ok to try reference link: https://perplexity.ai/pro?referral_codeV6UOS5PH

Shell判斷:模式匹配:case(三)

系統管理工具箱 1、需求:Linux提供的豐富的管理命令,用戶管理,內存管理,磁盤管理,進程管理,日志管理,文件管理,軟件管理,網絡管理等等數十個工具包。如果你能通過shell編…

【代碼隨想錄】算法訓練計劃30

【代碼隨想錄】算法訓練計劃30 1、51. N 皇后 按照國際象棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。 n 皇后問題 研究的是如何將 n 個皇后放置在 nn 的棋盤上,并且使皇后彼此之間不能相互攻擊。 給你一個整數 n ,…

微信API:探究Android平臺下Hook技術的比較與應用場景分析

微信API:探究Android平臺下Hook技術的比較與應用場景分析 正文: 在Android平臺開發中,Hook技術是一種常用的技術手段,用于在運行時修改應用程序的行為。下面對一些常見的Hook技術進行比較,并分析它們的適用場景和優缺…

信息系統項目管理師論文

軟考官網:中國計算機技術職業資格網 (ruankao.org.cn) 2020年 2020年下半年試題一:論信息系統項目的成本管理 2019年 2019年下半年試題一:論信息系統項目的整體管理 2019年下半年試題二:論信息系統項目的溝通管理

PCI5565反射內存網技術的應用研究

隨著嵌入式與通信技術的發展,數控系統經歷了由傳統的單處理器的集中式體系結構到開放式體系結構,再到多處理器的分布式數控系統體系結構的發展過程。分布式數控系統以高精、高速的加工特征為發展核心,同時以達到異構網絡間信息的無縫融合&…

分布式鎖3: zk實現分布式鎖

一 zk 實現分布式鎖 1.1 zk分布式操作命令 1.指令: ls / get /zookeeper create /aa "test" delete /aa set /aa "test1" 2..znode節點類型: 永久節點:create /pa…

優秀智慧園區案例 - 上海世博文化公園智慧園區,先進智慧園區建設方案經驗

一、項目背景 世博文化公園是上海的綠色新地標,是生態自然永續、文化融合創新、市民歡聚共享的大公園。作為世博地區的城市更新項目,世博文化公園的建設關乎上海城市風貌、上海文化展示、城市生態環境、市民游客體驗、上海服務品牌等,被賦予…

依托數據、平臺、知識增強等優勢 夸克大模型大幅降低問答幻覺率

“大模型時代,夸克有巨大機會創造出革新性搜索產品。”11月22日,夸克大模型公布了其面向搜索、生產力工具和資產管理助手的大模型技術布局。數據顯示,夸克千億級參數大模型登頂C-Eval和CMMLU兩大權威榜單,夸克百億級參數大模型同樣…

電大搜題——讓學習變得輕松高效

作為一名現代學者,您一定時刻關注著教育領域的進展和創新。今天,我將向大家介紹一個名為“電大搜題”的神奇工具,它將為您的學習之路帶來一場完美的革命。 在快節奏的現代社會中,學習已經成為每個人追求成功的必經之路。然而&…

【數據結構】動態順序表詳解

目錄 1.順序表的概念及結構 2.動態順序表的實現 2.1創建新項目 2.2動態順序表的創建 2.3接口的實現及測其功能 2.3.1初始化 2.3.2尾插 2.3.3頭插 2.3.4尾刪&頭刪 2.3.5打印&從任意位置插入 2.3.6刪除任意位置的數據 2.3.7查找 2.3.8銷毀順序表 3.結語 He…

【交易誤區】初學者常犯的MT4外匯交易錯誤有哪些?

作為初學者,踏入外匯交易市場時,往往會陷入一些常見的誤區,導致交易效果不佳甚至遭受損失。在本文中,我將列舉并解釋五個初學者常見的MT4外匯交易錯誤,并提供相應的解決方案,幫助您避免這些錯誤&#xff0c…

java項目之社區互助平臺(ssm+vue)

項目簡介 社區互助平臺實現了以下功能: 1、一般用戶的功能及權限 所謂一般用戶就是指還沒有注冊的過客,他們可以瀏覽主頁面上的信息。但如果有中意的社區互助信息時,要登錄注冊,只有注冊成功才有的權限。2、管理員的功能及權限 用戶信息的添…

react大文件上傳

目錄 大文件上傳優點: 大文件上傳缺點: 大文件上傳原理: 為什么要用md5 實現流程: 部分代碼1: 部分代碼2:? 大文件上傳優點: 文件太大分片上傳能加快上傳速度,提高用戶體驗能斷點續傳 如果上次上傳失敗…

簡單工程模式

代碼實現 //simpleFactory.h #ifndef _SimpleFactory_H_ #define _SimpleFactory_H_#include <iostream> #include <exception> #include <string>using namespace std;class Operation { protected:double _numberA 0;double _numberB 0; public:Operat…

udp通信socket關閉后,緩存不清空

udp通信socket關閉后&#xff0c;緩存不清空 udp通信socket關閉后&#xff0c;緩存不清空如何清空udp緩存 udp通信socket關閉后&#xff0c;緩存不清空 關閉一個 UDP socket 連接后&#xff0c;底層接收緩沖區中存儲的數據不會被清空。實際上&#xff0c;關閉 socket 連接并不…