【慕課-數據結構-C++語言】線性表篇


原文地址:https://www.cloudcrossing.xyz/post/30/

線性表是n個數據元素的有限序列。可應用與通訊錄、一元多項式等等。

順序表

新建List.h和List.cpp

#List.h
#ifndef LIST_H
#define LIST_H

class List {
public:
    List(int size);     //創建線性表
    ~List();            //銷毀線性表
    void CleanList();   //清空線性表
    bool ListEmpty();   //判斷線性表是否為空
    bool ListFull();    //判斷線性表是否為滿
    int ListLength();   //獲取線性表長度(元素個數)
    bool GetElem(int i, int *e);    //獲取指定元素
    int LocateElem(int *e);         //尋找第一個滿足e的數據元素的位序
    bool PriorElem(int *currentElem, int *preElem);     //獲取指定元素的前驅
    bool NextElem(int *currentElem, int *nextElem);     //獲取指定元素的后繼
    bool ListInsert(int i, int *e);     //在第i個位置插入元素
    bool ListDelete(int i, int *e);     //刪除第i個位置的元素
    void ListTraverse();                //遍歷線性表
public:
    int *m_pList;
    int m_iSize;
    int m_iLength;
};

#endif // LIST_H
#List.cpp
#include "stdafx.h"
#include "List.h"
#include "iostream"

using namespace std;

List::List(int size)
{
    m_iSize = size;
    m_pList = new int[m_iSize];
    m_iLength = 0;
}


List::~List()
{
    delete[]m_pList;
    m_pList = NULL;
}

void List::CleanList()
{
    m_iLength = 0;
}

bool List::ListEmpty()
{
    return m_iLength == 0 ? true : false;
}

int List::ListLength()
{
    return m_iLength;
}

bool List::GetElem(int i, int *e)
{
    if (i < 0 || i >= m_iSize)
    {
        return false;
    }
    *e = m_pList[i];
    return true;
}

int List::LocateElem(int *e)
{
    for (int i = 0; i < m_iLength; i++)
    {
        if (m_pList[i] == *e)
        {
            return i;
        }
    }
    return -1;
}

bool List::PriorElem(int *currentElem, int *preElem)
{
    int temp = LocateElem(currentElem);
    if (temp == -1)
    {
        return false;
    }
    else
    {
        if (temp == 0)
        {
            return false;
        }
        else
        {
            *preElem = m_pList[temp - 1];
            return true;
        }
    }
}

bool List::NextElem(int *currentElem, int *nextElem)
{
    int temp = LocateElem(currentElem);
    if (temp == -1)
    {
        return false;
    }
    else
    {
        if (temp == m_iLength - 1)
        {
            return false;
        }
        else
        {
            *nextElem = m_pList[temp + 1];
            return true;
        }
    }
}

void List::ListTraverse()
{
    for (int i = 0; i < m_iLength; i++)
    {
        cout << m_pList[i] << endl;
    }
}

bool List::ListInsert(int i, int *e)
{
    if (ListFull())
    {
        cout << "棧滿,插入失敗" << endl;
        return false;
    }
    if (i < 0 || i > m_iLength)
    {
        return false;
    }
    for (int k = m_iLength - 1; k >= i; k--)
    {
        m_pList[k + 1] = m_pList[k];
    }
    m_pList[i] = *e;
    m_iLength++;
    return true;
}

bool List::ListDelete(int i, int *e)
{
    if (ListEmpty())
    {
        cout << "棧空,刪除失敗" << endl;
        return false;
    }
    if (i < 0 || i >= m_iLength)
    {
        return false;
    }
    *e = m_pList[i];
    for (int k = i + 1; k < m_iLength; k++)
    {
        m_pList[k - 1] = m_pList[k];
    }
    m_iLength--;
    return true;
}

測試用例demo.cpp

#demo.cpp
#include "stdafx.h"
#include "List.h"
#include "iostream"

using namespace std;

int main(void)
{
    int e1 = 3;
    int e2 = 5;
    int e3 = 7;
    int e4 = 2;
    int e5 = 9;
    int e6 = 1;
    int e7 = 8;
    int temp = 0;

    List * list1 = new List(7);
    cout << "before length:" << list1->ListLength() << endl;

    list1->ListDelete(0, &temp);
    list1->ListInsert(0, &e1);
    list1->ListInsert(1, &e2);
    list1->ListInsert(2, &e3);
    list1->ListInsert(3, &e4);
    list1->ListInsert(4, &e5);
    list1->ListInsert(5, &e6);
    list1->ListInsert(6, &e7);
    list1->ListInsert(6, &e7);
    cout << "after length:" << list1->ListLength() << endl;

    list1->ListTraverse();

    list1->GetElem(0, &temp);
    cout << "[0] is:" << temp << endl;

    temp = 5;
    cout << "where 5 in " << list1->LocateElem(&temp) << endl;

    list1->NextElem(&e4, &temp);
    cout << "4 next is: " << temp << endl;
    list1->PriorElem(&e4, &temp);
    cout << "4 prior is: " << temp << endl;

    list1->ListDelete(0, &temp);

    if (!list1->ListEmpty())
    {
        cout << "not empty" << endl;
    }

    list1->CleanList();

    if (list1->ListEmpty())
    {
        cout << "empty" << endl;
    }

    delete list1;

    system("pause");
    return 0;
}

