數據結構:線性表之鏈式存儲結構

爲了表示每個數據元素ai與其直接後繼元素ai+1之間的邏輯關係,對數據ai,除了存儲其自身的信息之外,還需存儲一個指示其直接後繼的信息(即直接後繼的存儲位置)。這兩部分信息組成數據元素ai的存儲映像,稱爲結點(Node)。N個結點鏈結成一個鏈表,即爲線性表(a1,a2,…,an)的鏈式存儲結構,因爲此鏈表的每個節點中只包含一個指針域,所以叫做單鏈表。我們把鏈表中的第一個結點的存儲位置叫做頭指針,,爲了更方便地對鏈表進行操作,如刪除第一個結點的特殊情況(第一個結點沒有前驅,而要摘除一個結點需要首先找到它的前驅才能做摘除操作),經常在單鏈表的第一個結點前附設一個結點,稱爲頭節點,這樣頭指針就指向了頭節點。
這裏寫圖片描述
單鏈表與數組的優缺點基本上是相反的,即單鏈表插入刪除效率高,而查找需要遍歷,效率較低。

示例程序:(改編自《大話數據結構》,增加了鏈表反轉等)

#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;

typedef int ElemType;

typedef struct  Node
{
    ElemType data;
    struct Node *next;
} Node;

typedef Node *NodePtr;

bool InitList(NodePtr *Npp)
{
    *Npp = (NodePtr)malloc(sizeof(Node));/* 產生頭結點,並使*Npp指向此頭結點 */
    if (!(*Npp))
        return false; //分配失敗

    (*Npp)->next = NULL;/* 指針域爲空 */

    return true;
}

bool ListEmpty(NodePtr Np)
{
    if (Np->next == NULL)
        return true;
    else
        return false;
}

bool ClearList(NodePtr *Npp)
{
    cout << "Clear List..." << endl;
    NodePtr p = *Npp;
    if (!(p->next))
        return true;
    while (p->next)/*  沒到表尾 */
    {
        NodePtr q = p->next;
        p->next = q->next;
        free(q);
    }

    return true;
}

int ListLength(NodePtr Np)
{
    cout << "List's length : ";
    NodePtr p = Np->next;/* p指向第一個結點 */
    int i = 0;

    while (p)
    {
        p = p->next;
        ++i;
    }

    return i;
}
/* 操作結果:用ptr返回Np中第pos個數據元素的值 */
bool GetElem(NodePtr Np, int pos, ElemType *ptr)
{
    cout << "Get Item from pos " << pos << " : ";
    NodePtr p = Np->next;
    int i = 1;
    /* p不爲空或者計數器i還沒有等於pos時,循環繼續 */
    while (p && i < pos)
    {
        p = p->next;
        ++i;
    }

    if (!p)
        return false;
    *ptr = p->data;/*  取第pos個元素的數據 */
    return true;
}
/* 返回Np中第1個與Elem滿足關係的數據元素的位序。 */
/* 若這樣的數據元素不存在,則返回值爲0 */
int LocateElem(NodePtr Np, ElemType Elem)
{
    cout << "Item " << Elem << "'s pos : ";
    NodePtr p = Np->next;
    int i = 1;

    while (p && p->data != Elem)
    {
        p = p->next;
        ++i;
    }

    if (!p)
        return 0;
    return i;
}
/* 操作結果:在Np中第pos個位置之前插入新的數據元素Elem,Np的長度加1 */
bool ListInsert(NodePtr *Npp, int pos, ElemType Elem)
{
    cout << "Insert List pos " << pos << " Item " << Elem << endl;
    NodePtr p = *Npp;
    int i = 1;

    while (p && i < pos)
    {
        p = p->next;
        ++i;
    }

    if (!p)
        return false;

    NodePtr In = (NodePtr)malloc(sizeof(Node));
    In->data = Elem;
    In->next = p->next;/* 將p的後繼結點賦值給In的後繼  */
    p->next = In;  /* 將In賦值給p的後繼 */

    return true;
}
/* 刪除Npp的第pos個數據元素,並用ptr返回其值,Npp的長度減1 */
bool ListDelete(NodePtr *Npp, int pos, ElemType *ptr)
{
    cout << "Delete List Item in pos " << pos << endl;
    NodePtr p = *Npp;
    int i = 1;

    while (p && i < pos)
    {
        p = p->next;
        ++i;
    }

    if (!p)
        return false;

    NodePtr q = p->next;
    *ptr = q->data;
    p->next = q->next;/* 將q的後繼賦值給p的後繼 */
    free(q);

    return true;
}

