数据结构单链表的基础操作

学习23王道数据结构笔记(整体代码在最后)

  一、分析

        数据结构的三要素:数据的逻辑结构、存储结构、运算。

      ① 在逻辑结构中,分为线性结构和非线性结构。线性结构中有:一般线性表、栈、队列、串、数组;非线性结构有:集合、一般树、二叉树、有向图、无向图。

       ② 在存储结构中,主要分为四大存储:顺序存储、链式存储、索引存储、散列存储

       ③ 运算包括:定义和实现。       

                定义:针对逻辑结构,指出运算的功能;

                实现:针对存储结构,指出运算的具体操作步骤。

二、特点

        我对线性表的顺序存储以及链式存储记一下它们的不同点。这里的单链表是就典型的线性表通过链式存储实现的线性表。

顺序表链表
弹性(可扩容)不太行可行
增删不太行可行
可行不太行

1.对扩容分析:

                                顺序表                                                        链表

               ① 逻辑上相邻的元素,对应的物理存             ①逻辑上相邻的元素,物理存

                储位置也相邻。费空间                                  储位置不一定相邻。

                ②需要一开始给足空间,空间过大闲            ②可扩充

                置,过小易溢出。不可扩充

2.对增删改查操作分析:

                                      顺序表                                                       链表

                                有序        无序                                         有序        无序

按值查找              O(log2n)     O(n)                                        O(n)        O(n)

按序号查找            O(1)          O(1)                                        O(n)         O(n)

插入删除      需要移动插入删除后的后面所有元素          只需要修改相关结点的指针域即可

三、代码操作

下面是对单链表的基本操作:(均为带头结点操作)

①定义单链表

//定义单链表
typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode, *LinkList;
 

②初始化结点

        只有当这个结点需要开辟空间存储数据时才会用到,这里用LNode *开辟而不用LinkList就是为了强调这个是一个单纯的结点,后面没跟链表。

//初始化结点
void InitLNode(LNode *&s,ElemType e){
    s = (LNode *)malloc(sizeof(LNode));     //强调结点
    s->next = NULL;
    s->data = e;
}

③头插法建立单链表

          一直在头结点L的后面插入结点

转变过程

 

 输入后的链表

 

 

//头插法建立单链表(带头结点)
LinkList List_HeadInsert(LinkList &L){
    LNode *s;
    int x;
    L = (LinkList)malloc(sizeof(LNode));    //创建头结点,强调链表
    L->next = NULL;
    cin >> x;
    
    while(x != -1){                         //循环输入这里是关键,先给s开空间,再给指,但后通过L,s的赋值定义为头插还是尾插,一定要熟练
        InitLNode(s, x);
        s->next = L->next;
        L->next = s;
        cin >> x;
    }
    
    return L;                               //L指向的是表头
}

④输出链表

        顺便加了一个gross记录表长哈,这个可以在定义链表在中加个length赋值给它哈。

//单链表的遍历
void PrintList(LinkList head){
    LNode* current = head;
    int gross = 0;
    cout << "遍历单链表的值:" << endl;
    
    /*while(current != NULL){     //不带头结点时,带头结点用这个输出会将头结点也一并输出
        gross ++;
        cout << current->data << "->";
        current = current->next;
    }*/
    
    while(current->next != NULL){
        gross++;
        current = current->next;
        cout << current->data << "->";
    }
    
    cout << "\n链表的数量为:\n" << gross << endl << endl;
}

④测试

        到这就可以测试了,输出一下代码看看:

int main(){             //编号从1开始
    
    //头插法建立单链表
    LinkList L1;
    cout << "头插法建立单链表,输入-1结束:" << endl;
    List_HeadInsert(L1);        //头插法建立单链表
    PrintList(L1);

    return 0;
}

运行截图

                                 

⑤尾插法建立单链表

         头插法会一直往插入的结点后插入。来张图要好理解的多

        

//尾插法建立单链表(带头结点)
LinkList List_TailInsert(LinkList &L){
    int x;
    L = (LinkList)malloc(sizeof(LNode));    //创建头结点,强调链表
    LNode *s, *r=L;
    cin >> x;
    
    while(x != -1){
        InitLNode(s, x);
        r->next = s;
        r = s;              //r指向新的表尾指针
        cin >> x;
    }
    r->next = NULL;         //尾结点指针置空
    return L;
}

⑥按序号查找结点

//按序号查找结点
LNode *GetElem(LinkList L, int i){          //强调结点
    int j = 1;    //计数
    LNode *p = L->next;
    //对i进行逻辑判断,加强代码的健壮性
    if(i == 0)
        return L;
    if(i < 0)
        return NULL;
        
    while(p && j<i){            //判断了p就不用再去通过头结点判断链表是否为空了
        p = p->next;
        j++;
    }
    return p;
}

⑦按值查找表结点

//按值查找表结点
LNode *LocateElem(LinkList L, ElemType e){
    LNode *p = L->next;
    int gross = 1;              //记录第几个数
    while(p && p->data!=e){
        p = p->next;
        gross++;
    }
    return p;
}

⑧尾插法插入结点 (在给定结点后插入一个结点)

//尾插法插入结点 (在给定结点后插入一个结点)
bool InsertLNode(LinkList &L, ElemType e, int i){        //(需要插入的链表,插入值,插入的位置)
    if(L == NULL)
        return false;
    else{
        LNode *p = GetElem(L , i), *s;
        if(p == NULL)
            return false;
        else{
            InitLNode(s , e);
            s->next = p->next;
            p->next = s;
        }
    }
}

⑨前插法插入结点(在给定结点前插入一个结点)

//前插法插入结点(在给定结点前插入一个结点)
bool FrontInsertNode(LinkList &L, ElemType e, int i){
    if(L == NULL)
        return false;
    else{
        LNode *p = GetElem(L, i), *s;
        if(p == NULL)
            return false;
        else{
            InitLNode(s, e);
            s->next = p->next;
            p->next = s;
            
            temp = p->data;             //这里区别与后插
            p->data = s->data;
            s->data = temp;
        }
    }
}

⑩删除结点操作


//删除结点操作
bool DelectLNode(LinkList L, int i){
    if(L == NULL)
        return false;
    else{
        LNode *p = GetElem(L, i-1),*s;     //找该结点的前驱结点
        if(p == NULL)
            return false;
        else{
            InitLNode(s, 0);
            s = p->next;
            p->next = s->next;
            free(s);
        }
    }
}

 

五、整体代码

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

#define MaxSize 50
typedef int ElemType;

int n,m;
int arr[MaxSize],brr[MaxSize];
bool flag,judge;
ElemType temp,over;     //用于过度

//单链表的应用

//定义单链表
typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode, *LinkList;

//初始化结点
void InitLNode(LNode *&s,ElemType e){
    s = (LNode *)malloc(sizeof(LNode));     //强调结点
    s->next = NULL;
    s->data = e;
}

//头插法建立单链表(带头结点)
LinkList List_HeadInsert(LinkList &L){
    LNode *s;
    int x;
    L = (LinkList)malloc(sizeof(LNode));    //创建头结点,强调链表
    L->next = NULL;
    cin >> x;
    
    while(x != -1){                         //循环输入这里是关键,先给s开空间,再给指,但后通过L,s的赋值定义为头插还是尾插,一定要熟练
        InitLNode(s, x);
        s->next = L->next;
        L->next = s;
        cin >> x;
    }
    
    return L;                               //L指向的是表头
}

//尾插法建立单链表(带头结点)
LinkList List_TailInsert(LinkList &L){
    int x;
    L = (LinkList)malloc(sizeof(LNode));    //创建头结点,强调链表
    LNode *s, *r=L;
    cin >> x;
    
    while(x != -1){
        InitLNode(s, x);
        r->next = s;
        r = s;              //r指向新的表尾指针
        cin >> x;
    }
    r->next = NULL;         //尾结点指针置空
    return L;
}

//按序号查找结点
LNode *GetElem(LinkList L, int i){          //强调结点
    int j = 1;    //计数
    LNode *p = L->next;
    //对i进行逻辑判断,加强代码的健壮性
    if(i == 0)
        return L;
    if(i < 0)
        return NULL;
        
    while(p && j<i){            //判断了p就不用再去通过头结点判断链表是否为空了
        p = p->next;
        j++;
    }
    return p;
}

//按值查找表结点
LNode *LocateElem(LinkList L, ElemType e){
    LNode *p = L->next;
    int gross = 1;              //记录第几个数
    while(p && p->data!=e){
        p = p->next;
        gross++;
    }
    return p;
}

//单链表的遍历
void PrintList(LinkList head){
    LNode* current = head;
    int gross = 0;
    cout << "遍历单链表的值:" << endl;
    
    /*while(current != NULL){     //不带头结点时,带头结点用这个输出会将头结点也一并输出
        gross ++;
        cout << current->data << "->";
        current = current->next;
    }*/
    
    while(current->next != NULL){
        gross++;
        current = current->next;
        cout << current->data << "->";
    }
    
    cout << "\n链表的数量为:\n" << gross << endl << endl;
}

//尾插法插入结点 (在给定结点后插入一个结点)
bool InsertLNode(LinkList &L, ElemType e, int i){        //(需要插入的链表,插入值,插入的位置)
    if(L == NULL)
        return false;
    else{
        LNode *p = GetElem(L , i), *s;
        if(p == NULL)
            return false;
        else{
            InitLNode(s , e);
            s->next = p->next;
            p->next = s;
        }
    }
}

//前插法插入结点(在给定结点前插入一个结点)
bool FrontInsertNode(LinkList &L, ElemType e, int i){
    if(L == NULL)
        return false;
    else{
        LNode *p = GetElem(L, i), *s;
        if(p == NULL)
            return false;
        else{
            InitLNode(s, e);
            s->next = p->next;
            p->next = s;
            
            temp = p->data;             //这里区别与后插
            p->data = s->data;
            s->data = temp;
        }
    }
}

//删除结点操作
bool DelectLNode(LinkList L, int i){
    if(L == NULL)
        return false;
    else{
        LNode *p = GetElem(L, i-1),*s;     //找该结点的前驱结点
        if(p == NULL)
            return false;
        else{
            InitLNode(s, 0);
            s = p->next;
            p->next = s->next;
            free(s);
        }
    }
}


int main(){             //编号从1开始
    
    //头插法建立单链表
    LinkList L1;
    cout << "头插法建立单链表,输入-1结束:" << endl;
    List_HeadInsert(L1);        //头插法建立单链表
    PrintList(L1);
    
    //尾插法建立单链表
    LinkList L2;
    cout << "尾插法建立单链表,输入-1结束:" << endl;
    List_TailInsert(L2);        //尾插法建立单链表
    PrintList(L2);
    
    //按序号查找结点
    LinkList L3;
    cout << "按序号查找结点,序号为 3 的结点值为:" << endl;
    List_HeadInsert(L3);        //头插法建表
    LNode *p1 = GetElem(L3 , 3); //序号查结点
    if(p1 == NULL)
        cout << "查无此值" << endl << endl;
    else
        cout << p1->data << endl << endl;
    
    //按值查找表结点
    LinkList L4;
    cout << "按值查找表结点,值为 35 的结点为:" << endl;
    List_TailInsert(L4);            //尾插法建表
    LNode *p2 = LocateElem(L4 , 35); //按值查结点
    if(p2 == NULL)
        cout << "查无此值" << endl << endl;
    else
        cout << p2->data << endl << endl;
    
    //在给定结点位置后插入一个结点
    LinkList L5;
    cout << "在序号为5的结点后插入值 555 :" << endl;
    List_HeadInsert(L5);            //头插法建表
    InsertLNode(L5, 555, 5);        //插入一个结点
    PrintList(L5);
    
    //在给定结点位置前插入一个结点
    LinkList L6;
    cout << "在序号为6的结点前插入 666 :" << endl;
    List_TailInsert(L6);
    FrontInsertNode(L6, 666, 6);
    PrintList(L6);
    
    //删除给定结点位置的结点
    LinkList L7;
    cout << "删除给定结点位置的结点: " <<endl;
    List_HeadInsert(L7);
    DelectLNode(L7, 7);
    PrintList(L7);
    
    return 0;
}

整体运行截图:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值