1.棧
棧的核心是LIFO(Last In First Out),即后進先出
出棧和入棧只會對棧頂進行操作,棧底永遠為0
1.1概念
棧底(bottom):棧結構的首部
棧頂(top):棧結構的尾部
出棧(Pop):結點從棧頂刪除
進棧(Push):結點在棧頂位置插入
取棧頂內容操作(Top):取棧頂結點數據值的操作
空棧:當棧中結點數為0時
1.2操作
如果是入棧,要將入棧元素賦值給棧數組,再將棧頂上移一位;
出棧時要先將棧頂下移一位,再將棧頂元素賦值給引用
2.隊列
普通隊列效率低下,出隊的時候頭數組要清空,后面的元素都要向前移動
環形隊列判空判滿不能用head = tail判斷,因為隊滿和隊空都滿足這種條件
這里只討論環形隊列
隊列的核心思想是FIFO(First In First Out),即先入先出
2.1概念
隊首(front):隊列首元素
隊尾(rear):隊列尾元素
出隊(Dequeue):把結點從隊首刪除的操作
入隊(Enqueue):在隊尾位置插入的操作
2.2操作
入隊(新增元素)必須從隊尾加入,出隊(刪除元素)必須從隊首出去
3.線性表
3.1概念:
線性表:是零個或多個數據元素的有限序列
需要注意的是:
1. 線性表中的數據元素是有順序的
2. 線性表中數據元素的數量是有限的
3. 線性表中數據元素個數可以為零
如圖按下標排序的數據元素就可以認為是一個線性表:
線性表長度:線性表中數據元素個數稱為線性表的長度, 長度為零的表稱為空表.
前驅:某個數據元素的前一個數據元素稱為該元素的前驅, 每個元素至多有一個前驅.
后繼:某個數據元素的后一個數據元素稱為該元素的后繼, 每個元素至多有一個后繼.
線性表的兩種存儲結構:
根據線性表中數據元素在內存中的存儲方式可以用順序存儲結構或鏈式存儲結構實現線性表.
用順序存儲結構即使用數組來表示線性表元素之間的順序和對應關系. 用鏈式存儲結構即使用指針來表示線性表元素之間的順序和對應關系, 我們稱其為鏈表.
3.2順序表
核心操作
3.2.1插入元素:
先判斷索引是否合法
索引如果合法
先對插入元素的位置后面進行移動,並且是倒序移動,因為順序移動順序表前面的元素值會一直覆蓋掉后面的
假如按順序插入,如
list1->ListInsert(0, &e1);
list1->ListInsert(1,&e2);
是不會進入for循環的,只有插入同一個位置,才會向后移動元素
移動完元素后進行賦值操作
長度++
PS:這里m_pList[k+1] = m_pList[k]寫成m_pList[k] = m_pList[k-1]效果是一樣的,那么上面k就要等於m_iLength = m_iLength,不然順序表尾元素會丟失掉
bool List::ListInsert(int i, int *Elem) { if (i<0 || i>m_iLength) { return false; } else { //先在后面進行移動 for (int k = m_iLength - 1; k >= i; k--) { m_pList[k + 1] = m_pList[k]; } //插入元素 m_pList[i] = *Elem; m_iLength++; return true; } }
3.2.2刪除元素
對索引判斷是否合法
然后先將要刪的元素賦值給Elem所指向空間的值,后賦值,元素已經被刪掉,獲取不到了
從前往后移動,從后面往前移動會造成元素值的丟失
移動完畢
長度--
bool List::ListDelete(int i, int *Elem) { if (i<0 || i>=m_iLength) { return false; } else { *Elem = m_pList[i]; //先在前面進行移動,從后面開始移動會造成值的替代 //k=i+1對應着k-1,若k=i,k <= m_iLength-1,m_pList[k] = m_pList[k+1]; for (int k = i+1; k <= m_iLength; k++) { m_pList[k-1] = m_pList[k]; } m_iLength--; return true; } }
3.3鏈表
頭結點:不算在數組索引中,數據域為空,指針域指向開始的結點
核心操作:
3.3.1插入元素:
判斷參數i的合法性
i不能小於0,i不能大於鏈表長度,i等於鏈表長度代表可以尾元素后插入
currentNode保存頭結點
聲明newNode
pNode數據域賦值給newNode數據域
新結點指向原來結點的下一個結點
原來的結點指向新結點
bool List::ListInsert(int i, Node *pNode) { if (i<0 || i>m_iLength) { return false; } Node *currentNode = m_pList; for (int k = 0; k < i; k++) { currentNode = currentNode->next; } Node *newNode = new Node; if (newNode == NULL) //判斷申請的結點內存是否為空 { return false; } else { newNode->data = pNode->data; newNode->next = currentNode->next; currentNode->next = newNode; return true; } }
3.3.2刪除元素:
判斷i是否合法,索引最大是m_iLength-1,和循環條件k<i對應
currentNode保存頭指針
currentNodeBefore置空
currentNodeBefore指向currentNode的下一個結點的指針域,這樣currentNode就可以被刪除釋放了
pNode數據域接收當前結點的數據域
刪除當前結點並置空
bool List::ListDelete(int i, Node *pNode) { if (i<0 || i>=m_iLength) { return false; } Node *currentNode = m_pList; Node *currentNodeBefore = NULL; for (int k = 0; k <= i; k++) { currentNodeBefore = currentNode; currentNode = currentNode->next; } currentNodeBefore->next = currentNode->next; pNode->data = currentNode->data; delete currentNode; currentNode = NULL; m_iLength--; return true; }
4.樹
4.1概念
- 孩子:子結點
- 雙親:父節點
- 度:有多少個子結點
- 有序樹:固定的排列的樹
- 無序樹:排列與順序無關的樹
- 二叉樹:所有結點小於等於2的樹
- 滿二叉樹:二叉樹中,所有分支結點都同時具有左孩子和右孩子,並且所有子節點在同一層上
- 完全二叉樹:只允許樹的最后一層出現空結點,且最下層的葉子節點集中在樹的左部
4.2樹的數組存儲
核心操作:
4.2.1添加結點:
direction = 0代表添加左子節點,direction = 1代表添加右子節點
左子節點 = 2*索引+1;右子節點 = 2*索引+2
下圖可以驗證
繼續判斷索引的合法性,左子節點和右子節點同樣不能小於0或者大於等於數組長度
最后將*pNode賦值給子節點
bool Tree::AddNode(int nodeIndex, int direction, int *pNode) { if (nodeIndex<0 || nodeIndex >= m_iSize) { return false; } if (m_pTree[nodeIndex] == 0) { return false; } if (direction == 0) { //nodeIndex * 2 + 1<0可以省略 if (nodeIndex * 2 + 1<0 || nodeIndex * 2 + 1 >= m_iSize) { return false; } //判斷是否有左子節點 if (m_pTree[nodeIndex * 2 + 1] != 0) { return false; } m_pTree[nodeIndex * 2 + 1] = *pNode; } if (direction == 1) { //nodeIndex * 2 + 2<0可以省略 if (nodeIndex * 2 + 2<0 || nodeIndex * 2 + 2 >= m_iSize) { return false; } //判斷是否有左子節點 if (m_pTree[nodeIndex * 2 + 2] != 0) { return false; } m_pTree[nodeIndex * 2 + 2] = *pNode; } return true; }
4.2.2刪除結點:
判斷索引和數組值的合法性
將*pNode接收刪除的對應索引的數組
將該索引數組置為0,結點值等於0代表刪除
返回正確結果
bool Tree::DeleteNode(int nodeIndex, int *pNode) { if (nodeIndex<0 || nodeIndex >= m_iSize) { return false; } if (m_pTree[nodeIndex] == 0) { return false; } *pNode = m_pTree[nodeIndex]; m_pTree[nodeIndex] = 0; return true; }
4.3 樹的鏈式存儲
核心操作:
前序遍歷:根左右
中序遍歷:左根右
后序遍歷:左右根
void Node::PreorderTraversal() { cout << this->index<<" "<<this->data << endl; if (this->pLChild != NULL) { this->pLChild->PreorderTraversal(); } if (this->pRChild != NULL) { this->pRChild->PreorderTraversal(); } } void Node::InorderTraversal() { if (this->pLChild != NULL) { this->pLChild->InorderTraversal(); } cout << this->index << " " << this->data << endl; if (this->pRChild != NULL) { this->pRChild->InorderTraversal(); } } void Node::PostorderTraversal() { if (this->pLChild != NULL) { this->pLChild->PostorderTraversal(); } if (this->pRChild != NULL) { this->pRChild->PostorderTraversal(); } cout << this->index << " " << this->data << endl; }
5.圖
5.1概念
圖:簡單的說,圖是一個用線或邊連接在一起的頂點或結點的集合,嚴格的說,圖是有限頂點V和邊E的有序對
有向邊:帶有方向的邊
無向邊:沒有方向的邊
權:在圖的一些應用中,要為每條邊賦予一個表示大小的值,這個值就稱為權
連通圖:設圖G是無向圖,當且僅當G的每一對頂點之間都有一條路徑,則稱G是連通圖
生成樹:如果圖H是圖G的子圖,且他們的頂點集合相同,並且H是沒有環路的無向連通圖(即一棵樹),則稱H是G的一棵生成樹
極大連通子圖:連通圖本身
極小連通子圖:為其生成樹
連通分量:無向圖的極大連通子圖
出度:從該頂點出發的邊數
入度:到達該頂點的邊數
深度優先搜索:樹的前序遍歷
廣度優先搜索:按層次來遍歷的樹的前序遍歷
如圖,存在三個連通分量
子圖:如果圖H的頂點和邊的集合是圖G的子集,那么稱圖H是圖G的子圖
強連通圖:圖G是一個有向圖,當且僅當每一對不同的頂點u,v,從u到v和v到u都有一條有向路徑
5.2圖的數組存儲
核心操作:
5.2.1為有向圖設置鄰接矩陣:
判斷行列的合法性
如果行小於0,行大於等於最大容量,返回錯誤
如果列小於0,列大於等於最大容量,返回錯誤
圖如下:
上圖的鄰接矩陣如下:
以(A,B)即(0,1),0行1列,0*8+1=1。
滿足row*m_iCapacity+col計算的索引
bool cMap::setValueToMatrixForDirectedGraph(int row, int col, int val) { if(row<0 || row>=m_iCapacity) { return false; } if (col < 0 || col >= m_iCapacity) { return false; } m_pMatrix[row*m_iCapacity + col] = val; return true; }
6.排序算法
6.1冒泡排序
6.2插入排序
基本思想:將一組數據分為有序組和待插入組,有序組增加,待插入組減少至0為止。
有序組一般選擇左邊第一個元素
6.3快速排序