数据结构-线性表(二)单链表

上篇博文-线性表(一)基本概念及基本操作
👉数据结构-线性表(一)基本概念及基本操作

本文介绍了单链表定义及基本操作!Let’s go🏃‍♂️

数据结构

线性表(二)单链表

思维导图

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

1 线性表的链式表示

链式存储线性表时,不需要使用地址连续的存储单元,结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。

2 单链表

1、定义

线性表的链式存储又称单链表,链表通过指针将一组零散的内存块串联在一起,内存块称为链表的 “结点”。每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址,叫作后继指针 next。链表有两个特殊的结点,分别是第一个结点(头结点)和最后一个结点(尾结点)。头结点用来记录链表的基地址,用它可以遍历得到整条链表。尾结点指向一个空地址 NULL,表示这是链表上最后一个结点。

链表特征:

  1. 链表是以节点的方式来存储,是链式存储。
  2. 每个节点包含 data 域, next 域:指向下一个节点。
  3. 链表的各个节点不一定是连续存储。
  4. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定。

相关术语:

  1. 头指针是指向链表中第一个结点的指针。

  2. 头结点要表示一个单链表时,只需声明一个头指针 L ,指向单链表的第一个结点,是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息。

  3. 首元结点是指链表中存储第一个数据元素a1的结点。

2、优缺点

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

3、结构图

(1)内存结构图

在这里插入图片描述

(2)逻辑结构图
在这里插入图片描述

4、基本操作

(1)建立单链表

头插法建立单链表

该方法从一个空表开始,生成新节点,将新结点插入到当前链表的表头,即头结点之后。

平均时间复杂度:O(n)

头插法的重要应用: 链表的逆置 (reverse LinkedList)

在这里插入图片描述

尾插法建立单链表

该方法将新结点插入到当前链表的表尾,为此必须增加一个尾指针,使其始终指向当前链表的尾结点。

在这里插入图片描述

(2)插入单链表

按位序插入

ListInsert(&L,i,e): 插入操作。在表L中的第i个位置上插入指定元素e。

平均时间复杂度:O(n)

在这里插入图片描述

对某一结点进行前插操作

时间复杂度:O(1)

在这里插入图片描述

对某一结点进行后插操作

时间复杂度:O(1)

在这里插入图片描述

(3)删除单链表

按位序删除

ListDelete(&L,i,&e): 删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。

平均时间复杂度:O(n)

在这里插入图片描述

指定结点的删除

时间复杂度:O(1)

在这里插入图片描述

(4)查找单链表

按位查找

GetElem(L,i): 按位查找操作。获取表L中第i个位置的元素的值。

平均时间复杂度:O(n)

按值查找

LocateElem(L,e): 按值查找操作。在表L中查找具有给定关键字值的元素。

平均时间复杂度:O(n)

(5)表长

遍历链表计算

时间复杂度:O(n)

(6)代码

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

//定义一个链表
//强调这是一个单链表  ——使用 LinkList
//强调这是一个结点    ——使用 LNode *
typedef struct LNode //定义单链表结点类型
{
    /* data */
    int data;  //数据域
    struct LNode *next;  //指针域
} LNode, *LinkList;      //LNode *p == LinkList p
//指针变量p:表示结点地址
//结点变量 *p:表示一个结点

/**
 * 初始化
 */ 
bool InitList(LinkList &L) {
    L = (LNode *)malloc(sizeof(LNode));
    if (L == NULL)  //内存不足,分配失败
        return false;
    L->next = NULL;    //头结点之后暂时还没有结点
    //L = NULL; //空表
    return true;
}

/**
 * 判断链表是否为空
 */ 
bool isEmpty(LinkList L) {
    if (L->next == NULL)
        return true;
    else
        return false;
}

/**
 * 头插法
 */ 
LinkList List_HeadInsert(LinkList &L) {
    LNode *s;  //新结点
    for (int i = 1; i <= 10; i++)
    {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = i; //数据
        s->next = L->next;
        L->next = s; //将新结点插入表中,L为头指针
        /* code */
    }
    return L;
}

/**
 * 尾插法
 */
LinkList List_TailInsert(LinkList &L)
{
    LNode *s; //新结点
    LNode *r = L; //尾指针
    for (int i = 1; i <= 10; i++)
    {
        s = (LNode *)malloc(sizeof(LNode));
        s->data = i; //数据
        r->next = s;
        r = s; //r指向新的表位
        /* code */
    }
    r->next = NULL; //尾结点指针置空
    return L;
}


/**
 * 输出链表
 */ 
void printList(LinkList L) {
    LNode *temp = L->next;
    while (temp != NULL)
    {
        /* code */
        cout << temp->data << endl;
        temp = temp->next;
    }
    
}

/**
 * 按位序插入
 */ 
bool LinkedListInsert(LinkList &L, int i, int e) {
    if(i < 1)
        return false;
    LNode *p = L;
    int j = 0;
    while(p != NULL && j < 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->next = s;
    cout << "在第 " << i << " 位" << "插入数值为:" << e << endl;
    cout << "插入成功!" << endl;
    return true;    
}

/**
 * 对某一结点进行后插操作
 */ 
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;
    cout << "插入成功!" << endl;
    return true;    
}

/**
 * 对某一结点进行前插操作
 */
