資料結構(學習中)
預先知識:(C語言)
1、指標
地址:記憶體單元的編號
指標:指標就是地址,地址就是指標
指標變數:就是一個變數,這個變數儲存了一個非負整數,即儲存了記憶體單元的編號的變數,所有指標變數只佔4個位元組(32位機器來說)
基本型別的指標
指標和陣列
#include <stdio.h> int main(void) { int a[5] = {1, 2, 3, 4, 5}; int *p = a; /* int *p; p = a 陣列名也是一個變數,一個指向陣列第一個元素的指標變數,存放了第一個元素的記憶體空間地址 將陣列名給指標變數p,意味著p指向陣列的第一個元素(真正指向第一個元素的第一個位元組單元,因為這是個整型指標變數,所以可以理解為指向第一個元素) a = p => 第一個元素的記憶體空間地址 所以 *p就是第一個元素的記憶體空間 *(p+1) 是第二個元素的記憶體空間,因為p = a,所以a[0] = p[0] */ printf("p = %p\n", p); printf("*p = %d\n", *p); printf("p[0] = %d\n", p[0]); printf("*(p+1) = %d\n", *(p+1)); printf("*p+1 = %d\n", (*p + 1) ); return 0; }
傳遞給被調函式的實參,在被調函式中修改後,在主調函式中能響應到 ,這裡可以說是主調函式中的靜態記憶體在被調函式中進行了修改,關鍵在於:在主調函式中的實參是否想要在被調函式中被修改,如果想被修改,就需要傳實參地址給被調函式
#include <stdio.h> void func(int *); int main(void) { int p = 1; func(&p); /* 主調函式中要使用取地址符&來獲取整型p(對4個位元組的記憶體空間命名為p,沒有其他實際意義)的第一個位元組地址,傳遞到func中 */ printf("p = %d\n",p); } void func(int *pt) { *pt = 10; /* pt是一個整型指標變數,雖然它指向p的第一個位元組地址 ,但對剩餘的3個位元組也有訪問許可權, 所以*pt應該是代表了4個位元組單元空間 即*pt = p,這樣對記憶體進行操作了,主調函式中的變數自然就會被改變 */ }
2、結構體
1、定義的末位分號不能省
2、資料型別是 【struct 名稱】整體是一個數據型別
3、‘.’成員操作符,結構體變數.成員;‘->’成員操作符,結構體指標變數->成員
4、與基本資料型別不同,不能算術運算和邏輯運算,允許賦值操作
#include <stdio.h> struct ST1 { char name[200] ; int age; }; //末位的;不能省略 int main(void) { struct ST1 st = {"zhangsan", 20}; struct ST1 st2 = {"wangwu", 22}; struct ST1 *sp ; sp = &st; //定義結構體變數,使用'.'成員操作符獲取結構體的成員變數值 printf("name = %s\n", st.name); printf("age = %d\n", st.age); //定義結構體指標變數,使用'->'成員操作符獲取結構體的成員變數值 printf("name = %s\n", sp->name); printf("age = %d\n", sp->age); /* sp = &st,sp儲存了結構體變數st的地址,這個地址是結構體的第一個位元組的記憶體空間的地址 *sp 就是結構體的位元組空間(準確來講是結構體第一個位元組空間,因為sp是這個結構體的指標,所以後面空間也能被方訪問),所以 *sp = st ===> (*sp).name = st.name ,為了書寫方便, 就把(*sp).name 替換成了 sp->name,這個是C語言開發人員人為約定的。 */ // st + st2 這個操作是不允許的,同樣減法,乘法,除法都不能操作,只允許賦值 printf("name = %s\n", st2.name); printf("age = %d\n", st2.age); st2 = st; printf("name = %s\n", st2.name); printf("age = %d\n", st2.age); return 0; }
3、動態記憶體分配和釋放
靜態記憶體分配:由申明好的資料型別,系統自動分配指定好的型別記憶體分配數量,這個可以在被調函式中修改它的記憶體值
malloc() free()
跨函式使用記憶體,只能通過動態分配記憶體才能保證記憶體被函式外使用,什麼時候釋放,取決於什麼時候使用free函式
在被呼叫函式中國動態分配記憶體,供主調函式進行使用和修改
#include <stdio.h>
#include <malloc.h>
int declare_memory(int *p , int memory_size);
int main(void)
{
int * p; //這個整型指標用於指向申請的記憶體空間的地址
/*
使用&為了能夠在主調函式中響應修改後實參的值
p是 int *型別
&p 就是 int **型別了,所以被調函式中形參的型別也要對應
如果使用declare_memory(p, 12),而被調函式的形參使用int *型別,這樣和一般函式的實參修改 一樣,不能將被調函式修改後的值傳遞到主調函式中了
*/
if(!declare_memory(&p, 12))
{
printf("記憶體申請失敗");
return 0;
}
//在這裡釋放declare_memory函式申請的記憶體空間,如果一直不釋放,要麼等到main函式執行結束,要 麼就會記憶體洩漏,
free(p);
return 0;
}
int declare_memory(int **p , int memory_size)
{
// 這裡的*p == 主調函式中的p了
*p = (int *)malloc(sizeof(int)*memory_size);
if(*p == NULL)
{
return 0;
}
return 1;
}
資料結構:
將現實中大量而複雜的問題轉換為資料,儲存到記憶體中,這個就是資料結構,以及在此基礎上實現某些功能而執行的相應的操作,這個操作就是演算法
演算法:
狹義的演算法與資料的存取方式密切相關
廣義的演算法與資料的儲存無關
泛型:利用某種技術達到這個效果(不同的資料儲存方式,執行的操作是一樣的)
時間複雜度和空間複雜度
程式 = 資料的儲存 + 資料的操作 + 可以被計算機執行的語言
1、線性結構:第一個元素只有後繼元素,最後一個元素只有前驅元素,其餘元素都有前驅元素和後繼元素
基本操作:
1、初始化 int init(&L)
2、銷燬 int destroy(&L)
3、重置 int clear(&L)
4、判空 bool isEmpty(&L)
5、獲取已存元素數量 void listLen(&L,int * len)
6、獲取指定索引的元素 int getElem(&L, int index,ElemType *e)
7、獲取給定值並滿足給定關係的第一個元素(這裡的一個形參是函式型別) int locateElem(&L, ElemType elem, (int)(*comp)(ElemType , ElemType) )
8、獲取一個元素的前驅 int getPriorElem(&L, ElemType elem, ElemType *pre_)
9、獲取一個元素的後繼 int getNextElem(&L, ElemType elem , ElemType *next_)
10、插入一個元素 int insertElem(&L, int index, ElemType elem)
11、刪除一個元素 int deleteElem(&L, int index, ElemType *del_elem)
12、根據給定關係改變元素的值(這裡同樣有一個形參是函式型別)int traverse(&L, (void)(*update)(ElemType *))
連續儲存:陣列 ,操作程式碼
離散儲存:連結串列,操作程式碼,迴圈連結串列程式碼(待更新)
首節點(第一個存有有效元素的結點),尾節點(最後一個存有有效元素的結點),頭結點(首結點前的一個結點),頭指標(指向頭結點的指標),尾指標(指向尾結點的指標)
線性結構的常見應用:棧和佇列
結構如下,
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*PNode;
typedef struct stack
{
PNode base; //棧基地址,棧底指標
PNode pop; //棧頂指標
int len; //棧長度,方便操作
}STACK;
typedef struct queue
{
PNode front;
PNode rear;
}QUEUE;
棧:只允許在棧頂插入元素和刪除元素 ,鏈棧程式碼,順序棧程式碼(待更新)
佇列:只允許在佇列頭刪除元素,在佇列尾插入元素,靜態佇列程式碼(待更新),鏈式佇列(待更新)
靜態佇列一般是迴圈佇列,容易操作,入隊就把隊尾向上加,出隊把隊頭向上加,如果隊頭或隊尾到達空間上限,就置0,一是為了不浪費空間,二是不用將佇列元素整體向上或向下移動
鏈式佇列就和一般連結串列操作相同,只是只能在head結點加入,在tail結點刪除
鏈式結構和順序結構除了操作細節不一樣之外,廣義上說是完全一樣的結構型別
2、非線性結構
樹
二叉數:前序遍歷(根,左,右),中序遍歷(左,根,右),後序遍歷(左,右,根),層次遍歷(從根向下,一層一層的,每層從左到右)
圖
3、查詢和排序
折半查詢
排序:
冒泡
插入
選擇
歸併
快速