单链表笔记

单链表

线性表的链式存储又称为链表,它是指的是通过任意一组的存储单元来存储线性表中的数据元素。

单链表的存储

typedef int ElemType;
typedef struct LinkedNode {
    ElemType data;//存放数据元素
    struct LinkedNode* next;//指向下一个链表结点
}*LinkedList,LinkNode;

Notes:

  1. 利用链表可以接解决顺序表需要大量连续存储单元的缺点。
  2. 由于单链表元素离散地分布在存储空间中,所以单链表是非随机存取地存储结构,查找某个特定结点时,需要从表头开始遍历,依次查找。
  3. 引入头节点后可以带来两个优点
    1. 由于第一个数据结点的位置存放在头节点的指针域内,无需进行特殊处理。
    2. 无论链表是否为空,其头节点都指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也得到了处理

单链表基本操作实现

建立单链表

  • 采用头插法建立单链表

带头结点的实现

LinkedList List_HeadInsert(LinkedList& L)
{
    LinkNode* s;
    int x;
    InitList(L);//先构造一个空表

    scanf_s("%d", &x);//先读入一个x值
    while (x != -1)// -1作为读取结束的标志,当然也可以用其他标志
    {
        s = (LinkNode*)malloc(sizeof(LinkNode));
        s->data = x;
        s->next = L->next;
        L->next = s;//这两行代码是插入的关键代码,
        //先将L->next赋值给s->next,然后s赋值给L->next;
        scanf_s("%d", &x);
    }
    return L;
}
  • 采用尾插法建立单链表
LinkedList List_TailInsert(LinkedList& L)
{
    LinkNode* s;
    int x;
     InitList(L);

    scanf_s("%d", &x);
    LinkNode* pt = L;
    while (pt->next != NULL)
    {
        pt = pt->next;//首先找到尾结点,利用边界条件。
    }
    while (x != -1)
    {
        s = (LinkNode*)malloc(sizeof(LinkNode));
        s->data = x;
        pt->next = s;
        pt = s;
        scanf_s("%d", &x);
    }
    pt->next = NULL;//这句代码用来指令尾部结点的next是NULL,要不然容易指向野指针!

    return L;
}

查找值

  • 按序号查找结点值
    在单链表从第一个出发,顺着指针next域逐个往下搜索,直到找个第i个结点为止,否则返回最后一个结点指针域NULL
    算法如下
  LinkNode* GetElem(LinkedList L, int index)
{
    if (index < 0 || index > Length(L))
    {
        std::cout << "index的值不合法!" << std::endl;
        return NULL;
    }//若index无效,则返回NULL;

    if (index == 0)
    {
        return L;
    }
    if (L == NULL)
    {
        return NULL;
    }

    int j = 1;//计数。初始值为1

    LinkNode* pt = L->next;//头结点指针赋值给p

    while (pt != NULL && j < index)
       // while (pt && j < index)
    {
        pt = pt->next;
        j++;
    }
    return pt;//返回第i个结点的指针,若i大于表长,则返回NULL
}

按序查找操作的时间复杂度为O(n)

  • 按照值查找表结点

从单链表第一个结点开始,从前往后依次比较表中各节点指针域的值,若某结点数据域的值等于给定值e,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL;

LinkNode* LocateElem(LinkedList L, ElemType e) {
    LinkNode* p = L->next;
    while (p != NULL  && p->data != e)
    {
        p = p->next;

    }
    return p;
}

时间复杂度为O(n),n为链表的长度

插入结点操作

插入结点操作将值为x的新节点插入到单链表的第i个位置上。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后插入新节点

bool InsertLinkNode(LinkedList L, int index, ElemType e) {
    if (index <= 0 || index > Length(L))
    {
        cout << "index值不合法" << endl;
        return false;
    }
    LinkNode* s;
    if (index == 1)//若为第一个位置,直接在表头后面插入即可
    {
        s = (LinkNode*)malloc(sizeof(LinkNode));
        s->data = e;
        s->next = L->next;
        L->next = s;
        return true;
    }
    s = (LinkNode*)malloc(sizeof(LinkNode));
    LinkNode* p = GetElem(L, index - 1);//首先找到第index-1个位置的结点

    //执行插入操作

    s->next = p->next;
    p->next = s;
    s->data = e;

    return true;
}