bool InsertPreNode(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->data = p->data;
    p->data = e;
    cout << "插入成功!" << endl;
    return true;
}

/**
 * 按位删除
 */ 
bool ListDelete(LinkList &L, int i) {
    if(i < 1)
        return false;
    LNode *p = L;
    int j = 0;
    while(p != NULL && j < i - 1) {
        p = p->next;
        j++;
    }    
    if(p == NULL || p->next == NULL)
        return false;
    LNode *q = p->next;
    int e = q->data;
    p->next = q->next;
    free(q);  //释放
    cout << "删除第 " << i << " 位" <<"数值为:" << e << endl;
    cout << "删除成功!" << endl;
    return true;
}

/**
 * 删除指定元素
 */
bool DeleteByNode(LNode *p)
{
    if (p == NULL)
        return false;
    LNode *q = p->next;
    p->data = p->next->data;
    p->next = q->next;
    free(q); //释放
    cout << "删除成功!" << endl;
    return true;
}

/**
 * 按位查找
 */ 
int LinkedListSearchBySite(LinkList L, int i) {
    cout << "查找第 " << i << " 位的值为:";
    int j = 1;
    LNode *p = L->next; //获得头结点
    if(i < 0)
        return 0;
    while (p != NULL && j < i)
    {
        /* code */
        p = p->next;
        j++;
    }
    return p->data;    

}

/**
 * 按值查找
 */ 
int LinkedListSearchByValue(LinkList L, int e) {
    cout << "查找值为 " << e << " 的位置为:";
    LNode *p = L->next; //获得头结点
    int i = 1;
    while (p != NULL && p ->data != e)
    {
        /* code */
        p = p->next;
        i++;
    }
    return i;    

}

/**
 * 求表长
 */
void GetLength(LinkList L) {
    LNode *p = L;
    int len = 0;
    while (p->next != NULL)
    {
        /* code */
        p = p->next;
        len++;
    }
    cout << "表长为 " << len << endl;
}


int main()
{
    LinkList L; //声明一个指向单链表的指针
    //初始化一个空表
    InitList(L);
    // L = List_HeadInsert(L);
    L = List_TailInsert(L);
    // L = List_TailInsert2(L);
    printList(L);
    GetLength(L);
    //插入元素
    LinkedListInsert(L, 4, 520);
    LinkedListInsert(L, 6, 1314);
    printList(L);
    //删除元素
    ListDelete(L, 2);
    printList(L);
    //查找
    cout << LinkedListSearchBySite(L, 4) << endl;
    cout << LinkedListSearchByValue(L, 8) << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
表长为 10
在第 4 位插入数值为:520
插入成功!
在第 6 位插入数值为:1314
插入成功!
1
2
3
520
4
1314
5
6
7
8
9
10
删除第 2 位数值为:2
删除成功!
1
3
520
4
1314
5
6
7
8
9
10
查找第 4 位的值为:4
查找值为 8 的位置为:9

加油!加油!加油!💪

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
选题1:(易)实现顺序各种基本运算的算法 参考实验指导书“实验题 1:实现顺序各种基本运算的算法实现”。 选题2:(易)实现链表各种基本运算的算法 参考实验指导书“实验题 2:实现链表各种基本运算的算法实现”。 选题3:(易)编写算法实现二个有序的线性的合并问题(存储结构可选:顺序/链表)。 参考课件“chap002线性.ppt”相关例题。 选题4:(难)运用向循环链表实现约瑟夫环的问题。 参考实验指导书“实验题 4:运用向循环链表实现约瑟夫环的问题”。 选题5:(易)将元素X插入到链表中合适的位置。 完成习题集中的算法并编程实现:2.11。参考课件“chap002线性习题讲解.ppt”2.11。 带头结点的链表L,其中有n 个元素非递减有序排列,将元素X插入到链表中合适的位置。 提示:先创建链表,其中的元素值可由随机函数按阶段生成或键盘输入,先打印初始链表数据,然后插入新结点,再打印结果链表。 插入前: 插入后: 选题6:(易)删除中所有大于mink且小于maxk的元素。 已知线性中的元素(整数)以值递增有序排列,并以链表作存储结构。试写一高效算法,删除中所有大于mink且小于maxk的元素(若中存在这样的元素),分析你的算法的时间复杂度(注意:mink和maxk是给定的两个参变量,它们的值为任意的整数)。 参考实验指导书“实验题 5:删除有序链表中所有大于 mink 且小于 maxk的元素”。 选题7:(中等)删除链表中多余元素。 完成习题集中的算法并编程实现:2.20。参考课件“chap002线性习题讲解.ppt”2.20。 已知链表中的元素有序,写一算法,删除中所有值相同的多余元素(使操作后线性中的元素值均不相同),同时释放被删结点空间。 选题8:(易)实现链表的就地逆置。 完成习题集中的算法并编程实现:2.22。参考课件“chap002线性习题讲解.ppt”2.22。 试分别以不同的存储结构实现线性的就地逆置算法,即在原的存储空间将线性(a1,a2...,an)逆置为(an,an-1,...,a1)。 选题9:(难)链表拆分。 将带头结点的链表LA中分拆成LB和LC两条链表,LA中的data域为奇数的节点依次进入LB,而为偶数的节点进入LC。 参考课件“chap002线性习题讲解.ppt”第35页例题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值