bool ListTraverse(NodePtr Np)
{
    cout << "List's Items : ";
    NodePtr p = Np->next;
    while (p)
    {
        cout << p->data << ' ';
        p = p->next;
    }
    cout << endl;
    return true;
}
/*  隨機產生n個元素的值,建立帶表頭結點的單鏈線性表Npp(頭插法) */
void CreateListHead(NodePtr *Npp, int num)
{
    cout << "Create List from Head ..." << endl;
    if (*Npp != NULL)
        free(*Npp);
    *Npp = (NodePtr)malloc(sizeof(Node));
    (*Npp)->next = NULL;/*  先建立一個帶頭結點的單鏈表 */
    srand(time(NULL));

    for (int i = 0; i < num; i++)
    {
        NodePtr p = (NodePtr)malloc(sizeof(Node));
        p->data = rand() % 100 + 1; //隨機數
        p->next = (*Npp)->next;
        (*Npp)->next = p;/*  插入到表頭 */
    }

}
/*  隨機產生n個元素的值,建立帶表頭結點的單鏈線性表Npp(尾插法) */
void CreateListTail(NodePtr *Npp, int num)
{
    cout << "Create List from Tail ..." << endl;
    if (*Npp != NULL)
        free(*Npp);
    *Npp = (NodePtr)malloc(sizeof(Node));
    (*Npp)->next = NULL;
    srand(time(NULL));

    NodePtr tail = *Npp; /* tail爲指向尾部的結點 */
    for (int i = 0; i < num; i++)
    {
        NodePtr p = (NodePtr)malloc(sizeof(Node));
        p->data = rand() % 100 + 1;
        tail->next = p;  /* 將表尾終端結點的指針指向新結點 */
        tail = p; /* 將當前的新結點定義爲表尾終端結點 */
    }

    tail->next = NULL;
}
/* 反轉單鏈表*/
NodePtr ReverseList(NodePtr head)
{
    cout << "Reverse List ...." << endl;
    if(NULL == head->next || NULL == head->next->next)
        return head;
    NodePtr p;
    NodePtr q;
    NodePtr r;
    p = head->next;
    q = p->next;
    head->next->next = NULL;
    while(q)
    {
        r = q->next; //
        q->next = p;
        p = q; //
        q = r; //
    }
    head->next = p;
    return head;
}

int main(void)
{
    NodePtr Np; //頭指針
    InitList(&Np);
    for (int i = 1; i < 5; i++)
        ListInsert(&Np, i, i);

    if (!ListEmpty(Np))
        cout << ListLength(Np) << endl;
    ListTraverse(Np);

    cout << LocateElem(Np, 3) << endl;

    int get;
    GetElem(Np, 2, &get);
    cout << get << endl;

    ClearList(&Np);
    cout << ListLength(Np) << endl;

    CreateListHead(&Np, 10);
    ListTraverse(Np);
    int result;
    ListDelete(&Np, 5, &result);
    ListTraverse(Np);

    ClearList(&Np);
    CreateListTail(&Np, 10);
    ListTraverse(Np);
    ListTraverse(ReverseList(Np));

    ClearList(&Np);

    return 0;
}

輸出爲:
這裏寫圖片描述
注意:程序中使用的單鏈表反轉的方法是:使用p和q連個指針配合工作,使得兩個節點間的指向反向,同時用r記錄剩下的鏈表。
下圖是不含頭節點時的單鏈表反轉情況,含頭節點的單鏈表反轉有細微區別,需小心。
這裏寫圖片描述

轉載自:http://blog.csdn.net/jnu_simba/article/details/8830179

發佈了38 篇原創文章 · 獲贊 17 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章