1. 程式人生 > >數據結構(一)線性表鏈式存儲實現

數據結構(一)線性表鏈式存儲實現

spl 原因 pause main -- 基本 無法 輸入 pen

(一)前提

在前面的線性表順序存儲結構,最大的缺點是插入和刪除需要移動大量的元素,需要耗費較多的時間。
原因:在相鄰兩個元素的存儲位置也具有鄰居關系,他們在內存中的位置是緊挨著的,中間沒有間隙,當然無法快速插入和刪除。
為了解決這個為題,出現了鏈式存儲結構

(二)鏈式線性表兩種結構(帶有頭結點和不帶頭結點)

不帶頭結點:

技術分享圖片

空鏈表:

技術分享圖片

帶有頭結點:

技術分享圖片

空鏈表:

技術分享圖片

(三)頭結點和頭指針的區別

頭指針:

1.頭指針是指向第一個結點的指針,若是鏈表中只有頭結點,則是指向頭結點的指針
2.頭指針具有標識作用,所以常常以頭指針冠以鏈表的名字(指針變量名)
3.無論鏈表是否為空,頭結點均不為空
4.頭指針是鏈表必要元素

頭結點:

1.頭結點是為了操作統一和方便而設立的,放在第一個元素結點之前,其數據域一般無意義(也可以存放表長度)
2.有了頭結點,對第一個元素結點的插入,刪除,其操作和其他結點操作統一了
3.頭結點不一定是鏈表的必要元素

(四)帶頭結點的單鏈表實現

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int ElemType;
typedef 
int Status; typedef struct Node { ElemType data; struct Node* next; }Node; typedef struct Node* LinkList; //四個基本操作,初始,清空,判斷是否為空,獲取長度 Status InitList(LinkList* L); Status ClearList(LinkList* L); Status ListEmpty(LinkList L); int ListLength(LinkList L); //四個元素操作,插入,刪除,兩種查找 Status GetElem(LinkList L, int
i, ElemType* e); int LocateElem(LinkList L, ElemType e); Status ListInsert(LinkList* L, int i, ElemType e); Status ListDelete(LinkList* L, int i, ElemType* e); //用來打印鏈表 void PrintList(LinkList L); int main() { LinkList L; ElemType e; Status ret; int i; printf("1.InitList\n"); InitList(&L); printf("2.1 Insert range of 5 elements by head\n"); for (i = 1; i <= 5;i++) { ListInsert(&L, 1, i); } printf("2.2 Insert range of 5 elements by end\n"); for (; i <= 10; i++) { ListInsert(&L, ListLength(L)+1, i); } PrintList(L); printf("3. list length:%d\n", ListLength(L)); printf("4.find element by element:%d get index:%d\n",8, LocateElem(L, 8)); GetElem(L, 6, &e); printf("5.find element by index:%d get element:%d\n",6,e); ListDelete(&L, 3, &e); printf("6.delete element by index:%d get element:%d\n",3,e); PrintList(L); printf("7.Clear\n"); ClearList(&L); printf("8.is empty:%d\n", ListEmpty(L)); system("pause"); return 0; } //四個基本操作,初始,清空,判斷是否為空,獲取長度 //初始化帶有頭結點的鏈表 Status InitList(LinkList* L) { *L = (LinkList)malloc(sizeof(Node)); //使頭指針指向頭結點 if (*L == NULL) //內存分配失敗 return ERROR; (*L)->next = NULL; //指針域為空 (*L)->data = 0; //頭結點數據域用來存放鏈表長度 return OK; } //清空鏈表(不會清除頭結點) Status ClearList(LinkList* L) { LinkList q, p; q = (*L)->next; //是q指向第一個結點 while (q) { p = q; q = q->next; free(p); } (*L)->next = NULL; return OK; } //判斷鏈表是否為空 Status ListEmpty(LinkList L) { if (L->next) return FALSE; return TRUE; } //獲取列表長度 int ListLength(LinkList L) { /* int length=0; LinkList q=L; while (q=q->next) length++; return length; */ return L->data; } //四個元素操作,插入,刪除,兩種查找 //按照索引查找,獲取元素 Status GetElem(LinkList L, int i, ElemType* e) { int j=1; LinkList q = L->next; while (q&&j<i) { q = q->next; j++; } if (!q || j>i) //對結果進行判斷!q是沒有找到數據,已經到達尾結點,j>i是針對函數輸入進行判斷,例如輸入i=0,就不會走上面循環但是p!=null,這時節點也沒有找到,就需要我們進行判斷,針對這個,我們也可以在函數開始就進行長度校驗,更加容易理解 return ERROR; *e = q->data; return OK; } //按照元素進行查找,獲取索引 int LocateElem(LinkList L, ElemType e) { int j=0; LinkList q = L->next; while (q) { j++; if (q->data == e) break; q = q->next; } return j; } //按照索引進行插入數據 Status ListInsert(LinkList* L, int i, ElemType e) { if (L == NULL && i > (*L)->data+1) return ERROR; int j = 1; LinkList q = *L; while (q&&j<i) { q = q->next; j++; } if (i < j) return ERROR; //新建節點,加入鏈表 LinkList n = (LinkList)malloc(sizeof(Node)); n->data = e; n->next = q->next; q->next = n; (*L)->data++; //長度自加 return OK; } //進行元素刪除 Status ListDelete(LinkList* L, int i, ElemType* e) { if (L == NULL || i>(*L)->data) return ERROR; int j=1; LinkList q, p; q = *L; while (q->next&&j<i) //這是去找指定索引元素的直接前驅 { q = q->next; j++; } if (!(q->next) || j>i) return ERROR; p = q->next; //這才是我們要刪除的 *e = p->data; q->next = p->next; free(p); (*L)->data--; return OK; } //用來打印鏈表 void PrintList(LinkList L) { printf("begin print data:\n"); LinkList q = L->next; while (q) { printf("%d ", q->data); q = q->next; } printf("\nend print data\n"); }
技術分享圖片
1.InitList
2.1 Insert range of 5 elements by head
2.2 Insert range of 5 elements by end
begin print data:
5 4 3 2 1 6 7 8 9 10
end print data
3. list length:10
4.find element by element:8 get index:8
5.find element by index:6 get element:6
6.delete element by index:3 get element:3
begin print data:
5 4 2 1 6 7 8 9 10
end print data
7.Clear
8.is empty:1
請按任意鍵繼續. . .
輸出結果
註意:對於元素的插入和刪除註意索引的正確與否