運行結果:


單鏈表

首先新建節點類 Node.h 和 Node.cpp。

#Node.h
#ifndef NODE_H
#define NODE_H

class Node {
public:
    int data;    //數據域
    Node *next;  //指針域,指向Node類的指針變量
    void printNode();
};

#endif // NODE_H

PS:class 默認訪問修飾符是 private 而不是 public,所以在這里為了能訪問類的元素,把數據和指針域放到了 public 下。.

#Node.cpp
#include "Node.h"
#include "iostream"
#include "stdafx.h"

using namespace std;

void Node::printNode()
{
    cout << data << endl ;
}

接着新建單鏈表類 List_one.h 和 List.cpp

#List_one.h
#ifndef LIST_ONE_H
#define LIST_ONE_H

#include "Node.h"

class List_one {
public:
    List_one();     //創建鏈表
    ~List_one();            //銷毀鏈表
    void CleanList();       //清空鏈表
    bool ListEmpty();       //判斷鏈表是否為空
    int ListLength();       //獲取鏈表長度(元素個數)
    bool GetElem(int i, Node *pNode);    //獲取指定元素
    int LocateElem(Node *pNode);         //尋找第一個滿足e的數據元素的位序
    bool PriorElem(Node *pCurrentNode, Node *pPreNode);     //獲取指定元素的前驅
    bool NextElem(Node *pCurrentNode, Node *pNextNode);     //獲取指定元素的后繼
    bool ListInsert(int i, Node *pNode);     //在第i個位置插入元素
    bool ListDelete(int i, Node *pNode);     //刪除第i個位置的元素
    void ListTraverse();                 //遍歷線性表
    bool ListInsertHead(Node *pNode);        //在鏈表頭插入元素
    bool ListInsertTail(Node *pNode);        //在鏈表頭插入元素
public:
    Node *m_pList;
    int m_iLength;
};

#endif // LIST_ONE_H
#List_one.cpp
#include "List_one.h"
#include "stdafx.h"
#include "iostream"

using namespace std;

List_one::List_one()
{
    m_pList = new Node;
    m_pList->data = 0;
    m_pList->next = NULL;
    m_iLength = 0;
}

List_one::~List_one()
{
    CleanList();
    delete m_pList;
    m_pList = NULL;
}

void List_one::CleanList()
{
    Node *currentNode = m_pList->next;
    while (currentNode != NULL)
    {
        Node *temp = currentNode->next;
        delete currentNode;
        currentNode = temp;
    }
    m_pList->next = NULL;
    m_iLength = 0;
}

bool List_one::ListEmpty()
{
    return m_iLength == 0 ? true : false;
}

int List_one::ListLength()
{
    return m_iLength;
}

bool List_one::GetElem(int i, Node *pNode)
{
    if (i < 0 || i >= m_iLength)
    {
        return false;
    }
    Node *currentNode = m_pList;
    for (int k = 0; k <= i; k++)
    {
        currentNode = currentNode->next; 
    }
    pNode->data = currentNode->data;
    return true;
}

int List_one::LocateElem(Node *pNode)
{
    Node *currentNode = m_pList;
    int count = 0;
    while (currentNode->next != NULL)
    {
        currentNode = currentNode->next;
        if (currentNode->data == pNode->data)
        {
            return count;
        }
        count++;
    }
    return -1;
}

bool List_one::PriorElem(Node *pCurrentNode, Node *pPreNode)
{
    Node *currentNode = m_pList;
    Node *tempNode = NULL;
    while (currentNode->next != NULL)
    {
        tempNode = currentNode;
        currentNode = currentNode->next;
        if (currentNode->data == pCurrentNode->data)
        {
            if (tempNode == m_pList) //判斷是否為第一個節點
            {
                return false;
            }
            pPreNode->data = tempNode->data;
            return true;
        }
    }
    return false;
}

