【数据结构】单链表

My Blog / 2024-07-18 / 原文

单链表

结构描述

#include <iostream>
#include <cstdlib>

using namespace std;

typedef int DataType;

//链表节点
typedef struct Node {
    DataType A;
    struct Node * Next;
}Node;

class SingleLinkedList {
private:
    Node * Head;
public:
    //尾插
    void PushBack(DataType X);
    //头插
    void PushFront(DataType X);
    //在第Pos个位置插入,若不存在该位置报错
    void InsertThisPos(int Pos, DataType X);

    //尾删
    void PopBack();
    //头删
    void PopFront();
    //删除第Pos位元素,若无该位置,则返回错误
    void DeleteThisPos(int Pos);
    // 把链表置为空表
    void MakeEmpty();
    
    //查找值为 Target的元素,若不存在,则返回空指针,存在则返回节点指针
    Node * SearchByValue(DataType Target);
    void ModifyByValue(DataType Value, DataType NewValue);

    //创建一个空表
    void Init();
    //判空,若空,则返回true,反之返回false
    bool IsEmpty();
    //打印链表
    void Print();
    //分配空间,根据参数X分配空间,分配成功则返回该节点的指针
    Node * BuyNode(DataType X);
};

初始化

建立一个空表,即把 Head 指向空。

void SingleLinkedList::Init() {
    Head = nullptr;
}

判空

判断链表是否为空,即 Head == nullptr 是否成立。

  • 若成立则返回 true

  • 否则返回 false

bool SingleLinkedList::IsEmpty() {
    return Head == nullptr;
}

打印

  • 表空:返回错误

  • 非空:从头指针向后遍历,一直遍历到表尾,即Cur == nullptr 时,并打印数据域。

void SingleLinkedList::Print() {
    //表空时返回错误
    if (IsEmpty()) {
        cout << "List Is Empty!\n";
        exit(-1);
    }
    //表非空时遍历链表,并打印每一位元素的值
    Node * Cur = Head;
    while (Cur != nullptr) {
        cout << Cur->A << "\n";
        Cur = Cur->Next;
    }
}

创建节点

根据参数 X ,创建一个数据域为 X ,指针域为空的节点 NewNode 并返回

Node * SingleLinkedList::BuyNode(DataType X) {
    //分配空间,并判断是否成功
    Node * NewNode = (Node *)malloc(sizeof (Node));
    if (NewNode == nullptr) {
        cout << "Malloc Failed!" << "\n";
        exit(-1);
    }
    
    //给节点赋值
    NewNode->A = X;
    NewNode->Next = nullptr;
    
    return NewNode;
}

头插

  1. 分配节点 NewNode
  2. 把节点的指针域指向当前头节点,即 NewNode->Next = Head;
  3. 把头指针 Head 指向 NewNode
void SingleLinkedList::PushFront(DataType X) {
    Node * NewNode = BuyNode(X);
    NewNode->Next = Head;
    Head = NewNode;
}

尾插

  • 若表空:
    1. 分配节点;
    2. 把头指针指向该节点;
  • 表非空:
    1. 遍历,找到尾节点
    2. 分配节点,并把节点接在尾节点之后
void SingleLinkedList::PushBack(DataType X) {
    //表空
    if (IsEmpty()) {
        Head = BuyNode(X);
    }
    //非空
    else {
        Node * Cur = Head;
        //Cur->Next为空时,找到尾节点,退出循环
        while (Cur->Next != nullptr) {
            Cur = Cur->Next;
        }
        
        Node * NewNode = BuyNode(X);
        Cur->Next = NewNode;
    }
}

头删

  • 表空:报错
  • 非空:
    1. 用临时变量 Tmp 存放头节点;
    2. 把头节点向后移动一位,即 Head = Head->Next;
    3. 释放 Tmp 并置空,防止指针悬空
void SingleLinkedList::PopFront() {
    //表空
    if (IsEmpty()) {
        cout << "List Is Empty!\n";
        exit(-1);
    }
    
    //非空
    Node * Tmp = Head;
    Head = Head->Next;
    free(Tmp);
    Tmp = nullptr;
}

尾删

  • 表空:报错
  • 非空:
    • 只有一个节点:直接置空即可
    • 有多个节点:
      1. 找到倒数第二个节点 Cur
      2. 用临时变量保存尾节点,即 Tmp = Cur->Next
      3. Cur 的指针域指向 nullptr
      4. 释放 Tmp
