作業分析講解
**********===
1. **的錯誤:
語法錯誤:最容易的,gcc編譯的時候編譯器就會告訴你(警告warning或者錯誤error)
邏輯錯誤:編譯的時候,沒有任何語法錯誤,但是執行的時候產生的現象跟你心中所想的不一致
解決問題思路:
第一步:定位出錯誤的位置(通過列印語句)
第二步:在錯誤的位置一句句執行**,把結果手動寫出來,對照著看
核心鍊錶
**********==
1. 核心鍊錶:只適用於linux系統(window不要去使用),它是linux中專用的一種鍊錶(本質上就是個雙向迴圈鍊錶)
優勢:前面學習雙向迴圈鍊錶"很痛苦"(畫圖,理清指標的指向關係,關係理不清容易產生段錯誤)
核心鍊錶把我們同學最討厭的指標操作封裝成了函式或者巨集定義(方便你直接使用,隱藏了指標操作的細節)
2. 核心鍊錶的原理:
普通的雙向迴圈鍊錶定義
struct doublelist
核心鍊錶的定義
struct 名字自定義 //把資料和指標做了剝離
struct kernellist //大結構體(宿主結構體)
3. 核心鍊錶提供的介面函式或者巨集定義
(1) 初始化指標
init_list_head(ptr) //巨集函式
引數:ptr --》你要初始化的那個指標 struct list_head
(2) 插入
list_add_tail(struct list_head *new, struct list_head *head)
引數: 兩個小結構體指標
功能一: 把new這個節點插入到head表示的核心鍊錶的尾部
功能二: 把new這個節點插入到另外乙個節點的前面 list_add_tail(new, othernode);
void list_add(struct list_head *new, struct list_head *head)
引數:同上
功能一:把new這個節點插入到head和第乙個有效節點之間
功能二:把new這個節點插入到另外乙個節點的後面 list_add(new, othernode);
(3) 遍歷核心鍊錶
寫法一:
list_for_each_entry(pos, head, member) //巨集函式 //用乙個指標,pos來遍歷
引數:pos --》大結構體指標,用來遍歷核心鍊錶
head --》頭節點裡面的小結構體指標
member --》小結構體在大結構中的名字
注意:這種迴圈適合遍歷列印鍊錶中的資料,不適合在遍歷的過程中直接刪除資料(容易導致段錯誤)
寫法二:
list_for_each_entry_safe(pos, n, head, member) //用兩個指標pos和n一前一後遍歷
引數:pos --》大結構體指標,用來遍歷核心鍊錶
n --》大結構體指標,用來遍歷核心鍊錶,跟pos一起配合遍歷鍊錶
head --》頭節點裡面的小結構體指標
member --》小結構體在大結構中的名字
注意:這種迴圈,遍歷列印,刪除節點都沒有問題
寫法三:
list_for_each(pos, head)
引數:pos --》小結構體指標,用來遍歷核心鍊錶
head --》頭節點裡面的小結構體指標
必須配合list_entry(ptr, type, member)一起使用,原因是list_for_each()裡面的pos是個小結構體指標(無法訪問大結構體中的成員)
作用:通過小結構體得到對應的大結構體(位址轉換,原始碼中通過小結構體指標計算得到大結構體的首位址)
引數:ptr --》小結構指標
type --》大結構體的型別名
member --》小結構體在大結構中的名字
(4) 刪除
void list_del(struct list_head *entry)
引數: 小結構體指標
你要刪除的那個節點的小結構體指標
4. 總結:
第一點: 核心鍊錶中有兩種轉換
第一種:大結構呼叫小結構 struct kernellist *p; p->mypoint mypoint就是我的小結構體
第二種:小結構體得到大結構體 struct kernellist *p=list_entry(小結構體指標, 大結構體型別名,小結構體名字)
第二點:
練習:1. 熟悉核心鍊錶的介面函式,動手使用核心鍊錶的函式,體會每個引數的含義
2. 用核心鍊錶存放某個目錄種讀取的的路徑,鍵盤輸入next字串表示下一張(列印下乙個節點中的名字即可)
鍵盤輸入prev字串表示上一張(列印上乙個節點中的名字即可)
3. 擴充套件: 核心鍊錶還有其它的函式
//調整節點的位置
void list_move(struct list_head *list,struct list_head *head) //把list這個節點挪動到head表示的節點的後面
void list_move_tail(struct list_head *list,struct list_head *head) //把list這個節點挪動到head表示的節點的前面
void list_splice(struct list_head *list, struct list_head *head) //合併鍊錶,把head表示的鍊錶合併到list表示的鍊錶尾部
/usr/src/linux-headers-4.10.0-28/include/linux/list.h
棧和佇列
***************===
1. 棧:先進後出
兩種常見的實現方法: 順序棧(用順序表實現棧的邏輯)
鏈式棧(用單鏈表實現棧的邏輯)
(1) 順序棧
棧的初始化
壓棧(入棧)
彈棧(出棧)
棧的銷毀
struct stack
;struct stack *init_stack()
//壓棧 push_stack
int push_stack(int newdata,struct stack *stack)
『//出棧 pop_stack
int pop_stack(struct stack *stack)
佇列:先進先出
作業:1. 完成順序棧的**,三個函式的封裝,利用封裝的函式實現將鍵盤輸入的任意整數轉換成2進製儲存到棧裡面,然後列印出來
用棧實現字串的逆序輸出
#include "myhead.h"
#include "kernel_list.h"
//定義乙個結構體表示核心鍊錶
struct kernellist
;struct kernellist *myhead;
//初始化
struct kernellist *init_list()
//尾插
int insert_tail(char *newdata,struct kernellist *head)
//封裝遞迴函式
int myreaddir(char *dirpath)
struct dirent *p;
//迴圈讀取目錄
while((p=readdir(dirp))!=null)
}if(p->d_type==dt_dir) //子目錄
}}int main(int argc,char **ar**)
if(strcmp(cmd,"prev")==0) //上一張
}}
資料結構與演算法四
資料結構是資料儲存的方式,演算法則是處理資料的方法,通常我們通過分析演算法的時間複雜度和空間複雜度來判斷它的好壞。通常我們學資料結構的同時也會學習演算法,資料結構的不同就會導致演算法的不同,資料結構的選擇對演算法效率會產生重大的影響,所以資料結構與演算法緊密聯絡。乙個問題可能會有多種演算法,我們當然...
資料結構與演算法(四)
結構體 1 為什麼出現結構體 為了表達一些複雜的資料,而普通的基本資料型別變數無法表示 2 什麼是結構體 結構體是使用者根據自己的需求定義的一種新的資料型別 3 如何使用結構體 兩種方式 struct student int main void printf d s d n st.number,st...
資料結構與演算法《四》
一切推理都必須從觀察與實驗得來。伽利略 伽利雷 編寫乙個高效的演算法來搜尋 m x n 矩陣 matrix 中的乙個目標值 target。該矩陣具有以下特性 示例 現有矩陣 matrix 如下 1,4,7,11,15 2,5,8,12,19 3,6,9,16,22 10,13,14,17,24 18...