数据结构 单链表基本操作(C++)

本文详细介绍了C++中链表的基本操作,包括动态内存分配、链表的初始化、判断空表、销毁链表、清空链表、获取表长、取值、查找、插入、删除等。这些操作涉及头结点的使用、指针的遍历以及内存的管理,是数据结构学习中的重要内容。
摘要由CSDN通过智能技术生成

复习用笔记,参考资料《数据结构》严蔚敏、青岛大学-王卓

c++语法:

#define OK 1

#define ERROR 0

#define OVERFLOW -2

#include <iostream>

cin >> x;//输入 

cout << "输出内容" << endl; //输出 endl为换行

动态分配内存

int *p = new int; //在堆空间创建int类型变量,new的结果是int*类型,将结果赋值给p。new运算符返回一个指向所分配的存储空间的第一个单元的指针

//C:int *p =(int*)malloc(sizeof(int))

动态释放内存

delete p,delete[] p;//释放数组必须是delete[];delete p仅仅释放了p数组的首元素,内存空间并没有释放完全

//释放p指向的堆空间的int变量.凡是由new运算符分配的内存空间,一定要在使用完后用delete释放。

//C:free(p)


1、头结点

头结点在首元结点前,指针域指向首元结点(存储第一个数据元素的节点)。若有头结点,头指针指向结点为头结点,若无,则指向首元结点。

2、存储结构

typedef struct LNode {
   ElemType data; //结点的数据域
   struct LNode *next; //结点的指针域
} LNode, *LinkList; //LinkList为指向结构体LNode的指针类型

3、单链表的初始化(带头结点)

Status InitList(LinkList &L)   
{
   //构造一个空的单链表L
   L = new LNode; //生成新结点作为头结点,用头指针L指向头结点
   L->next = NULL; //头结点的指针域置空
   return OK;
}

4、判断空表

无头结点时,头指针为空表示空表(L=NULL)

有头结点时,头结点指针域为空表示空表(L->next=NULL)

bool ListEmpty(LinkList L)
{
    if(L->next==NULL)return 1;
    return 0;
}

//下列算法均为带头结点版本

5、单链表销毁(销毁后链表不存在)

从头指针开始,依次释放所有结点

Status DestroyList(LinkList &L)
{
   /* 初始条件:线性表L已存在。操作结果:销毁线性表L */
   LinkList p;//LNode *p;
   while(L) {
      p = L;
      L = L->next;
      delete p;
   }
   return OK;
}

6、清空链表(链表存在但无元素,头指针头结点存在)

依次释放所有结点,并将头结点指针域设置为空【两指针同时运行】

Status ClearList(LinkList &L)
{
    LNode *p, *q;
    p = L->next;
    while (p)
    {
      q = p->next;
      delete p;
      p=q;
    }
    L->next = NULL;//头结点指针域为空
    return OK;
}

7、表长

遍历链表,统计结点数

int ListLength(LinkList L)
{
    int count=0;
    LNode *p;
    p=L->next;
    while(p){
        count++;
        p=p->next;
    }
    return count;
}

8、取值

首元结点出发,遍历链表。用e返回第i个元素值。

Status GetElem(LinkList L, int i, ElemType &e)   
{
    LNode *p;
    p=L->next;
    int count=1;
    while(count<i&&p){
        p=p->next;
        ++count;//遍历至p为空或p指向第i个元素
    }
    if(!p||count>i)return ERROR;//第i个元素不存在
    e=p->data;
    return OK;
} //GetElem

当1<=i<=n,频度为i-1;每个元素取值概率为1/n;

ASL=1/n\sum_{i=1}^{n} i-1=n-1/2。时间复杂度为O(n)

9、查找

按值查找,返回地址

LNode *LocateElem(LinkList L, int e)   //按值查找地址
{
    LNode *p;
    p=L->next;
    while(p&&p->data!=e)
    {
        p=p->next;
    }
    return p;//找到返回地址p,查找失败返回NULL
} //LocateElem

按值查找,返回位置序号

int LocateElem(LinkList L, int e)   //略有改动 算法2.8 按值查找位置序号
{
    LNode *p;
    int count=1;
    p=L->next;
    while(p&&p->data!=e)
    {
        count++;
        p=p->next;
    }
    if(p)return count;
    else return 0;
} //LocateElem

时间复杂度类似取值,为O(n)

10、插入

在第i个结点前插入值为e的新结点

①找到第i-1个元素的存储位置p

②生成一个数据域为e的新结点s

③插入新结点:

1)新结点指针域指向第i个元素结点 s->next=p->next

2)第i-1个元素结点指向新结点 p->next=s

*插入步骤不可互换,否则丢失Ai位置

Status ListInsert(LinkList &L, int i, ElemType e)   
{
    LNode *p,*s;
    int j=0;
    p=L;
    while(p&&j<i-1){
        ++j;
        p=p->next;
    }//让p指向Ai-1位置
    if(!p||j>i-1) return ERROR;//非法
    s=new LNode;//生成新结点
    s->data=e;
    s->next=p->next;
    p->next=s;//插入
    return OK;
} //ListInsert

必须先找到第i-1个结点,时间复杂度与取值类似,仍为O(n)

11、删除

①首先找到Ai-1的存储位置p,保存要删除Ai值

②令p->next指向Ai+1

③释放Ai空间

Status ListDelete(LinkList &L, int i)  
{
   LNode *p, *q;
   int j = 0;
   p = L;
   while (p->next && j < i - 1)
   {
      p = p->next;
      ++j;
    } // 让p指向Ai-1位置
    if (!(p->next)|| j > i - 1)
      return ERROR; // 非法
    q=p->next;
    p->next=q->next;
    delete q;
    return OK;
} //ListDelete

时间复杂度与插入算法类似,仍为O(n)

*删除算法中循环条件(p->next&&j<i-1)与插入算法中循环条件(p&&j<i-1)有所区别,插入算法有n+1个位置,删除只有n个,如一样会造成引用空指针使删除失败。

12、输出链表

void ListPrint(LinkList L)
{
   LNode *p;
   for(p = L->next; p; p = p->next)
      cout << p->data << (p->next ? ' ' : '\n');
}

13、单链表创建

1)尾插法(元素生成在链表尾部)-适用于顺序存入

1.从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。
2.初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。

void CreateList_R(LinkList &L, int n) //后插法创建单链表
{
    LNode *tail=NULL,*p;
    L=new LNode;
    L->next=NULL;
    tail=L;//尾指针指向头结点
    for(int i=0;i<n;++i)
    {
        p=new LNode;//形成新结点
        cin >> p->data;
        p->next=NULL;
        tail->next=p;//新结点插入尾结点后
        tail=p;//tail指向新的尾结点p
    }
}

2)头插法(元素插入在链表头部)-适合逆序存入

1、从一个空表开始,重复读入数据;

2、生成新结点,将读入数据存放到新结点的数据域中

3.从最后一个结点开始,依次将各结点插入到链表的前端

 

void CreateList_H(LinkList &L, int n) // 头插法创建单链表
{
    LNode  *p;
    L = new LNode;
    L->next = NULL;//建立链表
    for (int i = 0; i < n; ++i)
    {
        p = new LNode; // 形成新结点
        cin >> p->data;
        p->next=L->next;//插入表头
        L->next=p;
    }
}

时间复杂度O(n)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值