IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 线性表的单链表 -> 正文阅读

[数据结构与算法]线性表的单链表

单链表的定义

typedef struct LNode {
    int data;
    struct LNode *next;
} LNode, *LinkList;

单链表的初始化

不带头结点

bool InitList(LinkList &L) {
    L = NULL;
    return true;
}

带头结点

bool InitList(LinkList &L) {
    L = (LNode *) malloc(sizeof(LNode));	//分配一个头结点
    if (L == NULL)	//内存不足, 分配失败
        return false;
    L->next = NULL;		//申请一个头结点,将指针域置空。
    return true;
}

头结点和头指针的区分:不管带不带头结点,头指针始终指向单链表的第一个结点,而头结点是带头结点的单链表中的第一个结点,结点内通常不存储信息。

判断单链表是否为空

不带头结点

bool Empty(LinkList L) {
    return L == NULL;
}

带头结点

bool Empty(LinkList L) {
    return L->next == NULL;
}

单链表的插入操作

按位序(不带头结点)

bool ListInsert(LinkList &L, int i, int e) {
    if (i < 1) return false;
    if (i==1) {
        LNode *s = (LNode *) malloc(sizeof(LNode));
        s->data = e;
        s->next = L;
        L = s;
        return true;
    }
    LNode *p;   //指针p指向当前扫描到的结点
    int j = 1;  //记录指针p当前在第几结点
    p = L;  //L指向头结点, 且头结点不存数据

    while (p != NULL && j < i - 1) { //循环找到第i-1个结点, 在其后添加数据
        p = p->next;
        j++;
    }
    if (p == NULL) return false;

    LNode *s = (LNode *) malloc(sizeof(LNode));
    s->data = e;    //赋值
    s->next = p->next;  //先将p原先后面的结点 与 新节点s相连接
    p->next = s;    //再将p的结点 与 新节点s相连接
    return true;
}

按位序(带头结点)

bool ListInsert(LinkList &L, int i, int e) {
    if (i < 1) return false;
    LNode *p;   //指针p指向当前扫描到的结点
    int j = 0;  //记录指针p当前在第几结点
    p = L;  //L指向头结点, 且头结点不存数据

    while (p != NULL && j < i - 1) { //循环找到第i-1个结点, 在其后添加数据
        p = p->next;
        j++;
    }
    if (p == NULL) return false;	//判断i的值是否合法

    LNode *s = (LNode *) malloc(sizeof(LNode));
    s->data = e;    //赋值
    s->next = p->next;  //先将p原先后面的结点 与 新节点s相连接
    p->next = s;    //再将p的结点 与 新节点s相连接
    return true;
}

后插操作

p结点之后插入元素e
bool InsertNextNode(LNode *p, int e) {
    if (p == NULL) return false;
    LNode *s = (LNode *) malloc(sizeof(LNode));
    if (s == NULL) return false;
    s->data = e;
    s->next = p->next;
    p->next = s;	//将结点s连接在p之后
    return true;
}
简化: 在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L, int i, int e) {
    if (i < 1) return false;
    LNode *p;   //指针p指向当前扫描到的结点
    int j = 0;  //记录指针p当前在第几结点
    p = L;  //L指向头结点, 且头结点不存数据

    while (p != NULL && j < i - 1) { //循环找到第i-1个结点, 在其后添加数据
        p = p->next;
        j++;
    }
    return InsertNextNode(p, e );
}

前插操作

p结点之前插入元素e (偷天换日)
bool InsertPriorNode(LNode *p, int e) {
    if (p == NULL) return false;
    LNode *s = (LNode *) malloc(sizeof(LNode));
    if (s == NULL) return false;

    s->next = p->next;
    p->next = s;    //新结点s连到p之后
    s->data = p->data;  //将p的值复制到s中
    p->data = e;    //e赋值给p, 这时候s相当于原先的p, 现在的p为前插的新结点
    return true;
}

单链表的删除操作

按位序(带头结点)