bool List_one::NextElem(Node *pCurrentNode, Node *pNextNode)
{
    Node *currentNode = m_pList;
    while (currentNode->next != NULL)
    {
        currentNode = currentNode->next;
        if (currentNode->data == pCurrentNode->data)
        {
            if (currentNode->next == NULL) //判斷是否為最后一個節點
            {
                return false;
            }
            pNextNode->data = currentNode->next->data;
            return true;
        }
    }
    return false;
}

void List_one::ListTraverse()
{
    Node *currentNode = m_pList;
    while (currentNode->next != NULL)
    {
        currentNode = currentNode->next;
        currentNode->printNode();
    }
}

bool List_one::ListInsert(int i, Node *pNode)
{
    if (i < 0 || i > m_iLength)
    {
        return false;
    }
    Node *currentNode = m_pList;
    for (int k = 0; k < i; k++)
    {
        currentNode = currentNode->next;
    }
    Node *newNode = new Node;
    if (newNode == NULL)
    {
        return false;
    }
    newNode->data = pNode->data;
    newNode->next = currentNode->next;
    currentNode->next = newNode;
    return true;
}

bool List_one::ListDelete(int i, Node *pNode)
{
    if (ListEmpty())
    {
        cout << "鏈空,刪除失敗" << endl;
        return false;
    }
    if (i < 0 || i >= m_iLength)
    {
        return false;
    }
    Node *currentNode = m_pList;    //保存頭節點
    Node *currentNodeBefore = NULL; //頭節點前一個節點不存在,為NULL
    for (int k = 0; k < i; k++)    //查找第i個節點  
    {
        currentNodeBefore = currentNode;  //找到第i-1個節點
        currentNode = currentNode->next;  //循環結束代表currentNode就是第i節點  
    }
    currentNodeBefore->next = currentNode->next; // 第i個節點指針域賦給第i - 1節點指針域
    pNode->data = currentNode->data;     //刪掉節點的數據域賦給pNode輸出  
    delete currentNode;          //釋放刪除節點的內存
    currentNode = NULL;
    m_iLength--;
    return true;
}

bool List_one::ListInsertHead(Node *pNode)
{
    Node *newNode = new Node;
    if (newNode == NULL)
    {
        return false;
    }
    newNode->data = pNode->data;
    newNode->next = m_pList->next;
    m_pList->next = newNode;
    m_iLength++;
    return true;
}

bool List_one::ListInsertTail(Node *pNode)
{
    Node *currentNode = m_pList;
    while (currentNode->next != NULL)
    {
        currentNode = currentNode->next;
    }
    Node *newNode = new Node;
    if (newNode == NULL)
    {
        return false;
    }
    newNode->data = pNode->data;
    newNode->next = NULL;
    currentNode->next = newNode;
    m_iLength++;
    return true;
}

首先構造函數實質上就是創建鏈表的頭結點,所以其數據域data賦值為0,指針域next賦值為NULL,當然,長度m_iLength也應賦值為0。

接着析構函數先放放,這里先講清空鏈表的函數void CleanList()。首先先創建一個節點類指針currentNode並賦值為頭結點的next;然后利用while循環釋放每一個節點,直到currentNode指向NULL。最后將頭結點的next也指向NULL,初始化鏈表長度。在循環里邊,先創建一個節點類指針temp,用來存放currentNode->next,為了在后面釋放currentNode時(也就是當前循環的節點)能把currentNode->next賦值給currentNode(保證不斷鏈)。

  • 其中,delete只是將currentnode指向的內存給回收了,並不是這個指針就不能用了。回收了的意思就是之后使用這個指針不能再對原來指向的這塊內存區域進行操作。指針變量是存放在棧中的,作用周期完了之后才會被回收。

講完清空鏈表函數來說說析構函數,因為析構函數實質就是釋放鏈表所有元素,包括頭結點,所以我們可以利用利用清空鏈表函數先把鏈表清空,接着釋放頭結點,並將頭結點指向NULL,done。

判空和獲取長度類似,略。

接着講講GetElem(int i, Node *pNode)獲取指定元素的函數。首先傳入的參數有兩個,位序和存放(也就是取來要存放的變量)。在尋找元素之前,需要先判斷位序是否合法;然后按照慣例,創建一個節點類指針currentNode存放頭結點;然后利用for循環找到鏈表的第i個元素賦值給currentNode;最后將currentNode->data賦值給參數pNode->data

int LocateElem(Node *pNode)尋找第一個滿足pNode的數據元素的位序的函數。因為找的是位序,所以函數返回的類型為int。首先創建一個節點類指針currentNode存放頭結點,並創建一個 int count=0 用來表示當前元素(頭結點)的位序;然后利用while進行遍歷,在每一次循環里邊進行判斷 currentNode->data == pNode->data 是否成立,如果成立返回當前count,如果不成立,count++進入下一次循環;最后也沒找到的話,返回-1.

bool PriorElem(Node *pCurrentNode, Node *pPreNode)獲取指定元素的前驅函數,傳入的參數有兩個,一個是指定的節點,一個是存放節點。和前邊兩個函數類似,首先創建一個節點類指針賦值為頭結點,還需創建一個臨時節點類指針tempNode指向NULL,然后利用while循環遍歷鏈表。在每一次循環中,先將當前節點currentNode賦值給臨時節點,將currentNode->next賦值給當前節點currentNode,然后判斷currentNode(其實是當前節點的next)的數據域是否和指定節點的相等。如果相等時繼續判斷臨時節點(即當前節點)是否為頭結點,如果不是頭結點,則當前節點為指定節點的前驅。

bool NextElem(Node *pCurrentNode, Node *pNextNode)獲取指定元素的后繼和獲取前驅類似,這里不再需要創建臨時節點。在遍歷的每一次循環中,先進行節點的移動(從前一節點移動到當前節點),然后直接將判斷當前節點的數據域是否和指定節點的相等。如果相等,繼續判斷當前節點next的指針域是否指向NULL(即判斷指定**節點是否為最后一個節點)。如果不是,則將當前節點的 next 的數據域(即當前節點的后繼數據域)取出。

void ListTraverse()遍歷線性表,略。

接着來講bool ListInsertHead(Node *pNode)在鏈表頭插入元素。首先創建一個節點類指針newNode,將要插入節點的數據域賦值給newNode->data,然后將newNode->next = m_pList->next,最后將頭結點的指針域指向newNode,不要忘記m_iLength++

bool ListInsertTail(Node *pNode)在鏈表頭插入元素。首先創建一個節點類指針currentNode指向頭節點,接着遍歷到鏈表尾指針,然后創建一個節點類指針newNode,將要插入節點的數據域賦值給newNode->data,然后將newNode->next 指向NULL,最后將鏈表尾的指針域指向newNode,m_iLength++

bool ListInsert(int i, Node *pNode)在第 i 個位置插入元素。和上邊兩個函數類x似,不同的是需要先判斷插入的位置合不合法。之后創建一個節點類指針currentNode指向頭節點,然后遍歷到插入位置的前一個節點。接着創建一個節點類newNode,將要插入節點的數據域賦值給newNode->data,然后將newNode->next 指向currentNode->next(要插入的位置的節點),最后將currentNode->next(插入位置的前一個節點的指針域)指向newNode,m_iLength++

最后講bool ListDelete(int i, Node *pNode)刪除第 i 個位置的元素。首先需要判空以及判斷刪除位置是否合法,接着創建兩個節點類,currentNode指向頭節點,currentNodeBefore指向NULL。然后遍歷到第 i 個節點(要刪除的節點)時,currentNodeBefore為第 i-1 個節點,currentNode為第 i 個節點。接着將第 i 個節點指針域賦給第 i-1 節點指針域(即跳過了第 i 個節點),把第 i 節點的數據域賦給pNode輸出,釋放刪除節點的內存,m_iLength--

【注意】在插入操作中,不能直接對pNode進行鏈接操作,而不用新建newNode節點。直接將傳入的結點作為鏈表中新添加的結點內存,是不安全的。因為傳入的結點內存是有可能在鏈表外被釋放掉的,如果被釋放掉,則鏈表就會斷開失效;而申請一個新的結點內存作為鏈表的結點內存,則該內存只有在鏈表中才可以被釋放掉,這樣保證了鏈表內存是安全釋放的。

運行一個例子看看

#demo_one.cpp
#include "stdafx.h"
#include "iostream"
#include "List_one.h"
#include "Node.h"

using namespace std;

int main(void)
{
    Node node1; node1.data = 3;
    Node node2; node2.data = 4;
    Node node3; node3.data = 5;
    Node node4; node4.data = 6;
    //Node node5; node5.data = 7;
    Node temp;  temp.data = 0;
    List_one *pList = new List_one;

    /*pList->ListInsertHead(&node1);
    pList->ListInsertHead(&node2);
    pList->ListInsertHead(&node3);
    pList->ListInsertHead(&node4);*/
    pList->ListInsertTail(&node1);
    pList->ListInsertTail(&node2);
    pList->ListInsertTail(&node3);
    pList->ListInsertTail(&node4);
    //pList->ListInsert(1, &node5);

    pList->ListDelete(2, &temp);
    //pList->NextElem(&node1, &temp);
    cout << "temp:" << temp.data << endl;

    pList->ListTraverse();

    delete pList;
    pList = NULL;

    system("pause");
    return 0;
}

運行結果:


順序表的應用以及單鏈表的應用的代碼我上傳到了github


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com