線性表-鏈式存儲結構


1、線性表的鏈式存儲結構

每個元素多用一個位置來存放指向下一個元素位置的指針,依次類推,可以找到所有的元素。鏈式存儲中,除了要存儲數據本身外,還要存儲它的后繼元素的存儲地址(指針)。

   數據域:存儲數據信息的域;

   指針域:存儲直接后繼位置的域。

   這兩部分信息組成數據元素稱之為存儲映像,節點Node。鏈表中每個結點中只包含一個指針域,為單鏈表。鏈表中的第一個結點的存儲位置叫做頭指針,最后一個結點指針為空。

2、頭節點和頭指針(頭節點不是必須的)

    頭指針:

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

   頭節點:

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

 

3、帶頭結點的單鏈表實現

C語言中可以用結構指針來描述單鏈表:

typedef int ElemType;

typedef struct Node {
    ElemType data;    //數據域
    struct Node* Next;//指針域
}Node, *LinkList;
//結點由存放數據的數據域和存放后繼結點地址的指針域組成

 

單鏈表的插入:

//單鏈表的插入
STATUS ListInsert(LinkList* L, int i, ElemType e)//L是指向頭節點的二級指針
{
    if ((L == NULL) || (i > (*L)->data + 1))
    {
        return 0;
    }

    int j=1;
    LinkList p = *L;

    while (p && (j < i))//找到要插入的位置
    {
        p = p->Next;
        j++;
    }
    LinkList n = (LinkList)malloc(sizeof(Node));
    n->data = e;
    n->Next = p->Next;
    p->Next = n;

    (*L)->data++;   //頭節點的數據域,表示當前鏈表的長度
    return 1;
}

單鏈表的刪除

//單鏈表的刪除
STATUS ListDelete(LinkList* L, int i, ElemType* e)
{
    if (L == NULL || i > (*L)->data + 1)
        return 0;
    int j = 1;
    LinkList p = (*L);
    while (p&&j<i)
    {
        p = p->Next;
        j++;
    }
    LinkList q = p->Next; //要刪除的結點
    *e = q->data;
    p->Next = q->Next;

    free(q);
    (*L)->data--;
    return 1;
}
//四個基本操作,初始,清空,判斷是否為空,獲取長度
//初始化帶有頭結點的鏈表
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;
}
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;
typedef int STATUS;

typedef struct Node {
    ElemType data;    //數據域
    struct Node* Next;//指針域
}Node, *LinkList;
//結點由存放數據的數據域和存放后繼結點地址的指針域組成

//單鏈表的插入
STATUS ListInsert(LinkList* L, int i, ElemType e)//L是指向頭節點的二級指針
{
    if ((L == NULL) || (i > (*L)->data + 1))
    {
        return 0;
    }

    int j=1;
    LinkList p = *L;

    while (p && (j < i))//找到要插入的位置
    {
        p = p->Next;
        j++;
    }
    LinkList n = (LinkList)malloc(sizeof(Node));
    n->data = e;
    n->Next = p->Next;
    p->Next = n;

    (*L)->data++;  //頭節點的數據域,表示當前鏈表的長度
    return 1;
}

//單鏈表的刪除
STATUS ListDelete(LinkList* L, int i, ElemType* e)
{
    if (L == NULL || i > (*L)->data + 1)
        return 0;
    int j = 1;
    LinkList p = (*L);
    while (p&&j<i)
    {
        p = p->Next;
        j++;
    }
    LinkList q = p->Next; //要刪除的結點
    *e = q->data;
    p->Next = q->Next;

    free(q);
    (*L)->data--;
    return 1;
}

int main()
{
    LinkList L=(LinkList)malloc(sizeof(Node));
    L->Next = NULL;
    L->data = 0;
    for (int i = 1; i <= 10; i++)
    {
        ListInsert(&L, i, i * i);
    }
    LinkList p = L->Next;
    while (p)
    {
        printf_s("%d ", p->data);
        p = p->Next;
    }
    printf_s("\n");
    int j,e;
    printf_s("請輸入要刪除第幾個結點\n");
    scanf_s("%d",&j);
    ListDelete(&L, j, &e);
    printf_s("刪除的結點為:%d\n", e);
    printf_s("刪除后的鏈表為:\n");
    p = L->Next;
    while (p)
    {
        printf_s("%d ", p->data);
        p = p->Next;
    }
    return 1;
}
View Code

對於插入或刪除數據越頻繁的操作,單鏈表的效率優勢越明顯。

4、創建鏈表

頭插法:從一個空表開始,生成新節點,讀取的數據放到新節點的數據域中,然后將新節點插入到當前鏈表的表頭上。也就是說,把新加的元素放在表頭后的第一個位置。

void CreatListHead(LinkList* L, int n)
{
    LinkList p;
    int i;

    srand(time(0));

    *L = (LinkList)malloc(sizeof(Node));
    (*L)->Next = NULL;

    for (i = 0; i < n; i++)
    {
        p = (LinkList)malloc(sizeof(Node));
        p->data = rand() % 10 + 1;
        p->Next = (*L)->Next;
        (*L)->Next = p;
     (*L)->data++; } }

尾插法:把新節點插入鏈表的最后,需要增加一個指向鏈表最后一個尾節點的指針r。

void CreatListEnd(LinkList* L, int n)
{
    LinkList p, r;
    //創建頭結點
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->data = 0;
    (*L)->Next = NULL;

    p = *L;
    srand(time(0));
    for (int i = 0; i < n; i++)
    {
        r = (LinkList)malloc(sizeof(Node));
        r->data = rand() % 10 + 1;
        r->Next = p->Next;
        p->Next = r;
        p = r;      //p指向最后一個結點
        (*L)->data++;
    }
}

5、單鏈表結構與順序存儲結構的優缺點:

1)若線性表需要頻繁查找,很少進行插入和刪除操作,采用順序存儲結構合適。 例如用戶注冊信息,絕大多數都是讀取數據。 

2)若需要頻繁的插入和刪除,采用單鏈表合適。

優缺點 順序存儲結構 單鏈表
存儲分配方式: 一段連續的存儲單元依次存儲線性表的數據元素 鏈式存儲,用一組任意的存儲單元存放線性表的元素
時間性能: 查找O(1),插入和刪除O(n) 查找O(n),插入和刪除O(1)
空間性能: 需要預先分配存儲空間,較大浪費,較小溢出 利用零碎空間

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM