数据结构与算法基础(王卓)(5):关于(单)链表较复杂的操作

目录

取第i个元素

按值查找(查找其地址,以及是表中第几个元素(位置序号))

插入(把元素e插到第i个位置结点上)

删除(链表中第i个元素结点)

单链表的建立(头插法)<倒序插入>

单链表的建立(尾插法)<顺序插入>


位置:PPT第二章:127


默认前置:

#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit  
#include<math.h>//OVERFLOW,exit

#define MAXlength 100  //初始大小为100,可按需修改

typedef int Status;         //函数调用状态

struct K
{
    float a;
    int b;
    string c;
};
typedef K Elemtype;         //函数调用状态

struct Lnode
    //node:结; 结点;
{
    Elemtype data;
    Lnode* next;
};
typedef Lnode* LinkList;

取第i个元素

project 1:(自己写的,凑合着也能用)

Status 取第i个元素(LinkList L, int i,Elemtype e)
{
if (链表是否为空(L))
        cerr << "链表为空" << endl;
if (i < 1)
    cerr << "i不对" << endl;
    LinkList p = L->next;
    int j = 0;
    while (p)
    {
        if (i == j)
        {
            e = p->data;
            break;
        }
        p = p->next;
        j++;
    }
    return true;
}

project 2:根据课程上写的(算法复杂度低一些,比较好用)

Status GetElem“i”(LinkList L, int i, Elemtype e)
{
    LinkList p;
    p = L->next;
    int j = 1;
    while (p && i > j)
    {
        p = p->next;
        j++;
    }
    if (i<0 || i<j || !p)
        return false;
    e=p->data;
    return true;
}

按值查找(查找其地址,以及是表中第几个元素(位置序号))

project 1:

Status 按值查找地址和位置序号(LinkList L,Elemtype e)
{
    LinkList p; int i=1;
    p = L->next;
    while (p)
    {
        i++;
        if (e == p->data)
        {
            cout << "地址为:  " << p << ";" << endl;
            cout << "位置序号为:  " << i <<";" << endl;
        }
        p = p->next;
    }
    //如果最后运行失败查找不到最后怎么返回ERROR?
    if (p = NULL)
        return NULL;
    return true; 
}

 但是要注意,如果要这么写的话,在类型K的声明里,还需要加上手撸判断表达式的定义:

struct K
{
    float a;
    int b;
    string c;
    bool operator==(K& t)
    {
        return t.a == a && t.b == b;
        //&& t.c = c;
    }
};
typedef K Elemtype;         //函数调用状态

 project 2:根据课程上写的(算法复杂度低一些,比较好用)

Status LocateELem(LinkList L, Elemtype e)
{
    //在线性表L中查找值为e的数据元素
    //找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
    auto p = L->next; int i=1;
    while (p && p->data != e)
    {
        i++;
        if (e == p->data)
        {
            cout << "地址为:  " << p << ";" << endl;
            cout << "位置序号为:  " << i << ";" << endl;
        }
        p = p->next;
    }
    if (p == NULL)
        return NULL;
    return true;
}

同理:

struct K
{
    float a;
    int b;
    string c;
    bool operator==(K& t)
    {
        return t.a == a && t.b == b;
        //&& t.c = c;
    }
    bool operator!=(K& t)
    {
        return t.a != a || t.b != b;
        //|| t.c = c;
    }
};
typedef K Elemtype;         //函数调用状态

插入(把元素e插到第i个位置结点上)

 project 1:

Status 插入(LinkList L,int i,Elemtype e)
{
    LinkList p,s; int j = 1;
    p = L->next;
    s->data = e;
    while (p)//&& j < i)
    { 
        if (j == i-1)
        {
            s->next = p->next;
            p->next = s;
            return true;
        }
        p = p->next;
        j++;
    }
    if (!p || j > i - 1)
        return false;//别忘了写插入非法时的情况
}

 project 2:根据课程上写的

Status Listlnsert(LinkList& L, int i, Elemtype e) 
{
    auto p = L; int j = 0;
    while (p && j < i - 1)
    { p = p->next; ++j; } 
    if (!p ||j > i - 1)
        return false;
    auto s = new Lnode; 
    s->data = e; 
    s->next=p->next;
    p->next = s; 
    return true;
}//Listlnsert_L

问题:(其实我觉得应该是一样的,只是把这个还没有确定的问题暂时先放在这里mark一下希望来日可以顺利解答)在这个算法里:

int j = 1;p = L->next;

auto p = L; int j = 0;

等价吗

删除(链表中第i个元素结点)

project 1:

Status 删除(LinkList& L, int i)
{
    LinkList p,s,q; int j=1;
    while (p&&j<i-1)
    {
        p->next;
        j++;
    }
    s = p->next;
    q = s->next;
    delete s;
    p = q;
    return true;
}

project 2:

Status 删除(LinkList& L, int i)
{
    LinkList p, s; int j = 1;
    while (p && j < i - 1)
    {
        p->next;
        j++;
    }
    s = (p->next)->next;
    delete p->next;
    p->next = s;
    return true;
}

project 3:根据视频(逻辑更严谨)

Status 删除(LinkList& L, int i)
{
    LinkList p = L, s; int j = 1;
    while (p && j < i - 1)
    {
        p = p->next;
        j++;
    }
    if (!p || j > i - 1)
        return false;
    s = p->next;
    p->next = s->next;
    //auto e = s->data;
    delete s;
    return true;
}

各操作的时间复杂度:

单链表的查找、插入和删除:O(n)

线性表的插入和删除:O(1)



单链表的建立(头插法)<倒序插入>

project 1:

用头插法建立链表,并插入(输入)数据结点(这里简化我们只输入3个)a,b,c

Status 头插法(LinkList &L)
{
    //创建空链表
    K a, b, c;
    L= new Lnode;//就一个首元结点
   L->next = NULL;
   //L->data里面没什么东西
   //(我们也不知道里面有啥)
  //但可以确定,里面不为空(NULL)

   auto p = new Lnode;//(头)插入
   L->data = c;
   p->next = L->next;
   L->next = p;
   //每次循环的操作,唯一的区别只有:
   //data域输入的数值不同
   auto p = new Lnode;
   L->data = b;
   p->next = L->next;
   L->next = p;
   //
   auto p = new Lnode;
   L->data = a;
   p->next = L->next;
 
}

实际上,这个程序是错的,程序设计逻辑:

Points:

关于

L= new Lnode;

实际上我觉得真的是多此一举:

系统开始带有参数LinkList &L的时候不是实际上已经提前给你默认提前分配好了L的内存空间吗,你他*还在这边费劲干啥呢

另外,   L= new Lnode;这种开辟空间的格式是哪弄出来的???

原来学过关于new的用法,只有通过定义指针分配新空间,like:

   Lnode* p = new Lnode;

但是既然这里可行可用,那我们也需要记住:

L= new Lnode;

即:

<开辟空间首地址> = new  <开辟空间存放数据类型>

格式(前面没有定义过记得加auto)

最后经过我们后面深入专门去学习了关于new的使用方法和格式,发现并不是这么一回事:

并不是因为代表L是表,而像我们猜测的那样有了什么新的格式

而是由于表头前面的Linklist &L,表明了他本来就是一个指针,依然属于符合原来的使用规范

也就是说,如果前面(形参传递时)我们已经定义过了该变量,那么在后面开辟空间时

我们就不用(也不能)再(重新)申明变量的类型

详见:

数据结构与算法基础(王卓)(8)附:关于new的使用方法详解_宇 -Yu的博客-CSDN博客_数据结构new

C语言日记 26 指针与函数,动态存储分配_宇 -Yu的博客-CSDN博客

末尾


OK,现在,我们来修正project 1:

注意如果我们想要实现像前面画的图中的“我们想要达到的效果”,那是不可能的了

我们最多只能退而求其次:

让头指针指向头结点,搞一个新的首元结点插进去,而不是搞个新的首元结点(办不到)

project 2:

其实要实现对前面的修正和更改也很简单,只需:

把语句 L->data = (结点信息);修改为:p->data = (结点信息),即可;

Status 头插法(LinkList& L)
{
    //创建空链表
    K a, b, c;
    L = new Lnode;//就一个首元结点
    L->next = NULL;//别忘了这句
    //L->data里面没什么东西
    //(我们也不知道里面有啥)
   //但可以确定,里面不为空(NULL)

    auto p = new Lnode;//(头)插入
    p->data = c;
    p->next = L->next;
    L->next = p;
    //
    auto p = new Lnode;
    p->data = b;
    p->next = L->next;
    L->next = p;
    //
    auto p = new Lnode;
    p->data = a;
    p->next = L->next;
}

正式根据PPT(148)设计的程序流程来设计实现程序:

Project 3:

Status 头插法()//LinkList &L)
{
    //创建空链表
    auto L = new Lnode;
    //创建新节点
    auto p = new Lnode;
    K an,an_1;
    p->data = an;
    //把新节点和链表串起来
    p->next = L->next;
    L->next = p;
    //重复下一轮循环
    auto p = new Lnode;
    p->next = L->next;
    L->next = p;
}

将其实现为我们具体能够落地实现使用的函数:(假设我们输入n个节点的数据)

Status 头插法(LinkList& L, int n)
{
    //创建空链表
    auto L = new Lnode;
    L->next = NULL;//别忘了这句
    int i = 1;
    while (i <= n)
    {
        auto p = new Lnode;
        cin >> p->data.a;
        cin >> p->data.b;
        cin >> p->data.c;
        p->next = L->next;
        L->next = p;
        i++;
    }
    return true;
}

这个结果基本和标准答案差不多,只不过标准答案用的是for语句:

void CreatListHead(LinkList& L, int n)
{
    auto L = new Lnode;
    L->next = NULL;//别忘了这句
    for (int i = n; i > 0; --i)
        //for (int i = 0; i < n; ++i)
    {
        auto p = new Lnode;
        cin >> p->data.a;
        cin >> p->data.b;
        cin >> p->data.c;
        p->next = L->next;
        L->next = p;
        i++;
    }
}

最终使用时,不能使返回表和构造表重定义:

Status 头插法(LinkList& A, int n)
{
    //创建空链表
    auto L = new Lnode;
    L->next = NULL;//别忘了这句
    int i = 1;
    while (i <= n)
    {
        auto p = new Lnode;
        cin >> p->data.a;
        cin >> p->data.b;
        cin >> p->data.c;
        p->next = L->next;
        L->next = p;
        i++;
    }
    A = L;
    return true;
}

时间复杂度:O(n) 

单链表的建立(尾插法)<顺序插入>

project 1:

Status 尾插法(int n)
{
    auto L = new Lnode;
    L->next = NULL;
    LinkList r = L;
    for (auto i = 1; i <= n; i++)
    {
        auto p = new Lnode;
        cin >> p->data.a;
        cin >> p->data.b;
        cin >> p->data.c;
        p->next = NULL;
        r->next = p;//尾插
        r = p;
    }
    return true;
}

project 2:(同理,不再赘述)

Status 尾插法(LinkList& A, int n)
{
    auto L = new Lnode;
    L->next = NULL;
    LinkList r = L;
    for (auto i = 1; i <= n; i++)
    {
        auto p = new Lnode;
        cin >> p->data.a;
        cin >> p->data.b;
        cin >> p->data.c;
        p->next = NULL;
        r->next = p;//尾插
        r = p;
    }
    A = L;
    return true;
}

猜你喜欢

转载自blog.csdn.net/Zz_zzzzzzz__/article/details/128166500