void SingleLinkedList::PopBack() {
    //表为空
    if (IsEmpty()) {
		cout << "List Is Empty!\n";
        exit(-1);
    }
    
    //只有一个节点
    if (Head->Next == nullptr) {
        free(Head);
        Head = nullptr;
    }
    //有两个及以上个节点
    else {
        Node * Cur = Head;
        //找倒数第二个节点
        while (Cur->Next->Next != nullptr) {
            Cur = Cur->Next;
        }
        //保存倒数第一个节点
        Node * Tmp = Cur->Next;
        //把倒数第二个节点指向空指针,Tmp指向尾节点,故其指针域为空指针
        Cur->Next = Tmp->Next;
        //释放
        free(Tmp);
        Tmp = nullptr;
    }
}

插入元素

Pos 处插入元素(模拟数组下标,从0开始计数)

  • 若存在该位置,则插入元素
    1. Pos 为0,即在链表头插入,直接调用头插方法
    2. Pos 大于0,则遍历链表,循环变量 i1 开始,考虑下标为 1 到尾的节点
    3. Pre 变量存放 第 i - 1 号位的节点指针,循环条件为 i < Pos && Pre != nullptr,如果 Pre 为空,即指向了链表的最后,若此时 i != Pos ,则下存在,返回错误
    4. i == Pos,则该位置存在,直接分配节点并插入即可 。有点双指针的意思
  • 不存在该位置,则返回错误;
void SingleLinkedList::InsertThisPos(int Pos, DataType X) {
    if (Pos == 0) {
        PushFront(X);
    }
	else {
        int i;
        Node * Pre;
        for (i = 1, Pre = Head; i < Pos && Pre != nullptr; i++, Pre = Pre->Next) {
            ;
        }
        //退出循环时,不是i == Pos, 就是 Pre == nullptr
        if (Pos == i && Pre != nullptr) {
            Node * NewNode = BuyNode(X);
            NewNode->Next = Pre->Next;
            Pre->Next = NewNode;
        }
        else {
            cout << Pos << " Posiont is not exist!\n";
            exit(-1);
        }
    }
}

删除元素

  • 表空:报错
  • 非空:
    • 删除第0个元素时,直接调用头删方法
    • 有多个元素时:
      1. 和插入一样,用一个 Pre 指针指向要删除元素的前一位,即 Pre = Head, i = 1
      2. 遍历链表,直至 i == Pos || Pre->Next == nullptr 时退出循环,Pre->Next == nullptr时,说明要删除的元素是尾指针的指针域,即空指针,这显然是错误的,所以可以以此来作为遍历的出口条件
      3. 若第 Pos 个元素存在,则删除该元素
void SingleLinkedList::DeleteThisPos(int Pos) {
    //判空
    if (IsEmpty()) {
        cout << "List Is Empty!\n";
    	exit(-1);
    }
    
    //非空,删除第0个元素
    if (Pos == 0) {
        PopFront();
    }
    else {
        //非空,删除第N个元素(N > 0)
        int i = 0;
        Node * Pre = nullptr;
        for (i = 1, Pre = Head; Pre->Next != nullptr && i < Pos; i++, Pre = Pre->Next) {
            ;
        }
        
        if (i == Pos && Pre->Next != nullptr) {
            Node * Tmp = Pre->Next;
            Pre->Next = Tmp->Next;
            free(Tmp);
            Tmp = nullptr;
        }
        else {
            cout << Pos << " Position is not exist!\n";
        }
    }
    
}

根据值查找元素

  • 表空:返回错误
  • 非空:遍历链表,逐一比较,直到第一个与目标值相等的元素时,返回该节点的指针。若直至表尾仍未发现该元素,则返回一个空指针。
Node * SingleLinkedList::SearchByValue(DataType Target) {
    if (IsEmpty()) {
        cout << "List Is Empty!\n";
        exit(-1);
    }
    
    Node * Cur = Head;
    while (Cur != nullptr) {
        if (Cur->A == Target) {
            return Cur;
        }
        Cur = Cur->Next;
    }
    
    return nullptr;
}

修改链表元素值

  • 查找:
    • 表空:返回错误
    • 非空:找到返回,找不到返回空
  • 修改:根据查找到的结果修改;
    • 查找返回空:给出提示消息,并退出程序
    • 查找返回元素节点指针:修改该元素的值
void SingleLinkedList::ModifyByValue(DataType Target, DataType NewValue) {
    Node * Ret = SearchByValue(Target);
    if (Ret == nullptr) {
        cout << "The Target Is Not Exist!\n";
        exit(-1);
    }
    
    Ret->A = NewValue;
}

摧毁

  • 表空:返回错误
  • 不空:一直头删,直至表空
void SingleLinkedList::MakeEmpty() {
    if (IsEmpty()) {
        cout << "List Is Empty!\n";
        exit(-1);
    }
    
    while (!IsEmpty()) {
        PopFront();
    }
}