本算法主要的时间开销在于查找i-1个元素,时间复杂度为O(N).若在给定的结点后面插入新节点,则时间复杂度为O(1)

删除结点操作

bool ListNodeDelete(LinkedList L, int index) {
    if (index <= 0 || index > Length(L))
    {
        cout << "index值不合法" << endl;
        return false;
    }

    LinkNode* p = GetElem(L, index - 1);

    LinkNode* pnext = p->next;
    p->next = pnext->next;
    free(pnext);
}

时间复杂度为O(n),主要时间开销主要在查找操作上

求表长操作

带头结点求表长

int Length(LinkedList L) {
    LinkNode *pt = L->next;
    int length = 0;
    while (pt != NULL)
    {
        length++;
        pt = pt->next;
    }
    return length;
}

单链表的原地逆置

LinkedList Reverse_1(LinkedList &L)
{
    LinkNode* p = L->next;
    LinkNode* q;
    L->next = NULL;//首先将原有链表置为空!

    while (p != NULL)
    {
        q = p->next;//q首先指向下一个
        p->next = L->next;
        L->next = p;//采用头插法插在L后面
        p = q;//p再取下一个
    }
    return L;
}

方法二

在这里插入图片描述

LinkedList Reverse_2(LinkedList &L) {
    LinkNode* pre, *p = L->next, *r = p->next;
    p->next = NULL;
    while (r != NULL)
    {
        pre = p;
        p = r;
        r = r->next;
        p->next = pre;
    }
    L->next = p;
    return L;
}

上述两个算法的时间复杂度为O(N),空间复杂度为O(1)

最后贴出完整代码

#include <iostream>
#include <stdio.h>

using namespace std;

typedef int ElemType;
typedef struct LinkedNode {
    ElemType data;
    struct LinkedNode* next;

}*LinkedList,LinkNode;

LinkNode* getHead(LinkedList L);
LinkNode* getTail(LinkedList L);

void  InitList(LinkedList &L);
int Length(LinkedList L);
LinkedList List_HeadInsert(LinkedList& L);
LinkedList List_TailInsert(LinkedList& L);
void Print(LinkedList L);
LinkNode * GetElem(LinkedList L,int index);
LinkNode* LocateElem(LinkedList L, ElemType e);
bool InsertLinkNode(LinkedList L, int index, ElemType e);
bool ListNodeDelete(LinkedList L, int index);
LinkedList Reverse_1(LinkedList &L);//单链表的原地逆置
LinkedList Reverse_2(LinkedList& L);
void reverseTravese(LinkedList head);
int main()
{
    LinkedList  L;
    List_HeadInsert(L);
    Print(L);
   /* std::cout << Length(L) << std::endl;
    LinkedList LL;
    List_TailInsert(LL);
    Print(LL);

    LinkNode* p = GetElem(L, 5);
    cout << p->data << endl;

    InsertLinkNode(L, 3, 22);
    Print(L);

    ListNodeDelete(L, 2);
    Print(L);*/

    reverseTravese(L);
    return 0;
}
void  InitList(LinkedList & L) {
    L = (LinkNode*)malloc(sizeof(LinkNode));
    L->data = 0;
    L->next = NULL;
}

