呀哈嘍,我是結衣
今天給大家帶來的內容如標題所述,我們來設計環形隊列,雖然隊列沒有講,但是我就是想講啊。那么環形隊列現在開始。
隊列的屬性
在設計環形隊列前,我們先要了解隊列的特點(先進先出),就想現實中我們排隊一樣,先到的人先得。所以現實中銀行的取票機是可以用隊列實現的。
循環隊列
本次講解是基于leetcode的以題來講解的,貼張圖給大家介紹吧:
看完題目不知道大家有沒有思路呢?沒有的話就聽我詳細的解釋吧。
順序表or鏈表
看完題目你最先想到的實現方法是順序表還是鏈表呢?倒不是說這兩種方法只能選擇一個,而是實現難易程度問題,你覺得用順序表難還是用鏈表難呢?這里我選擇用順序表來講解,我覺得順序表簡單一些呢。
順序表實現循環隊列
在實現之前我們來看看題目要我們實現的功能吧:
typedef struct {} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {}bool myCircularQueueDeQueue(MyCircularQueue* obj) {}int myCircularQueueFront(MyCircularQueue* obj) {}int myCircularQueueRear(MyCircularQueue* obj) {}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {}bool myCircularQueueIsFull(MyCircularQueue* obj) {}void myCircularQueueFree(MyCircularQueue* obj) {}
了解完要求下面我們就要分析了。
怎樣實現循環
首先我們要定義一個頭和尾。(front和back)他們就是下標哈。
有了頭和尾。我們要把他們的初始值給多少呢,都給‘0’還是給‘-1’呢,這里我的做法是把頭和尾都給‘0’,你可能會說那如果插入了一個數,back怎么辦。哼哼,我們把back++,我們讓back指向的最后一個數的下一個,而非最后一個數,那么新的問題又來了,當出現這種情況怎么辦。
back不就出去了嗎,是的,我們要解決這個問題就要多定義一個空間,題目讓我們定義k個空間,我們就要定義k+1個空間。但是有一個空間是不存儲數據的。
在k+1個空間中始終有一個空間是不存儲數據的只有這樣才能滿足題目要求的k個空間。
這就是循環的示意圖,當前面有空間,又要插入數據時,在back的位置上插入數據后再將back = 0。
結構體的創建
typedef struct {int* data;int front;int back;int k;
} MyCircularQueue;
相信看完上面的簡介這個結構體的創建是沒有問題的吧。
函數的實現
這里的函數的實現并不是按照題目順序來進行的。因為這里的函數也可以相互的調用為后續節省時間。
初始化
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* tmp = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));tmp->data = (int*)malloc(sizeof(int) * (k + 1));tmp->front = 0;tmp->back = 0;tmp->k = k;return tmp;
}
我們先要malloc一個結構體的空間,然后再malloc tmp->data這個數組的大小,因為我們有一個空位置所以就開了k+1個的空間。還有頭和尾的賦值尾‘0’,在上面的實現循環里有講解。
隊列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {if (obj->back == obj->front){return true;}return false;
}
這個代碼就很直觀了,back == front就直接判空了。因為我們這里的back是不會有數據的,出現這種情況這樣最初的時候在那種情況就是隊列為空的情況。
隊列判滿
bool myCircularQueueIsFull(MyCircularQueue* obj) {if ((obj->back + 1) % (obj->k + 1) == obj->front){return true;}return false;
}
我們都知道當back的下一個為front的時候就是滿的時候,所以寫成back+1 == front是不是就可以了呢?不是不行,多加個判斷就可以了,但是為我們要介紹的方法取余的方法,在這種循環中就特別的好用,利用的就是當被除數比除數小的時候余數為被除數。
數據插入
寫完判空判滿的函數,我們下面就會輕松一些
要插入數據我們首先就是要判斷隊列有沒有滿,如果滿了就不能插入,并且返回false
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}obj->data[obj->back] = value;if (obj->back == obj->k)obj->back = 0;elseobj->back++;return true;
}
除了判滿,我們還要小心當back在最后的情況下,在最后的話如果我們還是back++的話,back就出界了,后果可是很嚴重的,后面返回為數據的時候就可能會訪問野指針。導致程序崩潰。所以我們就要加判斷呢。
數據刪除
刪除數據的時候我們要知道隊列不能為空,為空就不能刪除,返回false。
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}if (obj->front == obj->k)obj->front = 0;elseobj->front++;return true;
}
當然后面判斷和前面插入的時候相似,front在最后的時候就不能++了,要讓他等于0。
返回頭
不能為空
int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->data[obj->front];
}
返回尾
不能為空的同時還要注意,back為0的時候,如果你還 減減 不久變成負數了嗎,這樣肯定是不行的,所以我們要再加一個判斷咯。
int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}if (obj->back == 0)return obj->data[obj->k];elsereturn obj->data[obj->back - 1];
}
銷毀
void myCircularQueueFree(MyCircularQueue* obj) {free(obj);obj = NULL;
}
銷毀就是十分的簡單
循環隊列代碼
typedef struct {int* data;int front;int back;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* tmp = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));tmp->data = (int*)malloc(sizeof(int) * (k + 1));tmp->front = 0;tmp->back = 0;tmp->k = k;return tmp;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {if (obj->back == obj->front){return true;}return false;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {if ((obj->back + 1) % (obj->k + 1) == obj->front){return true;}return false;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}obj->data[obj->back] = value;if (obj->back == obj->k)obj->back = 0;elseobj->back++;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}if (obj->front == obj->k)obj->front = 0;elseobj->front++;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->data[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}if (obj->back == 0)return obj->data[obj->k];elsereturn obj->data[obj->back - 1];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj);obj = NULL;
}
完