線性表是最簡單最多見的數據結構,屬於邏輯結構;node
線性表有兩種實現方式(存儲方式),分別是順序實現和連接實現;算法
線性表是由n(>=0)個數據元素組成的有限序列
,數據元素的個數n定義爲表的長度;數組
案例:數據結構
線性表用L表示,一個非空線性表可記爲L = (a1,a2,..an);測試
a1後面的稱爲a1的後繼spa
an前面的稱爲an的前驅設計
a1爲起始節點,an爲終端節點,任意相鄰的兩個元素,如a1和a2,a1是a2的直接前驅,a2是a1的直接後繼;3d
線性表中元素個數即表的長度,此處爲n;指針
表中沒有任何元素時,稱爲空表code
除了首節點和尾節點以外,每一個節點都有且只有一個直接前驅和直接後繼,首節點沒有前驅,尾節點沒有後繼;
節點之間的關係屬於一對一;
Initiate(L) 創建一個空表L(),L不包含數據元素
Length(L) 返回線性表的長度
Get(L,i) 返回線性表的第i個元素,i不知足1<=i<=Length(L)時,返回特殊值;
Locate(L,x)查找x在L中的節點序號,如有多個匹配的返回第一個,若沒有匹配的返回0;
Insert(L,x,i)將x插入到L的第i個元素的前面(其餘元素日後挪),參數i取值範圍爲1<=i<=Length(L)+1;運算結束後表長度+1;
Delete(L,i)刪除表L中的第i個元素,i有效範圍1<=i<=Length(L);操做結束後表長度-1
強調:上述的第i個指的是元素的序號從1開始,而不是下標從0開始;
另外:插入操做要保證操做後數據仍是一個接着一個的不能出現空缺;
線性表是一種邏輯結構,能夠經過順序存儲結構來實現,即:
將表中的節點一次存放在計算機內存中一組連續的存儲單元中,數據元素在線性表中的鄰接關係決定了它們在存儲空間中的存儲位置;換句話說邏輯結構中相鄰的兩個節點的實際存儲位置也相鄰;
用順序存儲結構實現的線性表也稱之爲爲順序表,通常採用數組來實現;
圖示:
線性表的大小:指的是最大能存儲的元素個數
線性表的長度:指的是當前已存儲的個數
c語言實現:
#include <stdio.h> //初始化操做: const MAX_SIZE = 5;//最大長度 typedef struct list { int data[MAX_SIZE];//數組 int length;//當前數據長度 }; //獲取targert在表中的位置 int locate(struct list *l,int target){ for (int i = 0;i < l->length;i++){ if (target == l->data[i]){ return i + 1; } } return 0; } //獲取第loc個元素 int get(struct list *l,int loc){ if (loc < 1 || loc > l->length){ printf("error:位置超出範圍\n"); return -1; }else{ return l->data[loc-1]; } } //插入一個元素到第loc個位置上 void insert(struct list *l,int data,int location){ if (l->length == MAX_SIZE){ printf("errolr:表容量已滿\n"); return; } if (location < 1 || location > l->length+1){ printf("error:位置超出範圍\n"); return; } //目標位置後面的內容以此日後挪 for (int i = l->length; i >= location; i--) { l->data[i] = l->data[i-1]; } //在目標位置放入新的數據 l->data[location-1] = data; l->length+=1;//長度加1 } //刪除第loc個元素,從目標位置日後的元素一次向前移動 void delete(struct list *l,int loc){ if (loc < 1|| loc > l->length){ printf("error:位置超出範圍\n"); return; } //目標位置及後面的全部元素所有向後移動 for (;loc < l->length; ++loc) { l->data[loc-1] = l->data[loc]; } l->length-=1; } //打印全部元素 測試用 void show(struct list l){ for (int i = 0; i < l.length; ++i) { printf("%d\n",l.data[i]); } } //測試 int main() { struct list alist = {}; insert(&alist,100,alist.length+1); insert(&alist,200,alist.length+1); insert(&alist,300,alist.length+1); insert(&alist,400,alist.length+1); delete(&alist,1); printf("%d\n",alist.length); show(alist); printf("%d\n",get(&alist,4)); printf("%d\n", locate(&alist,300)); printf("%d\n", get(&alist,1)); return 0; }
假設線性表中含有n個元素,
在插入元素時,有n+1個位置能夠插入,由於要保證數據是連續的
每一個位置插入數據的機率是: 1/(n+1)
在i的位置插入時,要移動的元素個數爲:n - i + 1
算法時間複雜度爲:O(n)
假設線性表中含有n個元素,
在刪除元素時,有n個位置能夠刪除
每一個位置插入數據的機率是: 1/n
在i的位置刪除時,要移動的元素個數爲:n - i
算法時間複雜度爲:O(n)
順序表在進行插入和刪除操做時,平均要移動大約一半的數據元素,當存儲的數據量很是大的時候,這一點須要特別注意;
簡單的說,順序表在插入和刪除時的效率是不夠好的;特別在數據量大的狀況下;
1.順序表是一維數組實現的線性表
2.邏輯上相鄰的元素,在存儲結構中也是相鄰的
3.順序表可實現隨機讀取
設第i個節點的存儲地址爲x
設順序表起始地址爲loc,每一個數據元素佔L個存儲單位
計算公式爲:x = loc + L * (i-1)
如 loc = 100 i = 5 L = 4 則 x = 116
線性表也可經過連接存儲方式來實現,用連接存儲方式實現的線性表也稱爲鏈表 Link List
鏈式存儲結構:
1.可用任意一組存儲單元來存儲數據
2.鏈表中節點的邏輯次序與物理次序不必定相同
3.每一個節點必須存儲其後繼節點的地址信息(指針)
圖示:
單鏈表指的是隻能沿一個方向查找數據的鏈表,如上圖
每一個節點由兩個部分(也稱爲域)組成
節點結構:
每一個節點只知道本身後面一個節點殊不知道本身前面的節點因此稱爲單鏈表
圖示:
帶有head節點的單鏈表:
單鏈表的第一個節點一般不存儲數據,稱爲頭指針,使用頭指針來存儲該節點的地址信息,之因此這麼設計是爲了方便運算;
//數據結構定義 typedef struct node { struct node *next; int data,length; } Node, *LinkList; /* * typedef 是用來取別名的 * Node 是struct node 的別名 * *LinkList 是 struct node *的別名 * 後續使用就不用在寫struct關鍵字了 */
一個空鏈表有一個頭指針和一個頭結點構成
假設已定義指針變量L,使L指向一個頭結點,並使頭結點的next爲NULL
//時間複雜度 :O(1) LinkList initialLinkList() { // 定義鏈表的頭結點 LinkList head; //申請空間 head = malloc(sizeof(struct node)); //使頭結點指向NULL head->next = NULL; return head; }
從頭指針開始遍歷每一個節點知道某個節點next爲NULL爲止,next不爲空則個數len+1;
//求表長 時間複雜度 :O(n) int length(LinkList list){ int len = 0; Node *c = list->next; while(c != NULL){ len+=1; c = c->next; } return len; }
給定一個位置n,獲取該位置的節點
遍歷鏈表,過程當中若某節點next爲NULL或已遍歷個數index=n則結束循環
//從鏈表中獲取第position個位置的節點 時間複雜度 :O(n) Node *get(LinkList list, int position) { Node *current; int index = 1; current = list->next; //若是下面還有值而且尚未到達指定的位置就繼續遍歷 要和查找元素區別開 這就是一直日後遍歷直到位置匹配就好了 while (current != NULL && index < position) { current = current->next; index += 1; } if (index == position) { return current; } return NULL; }
對給定表元素的值,找出這個元素的位置
遍歷鏈表,若某節點數據域與要查找的元素data相等則返回當前遍歷的次數index
//求表head中第一個值等於x的結點的序號(從1開始),若不存在這種結點,返回結果爲0 時間複雜度 :O(n) int locate(LinkList list,int data){ int index = 1; Node *c; c = list->next; while (c != NULL){ if (c->data == data){ return index; } index+=1; c = c->next; } return 0; }
在表的第i個數據元素結點以前插入一個以x爲值的新結點new
獲取第i的節點的直接前驅節點pre(若存在),使new.next = pre.next;pre.next = new;
//在表head的第i個數據元素結點以前插入一個以x爲值的新結點 時間複雜度 :O(n) void insert(LinkList list, int position, int data) { Node *pre, *new; if (position == 1) { //若插入位置爲1 則表示要插入到表的最前面 即head的後面 pre = list; } else { //pre表示目標位置的前一個元素 因此-1 pre = get(list, position - 1); if (pre == NULL) { printf("error:插入的位置超出範圍"); exit(0); } } new = malloc(sizeof(Node)); new->data = data; new->next = pre->next; pre->next = new; list->length += 1; }
刪除給定位置的節點
獲取目標節點target的直接前驅節點pre(若pre與目標都有效),pre.next = target.next; free(target);
//刪除鏈表中第position個位置的節點 時間複雜度 :O(n) void delete(LinkList list,int position){ //獲取要刪除節點的直接前驅 Node *pre; if (position == 1){ //如要刪除的節點是第一個那直接前驅就是頭結點 pre = list; }else{ pre = get(list,position-1); } ////若是目標和前驅都存在則執行刪除 if (pre != NULL && pre->next != NULL){ Node *target = pre->next; //要刪除的目標節點 //直接前驅的next指向目標的直接後繼的next pre->next = target->next; free(target); printf("info: %d被刪除\n",target->data); list->length -= 1; }else{ printf("error:刪除的位置不正確!"); exit(1); } }
//效率比較差算法 時間複雜度 :O(n^2) LinkList createLinkList1(){ LinkList list = initialLinkList(); int a;//輸入的數據 int index = 1; //記錄當前位置 scanf("%d",&a); while (a != -1){ // O(n) insert(list,index++,a); // O(n^2) 每次都要從頭遍歷鏈表 scanf("%d",&a); } return list; } //尾插算法 記錄尾節點 從而避免遍歷 時間複雜度 :O(n) LinkList createLinkList2(){ LinkList list = initialLinkList(); int a;//輸入的數據 Node *tail = list;//當前的尾部節點 scanf("%d",&a); while (a != -1){ // O(n) Node * newNode = malloc(sizeof(Node)); //新節點 newNode->next = NULL; newNode->data = a; tail->next = newNode;//尾部節點的next指向新節點 tail = newNode;//新節點做爲尾部節點 scanf("%d",&a); } return list; } //頭插算法 每次插到head的後面,不用遍歷可是順序與插入時相反 時間複雜度 :O(n) LinkList createLinkList3(){ LinkList list = initialLinkList(); int a;//輸入的數據 Node * head = list; scanf("%d",&a); while (a != -1){ // O(n) Node * newNode = malloc(sizeof(Node)); //新節點 newNode->next = NULL; newNode->data = a; newNode->next = head->next;//將本來head的next 交給新節點; head->next = newNode;//在把新節點做爲head的next; scanf("%d",&a); } return list; }
操做 | 順序表 | 鏈表 |
---|---|---|
讀表元 | O(1) | O(n) |
定位 | O(n) | O(n) |
插入 | O(n) | O(n) |
刪除 | O(n) | O(n) |