int Length(LinkedList L) {
    LinkNode *pt = L->next;
    int length = 0;
    while (pt != NULL)
    {
        length++;
        pt = pt->next;
    }
    return length;
}
LinkedList List_TailInsert(LinkedList& L)
{
    LinkNode* s;
    int x;
     InitList(L);

    scanf_s("%d", &x);
    LinkNode* pt = L;
    while (pt->next != NULL)
    {
        pt = pt->next;//首先找到尾结点,利用边界条件。
    }
    while (x != -1)
    {
        s = (LinkNode*)malloc(sizeof(LinkNode));
        s->data = x;
        pt->next = s;
        pt = s;
        scanf_s("%d", &x);
    }
    pt->next = NULL;//这句代码用来指令尾部结点的next是NULL,要不然容易指向野指针!

    return L;
}
LinkedList List_HeadInsert(LinkedList& L)
{
    LinkNode* s;
    int x;
    InitList(L);//先构造一个空表

    scanf_s("%d", &x);//先读入一个x值
    while (x != -1)// -1作为读取结束的标志,当然也可以用其他标志
    {
        s = (LinkNode*)malloc(sizeof(LinkNode));
        s->data = x;
        s->next = L->next;
        L->next = s;//这两行代码是插入的关键代码,
        //先将L->next赋值给s->next,然后s赋值给L->next;
        scanf_s("%d", &x);
    }
    return L;
}

void Print(LinkedList L)
{
    LinkNode* pt = L->next;
    while (pt->next != NULL)
    {
        std::cout << pt->data << " ";
        pt = pt->next;
    }
    std::cout << pt->data << std::endl;
}
LinkNode* GetElem(LinkedList L, int index)
{
    if (index < 0 || index > Length(L))
    {
        std::cout << "index的值不合法!" << std::endl;
        return NULL;
    }
    if (index == 0)
    {
        return L;
    }
    if (L == NULL)
    {
        return NULL;
    }

    int j = 1;
    LinkNode* pt = L->next;
    while (pt != NULL && j < index)
       // while (pt && j < index)
    {
        pt = pt->next;
        j++;
    }
    return pt;
}
LinkNode* LocateElem(LinkedList L, ElemType e) {
    LinkNode* p = L->next;
    while (p != NULL  && p->data != e)
    {
        p = p->next;

    }
    return p;
}
bool InsertLinkNode(LinkedList L, int index, ElemType e) {
    if (index <= 0 || index > Length(L))
    {
        cout << "index值不合法" << endl;
        return false;
    }
    LinkNode* s;
    if (index == 1)
    {
        s = (LinkNode*)malloc(sizeof(LinkNode));
        s->data = e;
        s->next = L->next;
        L->next = s;
        return true;
    }
    s = (LinkNode*)malloc(sizeof(LinkNode));
    LinkNode* p = GetElem(L, index - 1);
    s->next = p->next;
    p->next = s;
    s->data = e;
    return true;
}
bool ListNodeDelete(LinkedList L, int index) {
    if (index <= 0 || index > Length(L))
    {
        cout << "index值不合法" << endl;
        return false;
    }

    LinkNode* p = GetElem(L, index - 1);

    LinkNode* pnext = p->next;
    p->next = pnext->next;
    free(pnext);
    return true;
}

LinkedList Reverse_1(LinkedList &L)
{
    LinkNode* p = L->next;
    LinkNode* q;
    L->next = NULL;//首先将原有链表置为空!

    while (p != NULL)
    {
        q = p->next;//q首先指向下一个
        p->next = L->next;
        L->next = p;//采用头插法插在L后面
        
        p = q;//p再取下一个
    }
    return L;
}
LinkNode* getHead(LinkedList L) {
    return L;
}
LinkNode * getTail(LinkedList L)
{
    LinkNode* p = L->next;
    while (p->next != NULL)
    {
        p = p->next;
    }
    return p;
}
LinkedList Reverse_2(LinkedList &L) {
    LinkNode* pre, *p = L->next, *r = p->next;
    p->next = NULL;
    while (r != NULL)
    {
        pre = p;
        p = r;
        r = r->next;
        p->next = pre;
    }
    L->next = p;
    return L;
}
void reverseTravese(LinkedList head) {
    LinkedList p = head->next;
    ElemType stack[20];
    int top = -1;
    while (p != NULL)
    {
        stack[++top]= p->data;
        p = p->next;
    }
    while (top != 0)
    {
        cout << stack[top--] << " ";
    }
    cout << stack[0] << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的算算

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值