補充:使用頭插法和尾插法創建單鏈表

//頭插法和尾插法創建單鏈表
void CreateListHead(LinkList* L, int n);
void CreateListEnd(LinkList* L, int n);

//頭插法
void CreateListHead(LinkList* L, int n)
{
    LinkList q;
    //創建頭結點
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    (*L)->data = 0;

    srand(time(0));

    for (int i = 0; i < n;i++)
    {
        q = (LinkList)malloc(sizeof(Node));    //創建一個新節點
        q->data = rand() % 100 + 1;
        q->next = (*L)->next;
        (*L)->next = q;
        (*L)->data++;
    }
}

//尾插法
void CreateListEnd(LinkList* L, int n)
{
    LinkList q, p;
    //創建頭結點
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    (*L)->data = 0;

    p = *L;
    srand(time(0));    //創建隨機種子

    for (int i = 0; i < n; i++)
    {
        q = (LinkList)malloc(sizeof(Node));    //創建一個新節點
        q->data = rand() % 10 + 1;
        //進行指針交換
        q->next = p->next;  //這一步可以省去,但是需要在for循環後面加上p->next=NULL;
        p->next = q;
        //將p指針始終指向最後元素
        p = q;
        //頭結點數據域自增記錄鏈表長度
        (*L)->data++;
    }
}

(五)時間復雜度的分析

單鏈表的查找性能為O(n)---->順序存儲結構為O(1)
單鏈表的插入和刪除,在計算出某位置的指針後,插入和刪除性能為O(1)----->順序存儲結構為O(n)

(六)空間性能分析

順序存儲結構需要預分配存儲空間。
單鏈表不需要分配存儲空間,元素限制不受控制

(七)總結

若線性表需要頻繁查找,很少進行插入和刪除操作,應該選擇順序存儲結構。
當線性表中元素個數變化較大或者根本不知道有多大時,最好使用單鏈表結構,不需要考慮存儲空間大小問題

數據結構(一)線性表鏈式存儲實現