//L 单链表	i 删除的位序	e 删除的值
bool ListDelete(LinkList &L, int i, int &e) {
    if (i < 1) return false;
    LNode *p;
    int j = 0;
    p = L;    //L指向头结点
    while (p != NULL && j < i - 1) {
        p = p->next;
        j++;
    }
    if (p == NULL) return false;    //i的值不合法
    if (p->next == NULL) return false;    //第i-1个结点后无其他结点

    LNode *q = p->next;     //令p指向被删除的结点
    e = q->data;    //用e返回删除结点的值
    p->next = q->next;  //将q结点从链表中断开
    free(q);    //释放结点的存储空间
    return true;

}

按位序删除(不带头结点)

bool ListDeleteWithout(LinkList &L, int i, int &e) {
    if (i < 1) return false;
    if (i == 1) {
        LNode *p = L->next;
        e = p->data;
        L->data = p->data;
        L->next = p->next;
        free(p);
        return true;
    }

    LNode *p;
    int j = 1;
    p = L;    //L指向头结点
    while (p != NULL && j < i - 1) {
        p = p->next;
        j++;
    }
    if (p == NULL) return false;    //i的值不合法
    if (p->next == NULL) return false;    //第i-1个结点后无其他结点

    LNode *q = p->next;     //令p指向被删除的结点
    e = q->data;    //用e返回删除结点的值
    p->next = q->next;  //将q结点从链表中断开
    free(q);    //释放结点的存储空间
    return true;

}

指定结点删除

扩展: 删除结点 *p

? 要删除某个给定结点 *p, 通常的做法实现从链表的头结点开始顺序找到其前驱结点, 然后执行删除操作, 算法的时间复杂度为O(n)

? 其实, 删除结点 *p的操作可用删除 *p的后继结点操作来实现, 实质就是将其后继结点的值赋予其身, 然后删除后继结点, 也能使得时间复杂度为O(1)

q = p->next;
p->data = q->data;	//q->data也可写成p->next->data
p->next = q->next;
free(q);

重点理解前插和删除的特殊操作

单链表的查找操作

按位序查找

算法思想:从单链表的第一个结点开始,顺着指针域逐个往下搜索,直到找到第 i 个结点为止,否则返回最后一个结点的指针域NULL。

核心代码

// 查找单链表L中第 i 个位置的结点指针
LNode *GetElem(LinkList L, int i) {
    if (i < 0) return NULL;
    LNode *p;
    int j = 0;
    p = L;
    while (p != NULL && j < i) {
        p = p->next;
        j++;
    }
    return p;
}
按值查找

算法思想:从单链表的第一个结点开始,依次比较表中各个结点的数据域的值,若某结点数据域的值等于x,则返回该结点的指针并记录结点的位置index;若整个单链表中没有这样的结点,则返回空。

核心代码

/**
 * 查找值x在单链表L中的结点指针
 * @param L 单链表
 * @param e 查找的值
 * @param index 值所对应的位置
 * @return 
 */
LNode *LocateElem(LinkList L, int e, int &index) {
    index = 1;
    LNode *p = L->next;
    while (p != NULL && p->data != e) { //从第1个结点查找data值为e的结点
        p = p->next;
        index++;
    }
    return p;
}

单链表的创建操作(带头结点)

头插法建立

算法思想:首先初始化一个单链表,其头结点为空,然后循环插入新结点*s:将s的next指向头结点的下一个结点,然后将头结点的next指向s。

核心代码

LinkList HeadInsert(LinkList &L) {
    InitList(L);    //初始化单链表
    int x;
    scanf("%d", &x);
    while (x != 9999) {
        LNode *s = (LNode *) malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;
        L->next = s;    //将新结点插入表中, L为头指针
        scanf("%d", &x);
    }
    return L;
}

需要指出的是,头插法建立的单链表中结点的次序和输入数据的顺序不一致,是相反的。若希望两者的顺序是一致的,则可采用尾插法建立单链表。

尾插法建立

算法思想:首先初始化一个单链表,然后声明一个尾指针r,让r始终指向当前链表的尾结点,循环向单链表的尾部插入新的结点*s,将尾指针r的next域指向新结点,再修改尾指针r指向新结点,也就是当前链表的尾结点。最后别忘记将尾结点的指针域置空。

核心代码

LinkList TailInsert(LinkList &L) {
    InitList(L);    //初始化单链表
    int x;
    LNode *s, *r = L;
    scanf("%d", &x);
    while (x != 9999) {
        s = (LNode *) malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;      //r指向新的表尾结点
        scanf("%d", &x);
    }
    r->next = NULL;     //尾结点指针置为空
    return L;
}

