数据结构C++单链表笔记

个人学习所记录笔记,便于复习查看,如有错误,请见谅。

一、单链表的初始化(以下默认均为带头结点的)

//在这个示例中,我们使用 struct 关键字定义了一个结构体 LNode,并添加了成员变量 data 和 next。
//然后,使用 using 关键字将 struct LNode* 定义为类型别名 LinkList。
//在 C++ 中不需要在结构体声明之前使用 typedef 关键字,而是可以直接使用 using 关键字来定义类型别名。

强调这是一个单链表一使用 LinkList
强调这是一个结点     -使用 LNode *

struct LNode {
    int 	data;
    struct LNode* next;
};
using LinkList = struct LNode*;

//带头节点的单链表
bool InitList(LinkList& L) {
    L = new LNode; // 分配一个头结点
    if (L == nullptr) {
        // 内存不足,分配失败
        return false;
    }
    L->next = nullptr; // 头结点之后暂时还没有节点
    return true;
}

//判断是否为空
bool IsEmpty(LinkList L) {
    return L->next == nullptr;
}

二、各种操作

1.按位查找

//按位查找
LNode* GetElem(LinkList L, int i) {
    if (i < 0) {
        // 位序小于1或链表为空,返回nullptr
        return nullptr;
    }
    LNode* p = L;  // p指向第一个节点
    int j = 0;  // 计数器,用于记录当前节点的位序,即当前p指向第几个节点

    while (p != nullptr && j < i) {
        p = p->next;
        j++;
    }
    if (p == nullptr || j > i) {
        // 未找到指定节点或位序超出链表长度,返回nullptr
        return nullptr;
    }
    return p;  // 返回找到的节点指针
}

2.后插与前插操作

//指定结点的后插操作
bool InsertNextNode(LNode* p, int e) {
    if (p == nullptr) {
        // 插入位置无效
        return false;
    }
    // 创建新节点
    LNode* newNode = new LNode;
    newNode->data = e;
    // 插入新节点
    newNode->next = p->next;
    p->next = newNode;
    return true;
}
//给定结点的前插操作
bool InsertPriorNode(LNode* p, int e) {
    if (p == nullptr) {
        // 插入位置无效
        return false;
    }
    // 创建新节点
    LNode* newNode = new LNode;
    newNode->data = e;
    // 插入新节点
    newNode->next = p->next;
    p->next = newNode;
    // 交换数据
    int tmp = p->data;
    p->data = newNode->data;
    newNode->data = tmp;
    return true;
}

3.按位序插入操作

有两种写法,一种是基于按位查找与后插结合的方法,既下面展示的

另一种为展开写,为相应位置注释掉的,两种方法均可实现

//按位序插入
bool ListInsert(LinkList& L, int i, int e) {
    if (i < 1) {
        // 位序小于1无效
        return false;
    }
    //使用了按位查找的方法
    LNode* p = GetElem(L, i-1);

    //int j = 0;  // 计数器,用于记录当前节点的位序,既当前p指向第几个结点
    //LNode* p = L; // p指向头节点

     寻找要插入位置的前一个节点i-1	
    //while (p != nullptr && j < i - 1) {
    //    p = p->next;
    //    j++;
    //}
    //调用了后插操作
    return InsertNextNode(p, e);

    //if (p == nullptr) {
    //    // 位序超过链表长度,或者链表为空
    //    return false;
    //}
     创建新节点
    //LNode* s = new LNode;
    //s->data = e;
     插入新节点
    //s->next = p->next;
    //p->next = s;
    这两句不能颠倒
    //return true;
}

4.按位序删除

部分代码也可以替换为引用按位查找方法

//按位序删除(带头结点的)
bool ListDelete(LinkList& L, int i, int& e) {
    if (i < 1) {
        // 位序小于1无效
        return false;
    }
    LNode* p = GetElem(L, i - 1);
    if (p == nullptr || p->next == nullptr) {
        // 未找到待删除节点或待删除节点是最后一个节点,删除失败
        return false;
    }
    LNode* q = p->next;  // 待删除节点
    e = q->data;  // 将待删除节点的值赋给e 
    p->next = q->next;  // 将待删除节点的下一个节点链接到前一个节点上
    delete q;  // 删除节点
    return true;  // 删除成功
}

5.删除指定结点

//删除指定结点
void Delete(LNode* p) {
    if (p == nullptr || p->next == nullptr) {
        // 要删除的节点为空或为尾节点,无法删除
        return ;
    }
    LNode* q = p->next;  // q指向要删除的节点
    p->data = q->data;  // 将后继节点的值赋给当前节点
    p->next = q->next;  // 修改当前节点的指针指向后继节点的后继节点
    delete q;  // 删除要删除的节点
}

6.打印链表和输出链表的长度

// 输出链表数据
void PrintList(LinkList L) {
    LNode* p = L->next;  // p指向第一个节点
    while (p != nullptr) {
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}

//求表的长度
int GetLength(LinkList L) {
    int count = 0;
    LNode* p = L->next; // p指向首节点
    while (p != nullptr) {
        count++;
        p = p->next;
    }
    return count;
}

7.按位查找

//按值查找,返回节点
LNode* LocateElem(LinkList L, int e) {
    LNode* p = L->next; // p指向首节点
    while (p != nullptr && p->data != e) {
        p = p->next;
    }
    return p;
}

8.按值查找

第一种返回的是指针地址,第二种可以返回数据在第几位,既i。

//按值查找,返回第几位
int LocateElem1(LinkList L, int e) {
    LNode* p = L->next; // p指向首节点
    int i = 1; // 初始化计数器
    while (p != nullptr && p->data != e) {
        p = p->next;
        i++;
    }
    if (p == nullptr) {
        return 0; // 未找到元素,返回0
    }
    else {
        return i; // 返回元素在链表中的位置
    }
}
//求表的长度
int GetLength(LinkList L) {
    int count = 0;
    LNode* p = L->next; // p指向首节点
    while (p != nullptr) {
        count++;
        p = p->next;
    }
    return count;
}

9.头插法与尾插法,在表头或者表尾插入数据

两种方法类似,核心为:初始化操作,后插操作

//尾插法
LinkList List_TailInsert(LinkList& L) {
    int x;
    L = new LNode; // 创建头节点
    LNode* s = L; // 初始化尾指针
    while (cin >> x) { // 循环输入元素值
        if (x == -1) { // 输入-1结束输入
            break;
        }
        LNode* p = new LNode; // 创建新节点
        p->data = x; // 赋值新节点数据
        s->next = p; // 将新节点插入到尾部
        s = p; // 更新尾指针
    }
    s->next = nullptr; // 尾节点的next指向nullptr
    return L;
}
//头插法
LinkList List_HeadInsert(LinkList& L) {
    int x;
    L = new LNode; // 创建头节点
    L->next = nullptr; // 初始化头节点的next指针为nullptr
    while (cin >> x) { // 循环输入元素值
        if (x == -1) { // 输入-1结束输入
            break;
        }
        LNode* p = new LNode; // 创建新节点
        p->data = x; // 赋值新节点数据
        p->next = L->next; // 将新节点插入到头部
        L->next = p; // 更新头节点的next指针
    }
    return L;
}

10.函数调用

int main() {
    LinkList L;   //声明一个指向单链表的指针

    //初始化一个空表
    if (InitList(L)) {
        cout << "初始化成功" << endl;
    }
    else {
        cout << "初始化失败" << endl;
    }

   //插入数据
    ListInsert(L, 1, 10);

    ListInsert(L, 2, 20);

    ListInsert(L, 3, 30);

    // 判断链表是否为空
    if (IsEmpty(L)) {
        cout << "链表为空" << endl;
    }
    else {
        cout << "链表非空" << endl;
    }

    // 输出链表中的数据
    PrintList(L);

    int e;
    // 调用ListDelete函数删除指定位置的节点
    if (ListDelete(L, 3, e)) {
        cout << "成功删除节点,被删除节点的值为:" << e << endl;
    }
    else {
        cout << "删除节点失败" << endl;
    }
    PrintList(L);

    //按位置查找
    LNode* result = GetElem(L, 1);
    if (result != nullptr) {
        cout << "找到了位序为3的节点,该节点的值为:" << result->data << endl;
    }
    else {
        cout << "未找到位序为3的节点" << endl;
    }
    //按值查找
// 按值查找元素,找到的是节点指针
    int target = 20;
    LNode* result1 = LocateElem(L, target);
    if (result1 != nullptr) {
        cout << "找到元素 " << target << ",节点的地址为:" << result1 << endl;
    }
    else {
        cout << "未找到元素 " << target << endl;
    }

    // 按值查找元素,返回i
    int target1 = 30;
    int result2 = LocateElem1(L, target1);
    if (result2 != 0) {
        cout << "找到元素 " << target1 << ",节点的位置为:" << result2 << endl;
    }
    else {
        cout << "未找到元素 " << target1 << endl;
    }
    // 获取链表的长度
    int length = GetLength(L);
    cout << "链表的长度为:" << length << endl;


    //尾插法代码实现
    //L = List_TailInsert(L);

    //PrintList(L); // 输出链表

    //头插法实现与尾插类似 
    return 0;
}

11.完整代码实现

#include<iostream>
#include<new>
using namespace std;

struct LNode {
    int 	data;
    struct LNode* next;
};
using LinkList = struct LNode*;

//带头节点的单链表
bool InitList(LinkList& L) {
    L = new LNode; // 分配一个头结点
    if (L == nullptr) {
        // 内存不足,分配失败
        return false;
    }
    L->next = nullptr; // 头结点之后暂时还没有节点
    return true;
}

//判断是否为空
bool IsEmpty(LinkList L) {
    return L->next == nullptr;
}
//按位查找
LNode* GetElem(LinkList L, int i) {
    if (i < 0) {
        // 位序小于1或链表为空,返回nullptr
        return nullptr;
    }
    LNode* p = L;  // p指向第一个节点
    int j = 0;  // 计数器,用于记录当前节点的位序,即当前p指向第几个节点

    while (p != nullptr && j < i) {
        p = p->next;
        j++;
    }
    if (p == nullptr || j > i) {
        // 未找到指定节点或位序超出链表长度,返回nullptr
        return nullptr;
    }
    return p;  // 返回找到的节点指针
}
//指定结点的后插操作
bool InsertNextNode(LNode* p, int e) {
    if (p == nullptr) {
        // 插入位置无效
        return false;
    }
    // 创建新节点
    LNode* newNode = new LNode;
    newNode->data = e;
    // 插入新节点
    newNode->next = p->next;
    p->next = newNode;
    return true;
}
//按位序插入
bool ListInsert(LinkList& L, int i, int e) {
    if (i < 1) {
        // 位序小于1无效
        return false;
    }
    //使用了按位查找的方法
    LNode* p = GetElem(L, i-1);

    //int j = 0;  // 计数器,用于记录当前节点的位序,既当前p指向第几个结点
    //LNode* p = L; // p指向头节点

     寻找要插入位置的前一个节点i-1	
    //while (p != nullptr && j < i - 1) {
    //    p = p->next;
    //    j++;
    //}
    //调用了后插操作
    return InsertNextNode(p, e);

    //if (p == nullptr) {
    //    // 位序超过链表长度,或者链表为空
    //    return false;
    //}
     创建新节点
    //LNode* s = new LNode;
    //s->data = e;
     插入新节点
    //s->next = p->next;
    //p->next = s;
    这两句不能颠倒
    //return true;
}


//给定结点的前插操作
bool InsertPriorNode(LNode* p, int e) {
    if (p == nullptr) {
        // 插入位置无效
        return false;
    }
    // 创建新节点
    LNode* newNode = new LNode;
    newNode->data = e;
    // 插入新节点
    newNode->next = p->next;
    p->next = newNode;
    // 交换数据
    int tmp = p->data;
    p->data = newNode->data;
    newNode->data = tmp;
    return true;
}
//按位序删除(带头结点的)
bool ListDelete(LinkList& L, int i, int& e) {
    if (i < 1) {
        // 位序小于1无效
        return false;
    }
    LNode* p = GetElem(L, i - 1);
    if (p == nullptr || p->next == nullptr) {
        // 未找到待删除节点或待删除节点是最后一个节点,删除失败
        return false;
    }
    LNode* q = p->next;  // 待删除节点
    e = q->data;  // 将待删除节点的值赋给e 
    p->next = q->next;  // 将待删除节点的下一个节点链接到前一个节点上
    delete q;  // 删除节点
    return true;  // 删除成功
}

//删除指定结点
void Delete(LNode* p) {
    if (p == nullptr || p->next == nullptr) {
        // 要删除的节点为空或为尾节点,无法删除
        return ;
    }
    LNode* q = p->next;  // q指向要删除的节点
    p->data = q->data;  // 将后继节点的值赋给当前节点
    p->next = q->next;  // 修改当前节点的指针指向后继节点的后继节点
    delete q;  // 删除要删除的节点
}
// 输出链表数据
void PrintList(LinkList L) {
    LNode* p = L->next;  // p指向第一个节点
    while (p != nullptr) {
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}
//按值查找,返回节点
LNode* LocateElem(LinkList L, int e) {
    LNode* p = L->next; // p指向首节点
    while (p != nullptr && p->data != e) {
        p = p->next;
    }
    return p;
}
//按值查找,返回第几位
int LocateElem1(LinkList L, int e) {
    LNode* p = L->next; // p指向首节点
    int i = 1; // 初始化计数器
    while (p != nullptr && p->data != e) {
        p = p->next;
        i++;
    }
    if (p == nullptr) {
        return 0; // 未找到元素,返回0
    }
    else {
        return i; // 返回元素在链表中的位置
    }
}
//求表的长度
int GetLength(LinkList L) {
    int count = 0;
    LNode* p = L->next; // p指向首节点
    while (p != nullptr) {
        count++;
        p = p->next;
    }
    return count;
}
//尾插法
LinkList List_TailInsert(LinkList& L) {
    int x;
    L = new LNode; // 创建头节点
    LNode* s = L; // 初始化尾指针
    while (cin >> x) { // 循环输入元素值
        if (x == -1) { // 输入-1结束输入
            break;
        }
        LNode* p = new LNode; // 创建新节点
        p->data = x; // 赋值新节点数据
        s->next = p; // 将新节点插入到尾部
        s = p; // 更新尾指针
    }
    s->next = nullptr; // 尾节点的next指向nullptr
    return L;
}
//头插法
LinkList List_HeadInsert(LinkList& L) {
    int x;
    L = new LNode; // 创建头节点
    L->next = nullptr; // 初始化头节点的next指针为nullptr
    while (cin >> x) { // 循环输入元素值
        if (x == -1) { // 输入-1结束输入
            break;
        }
        LNode* p = new LNode; // 创建新节点
        p->data = x; // 赋值新节点数据
        p->next = L->next; // 将新节点插入到头部
        L->next = p; // 更新头节点的next指针
    }
    return L;
}



int main() {
    LinkList L;   //声明一个指向单链表的指针

    //初始化一个空表
    if (InitList(L)) {
        cout << "初始化成功" << endl;
    }
    else {
        cout << "初始化失败" << endl;
    }

   //插入数据
    ListInsert(L, 1, 10);

    ListInsert(L, 2, 20);

    ListInsert(L, 3, 30);

    // 判断链表是否为空
    if (IsEmpty(L)) {
        cout << "链表为空" << endl;
    }
    else {
        cout << "链表非空" << endl;
    }

    // 输出链表中的数据
    PrintList(L);

    int e;
    // 调用ListDelete函数删除指定位置的节点
    if (ListDelete(L, 3, e)) {
        cout << "成功删除节点,被删除节点的值为:" << e << endl;
    }
    else {
        cout << "删除节点失败" << endl;
    }
    PrintList(L);

    //按位置查找
    LNode* result = GetElem(L, 1);
    if (result != nullptr) {
        cout << "找到了位序为3的节点,该节点的值为:" << result->data << endl;
    }
    else {
        cout << "未找到位序为3的节点" << endl;
    }
    //按值查找
// 按值查找元素,找到的是节点指针
    int target = 20;
    LNode* result1 = LocateElem(L, target);
    if (result1 != nullptr) {
        cout << "找到元素 " << target << ",节点的地址为:" << result1 << endl;
    }
    else {
        cout << "未找到元素 " << target << endl;
    }

    // 按值查找元素,返回i
    int target1 = 30;
    int result2 = LocateElem1(L, target1);
    if (result2 != 0) {
        cout << "找到元素 " << target1 << ",节点的位置为:" << result2 << endl;
    }
    else {
        cout << "未找到元素 " << target1 << endl;
    }
    // 获取链表的长度
    int length = GetLength(L);
    cout << "链表的长度为:" << length << endl;


    //尾插法代码实现
    //L = List_TailInsert(L);

    //PrintList(L); // 输出链表

    //头插法实现与尾插类似 
    return 0;
}

三、特点
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存放,要耗费一定空间存放指针

四、时间复杂度(简写版)

按位插入:
最好:1
最坏:n
平均:n
后插:1
前插:1 
按位序删除
最坏:n
最好:1
平均:n
删除指定结点:1
按位查找:n
按值查找:n
尾插法:n
头插法:n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值