单链表的长度

算法思想:声明一个指针p,p指向头结点指向的第一个结点,如果p指向的结点不为空,那么长度加一,将p指向下一个结点,直到遍历到最后一个结点为止。

核心代码

int Length(LinkList L) {
    int len = 0;
    LNode *p = L;
    while (p->next != NULL) {
        p = p->next;
        len++;
    }
    return len;
}

遍历单链表

算法思想:声明一个指针p,从头结点指向的第一个结点开始,如果p不为空,那么就输出当前结点的值,并将p指向下一个结点,直到遍历到最后一个结点为止。

核心代码

void PrintList(LinkList L) {
    LNode *p = L->next;
    while (p) {
        printf("%d\t", p->data);
        p = p->next;
    }
    printf("\n");
}

单链表的查找,创建及后的完整代码

/**
 * @Author JiaHaoHao
 * @Date 2022/07/01 12:44
 * @ClassName: test11
 * @Description: 单链表建立
 */
#include "stdio.h"
#include "stdlib.h"

typedef struct LNode {
    int data;
    struct LNode *next;
} LNode, *LinkList;

bool InitList(LinkList &L) {
    L = (LNode *) malloc(sizeof(LNode));
    if (L == NULL)
        return false;
    L->next = NULL;
    return true;
}

/**
 * 前插法建立单链表
 * @param L
 * @return
 */
LinkList HeadInsert(LinkList &L) {
    InitList(L);    //初始化单链表
    int x;
    scanf("%d", &x);
    while (x != 9999) {
        LNode *s = (LNode *) malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;
        L->next = s;    //将新结点插入表中, L为头指针
        scanf("%d", &x);
    }
    return L;
}

/**
 * 尾插法建立单链表
 * @param L
 * @return
 */
LinkList TailInsert(LinkList &L) {
    InitList(L);    //初始化单链表
    int x;
    LNode *s, *r = L;
    scanf("%d", &x);
    while (x != 9999) {
        s = (LNode *) malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;      //r指向新的表尾结点
        scanf("%d", &x);
    }
    r->next = NULL;     //尾结点指针置为空
    return L;
}

//打印链表
void PrintList(LinkList L) {
    LNode *p = L->next;
    while (p) {
        printf("%d\t", p->data);
        p = p->next;
    }
    printf("\n");
}

/**
 * 按位序查找
 * @param L
 * @param i
 * @return
 */
LNode *GetElem(LinkList L, int i) {
    if (i < 0) return NULL;
    LNode *p;
    int j = 0;
    p = L;
    while (p != NULL && j < i) {
        p = p->next;
        j++;
    }
    return p;
}

/**
 * 按值查找
 * @param L 单链表
 * @param e 查找的值
 * @param index 值所对应的位置
 * @return
 */
LNode *LocateElem(LinkList L, int e, int &index) {
    index = 1;
    LNode *p = L->next;
    while (p != NULL && p->data != e) { //从第1个结点查找data值为e的结点
        p = p->next;
        index++;
    }
    return p;
}

//表长
int Length(LinkList L) {
    int len = 0;
    LNode *p = L;
    while (p->next != NULL) {
        p = p->next;
        len++;
    }
    return len;
}


int main() {
    LinkList L;

//    HeadInsert(L);  //头插法建立
    TailInsert(L);  //尾插法建立
    PrintList(L);   //打印链表;

    int i = 3;
    printf("第%d个值为%d\n", i, GetElem(L, i)->data);

    int e = 4;
    int index = -1;
    LocateElem(L, e, index);
    printf("值为%d的结点为%d\n", e, index);

    printf("表长: %d\n", Length(L));

    return 0;
}

运行结果

// 尾插法创建
C:\Users\User\Desktop\dataStructure\cmake-build-debug\01xianxingbiao11.exe
1 5 9 8 4 5 6 2 1 4 9999
1	5	9	8	4	5	6	2	1	43个值为9
值为4的结点为5
表长: 10

Process finished with exit code 0
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 11:03:35  更:2022-07-03 11:05:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:15:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码